{"id":34979232,"url":"https://github.com/huangwb8/vibenotification","last_synced_at":"2026-04-06T17:03:07.276Z","repository":{"id":329973128,"uuid":"1119977773","full_name":"huangwb8/VibeNotification","owner":"huangwb8","description":"Notification system for vibe coding CLIs (Claude Code \u0026 OpenAI Codex)","archived":false,"fork":false,"pushed_at":"2026-04-06T09:37:52.000Z","size":137,"stargazers_count":2,"open_issues_count":0,"forks_count":2,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-06T09:50:43.190Z","etag":null,"topics":["ai","ai-agents","claude-code","codex","python"],"latest_commit_sha":null,"homepage":"https://pypi.org/project/vibe-notification","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/huangwb8.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2025-12-20T08:17:17.000Z","updated_at":"2026-04-06T09:37:43.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/huangwb8/VibeNotification","commit_stats":null,"previous_names":["huangwb8/vibenotification"],"tags_count":9,"template":false,"template_full_name":null,"purl":"pkg:github/huangwb8/VibeNotification","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/huangwb8%2FVibeNotification","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/huangwb8%2FVibeNotification/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/huangwb8%2FVibeNotification/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/huangwb8%2FVibeNotification/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/huangwb8","download_url":"https://codeload.github.com/huangwb8/VibeNotification/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/huangwb8%2FVibeNotification/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31481238,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-06T14:34:32.243Z","status":"ssl_error","status_checked_at":"2026-04-06T14:34:31.723Z","response_time":112,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ai","ai-agents","claude-code","codex","python"],"created_at":"2025-12-27T00:51:46.826Z","updated_at":"2026-04-06T17:03:07.271Z","avatar_url":"https://github.com/huangwb8.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n\n# VibeNotification\n\n[![PyPI](https://img.shields.io/pypi/v/vibe-notification.svg)](https://pypi.org/project/vibe-notification/)\n[![Python](https://img.shields.io/pypi/pyversions/vibe-notification.svg)](https://pypi.org/project/vibe-notification/)\n[![Platform](https://img.shields.io/badge/platform-macOS%20%7C%20Linux%20%7C%20Windows-lightgrey.svg)](#installation)\n[![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)\n\nEnglish | [中文](README.zh.md)\n\n\u003cstrong\u003e Stop waiting when vibe coding — Give a notification when Claude Code or Codex finishes replies — \u003c/strong\u003e\n\n[Blog walkthrough (Chinese): AI应用系列 一个简单的Vibe coding的通知系统](https://blognas.hwb0307.com/ai/6659)\n\n\u003c/div\u003e\n\n![image-20251221214216954](https://chevereto.hwb0307.com/images/2025/12/21/image-20251221214216954.png)\n\n## Installation\n\n- Stable (PyPI): `pip install vibe-notification`\n- Dev: `pip install -e .`\n- Optional venv: `python -m venv venv \u0026\u0026 source venv/bin/activate`\n- Verify: `python -m vibe_notification --test` (should toast and chime when enabled)\n- Interactive setup: `python -m vibe_notification --config`\n  - Default config file: `~/.config/vibe-notification/config.json`\n  - Make sure both sound and system notifications are enabled\n\n## Quick Start\n\n### Claude Code\n\n- Hooks you can use: `Stop` (on every reply), `SessionEnd` (when the session ends), `SubagentStop` (Task tool completes)\n- Edit `~/.claude/settings.json` and add a Stop hook:\n\n```json\n{\n  \"hooks\": {\n    \"Stop\": [\n      {\n        \"hooks\": [\n          {\n            \"type\": \"command\",\n            \"command\": \"python -m vibe_notification\"\n          }\n        ]\n      }\n    ]\n  }\n}\n```\n\n- Example full settings snippet with environment variables:\n\n```json\n{\n  \"$schema\": \"https://json.schemastore.org/claude-code-settings.json\",\n  \"env\": {\n    \"ANTHROPIC_AUTH_TOKEN\": \"xxx\",\n    \"ANTHROPIC_BASE_URL\": \"https://open.bigmodel.cn/api/anthropic\",\n    \"ANTHROPIC_DEFAULT_HAIKU_MODEL\": \"glm-4.6\",\n    \"ANTHROPIC_DEFAULT_OPUS_MODEL\": \"glm-4.6\",\n    \"ANTHROPIC_DEFAULT_SONNET_MODEL\": \"glm-4.6\",\n    \"ANTHROPIC_MODEL\": \"glm-4.6\",\n    \"CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC\": \"1\",\n    \"DISABLE_ERROR_REPORTING\": \"1\",\n    \"DISABLE_TELEMETRY\": \"1\",\n    \"MCP_TIMEOUT\": \"60000\"\n  },\n  \"hooks\": {\n    \"Stop\": [\n      {\n        \"hooks\": [\n          {\n            \"command\": \"python -m vibe_notification\",\n            \"type\": \"command\"\n          }\n        ]\n      }\n    ]\n  },\n  \"includeCoAuthoredBy\": false,\n  \"outputStyle\": \"engineer-professional\"\n}\n```\n\n- Session end only:\n\n```json\n{\n  \"hooks\": {\n    \"SessionEnd\": [\n      {\n        \"hooks\": [\n          {\n            \"type\": \"command\",\n            \"command\": \"python -m vibe_notification\"\n          }\n        ]\n      }\n    ]\n  }\n}\n```\n\n- Combine multiple hooks (Stop + SessionEnd):\n\n```json\n{\n  \"hooks\": {\n    \"Stop\": [\n      {\n        \"hooks\": [\n          {\n            \"type\": \"command\",\n            \"command\": \"python -m vibe_notification\"\n          }\n        ]\n      }\n    ],\n    \"SessionEnd\": [\n      {\n        \"hooks\": [\n          {\n            \"type\": \"command\",\n            \"command\": \"python -m vibe_notification\"\n          }\n        ]\n      }\n    ]\n  }\n}\n```\n\n### Codex CLI\n\nAdd a notifier command to `~/.codex/config.toml` so Codex triggers VibeNotification when the agent actually finishes a turn (`agent-turn-complete`):\n\n```toml\nnotify = [\"python3\", \"-m\", \"vibe_notification\"]\n```\n\nNote: `notify` is turn-based, not session-exit-based.\n\nIf you only want one notification after the whole Codex session exits, do not configure `notify`. Instead, start Codex through the wrapper example at [examples/codex_session_end_wrapper.py](/Volumes/2T01/winE/Starup/VibeNotification/examples/codex_session_end_wrapper.py):\n\n```bash\npython examples/codex_session_end_wrapper.py\n```\n\nYou can pass normal Codex arguments through unchanged:\n\n```bash\npython examples/codex_session_end_wrapper.py --help\npython examples/codex_session_end_wrapper.py -C /path/to/project\n```\n\nIf you want this as your everyday entrypoint, add a shell alias such as:\n\n```bash\nalias codexn='python /Volumes/2T01/winE/Starup/VibeNotification/examples/codex_session_end_wrapper.py'\n```\n\nThen launch `codexn` instead of `codex`; VibeNotification will fire only once, after the Codex process actually exits.\n\nTypical placement in `config.toml`:\n\n```toml\nmodel_provider = \"xxx\"\nmodel = \"gpt-5.1-codex-max\"\nmodel_reasoning_effort = \"medium\"\ndisable_response_storage = true\nnotify = [\"python3\", \"-m\", \"vibe_notification\"]\n\n[model_providers.xxx]\nname = \"xxx\"\nbase_url = \"https://xxx/v1\"\nwire_api = \"responses\"\nrequires_openai_auth = true\n\n[tui]\nnotifications = true\n```\n\n## Configuration Recipes\n\n### Visual only (no sound)\n\n- Codex `~/.codex/config.toml`:\n\n```toml\nnotify = [\"python3\", \"-m\", \"vibe_notification\", \"--sound\", \"0\"]\n```\n\n- Claude Code `~/.claude/settings.json`:\n\n```json\n{\n  \"hooks\": {\n    \"Stop\": [\n      {\n        \"hooks\": [\n          {\n            \"type\": \"command\",\n            \"command\": \"python -m vibe_notification --sound 0\"\n          }\n        ]\n      }\n    ]\n  }\n}\n```\n\n- Quick test:\n\n```bash\npython -m vibe_notification --sound 0 --test\n```\n\n### Sound only (no system toast)\n\n- Codex:\n\n```toml\nnotify = [\"python3\", \"-m\", \"vibe_notification\", \"--notification\", \"0\"]\n```\n\n- Claude Code:\n\n```json\n{\n  \"hooks\": {\n    \"Stop\": [\n      {\n        \"hooks\": [\n          {\n            \"type\": \"command\",\n            \"command\": \"python -m vibe_notification --notification 0\"\n          }\n        ]\n      }\n    ]\n  }\n}\n```\n\n- Quick test:\n\n```bash\npython -m vibe_notification --notification 0 --test\n```\n\n### Temporary toggles (environment variables)\n\n- `VIBE_NOTIFICATION_SOUND=0` — mute sound\n- `VIBE_NOTIFICATION_NOTIFY=0` — disable system notification\n- `VIBE_NOTIFICATION_LOG_LEVEL=DEBUG` — enable debug logging; raw Codex payloads are also appended to `~/.config/vibe-notification/debug/codex-events.jsonl`\n\nCodex examples:\n\n```toml\n# Temporarily mute sound\nnotify = [\"env\", \"VIBE_NOTIFICATION_SOUND=0\", \"python3\", \"-m\", \"vibe_notification\"]\n\n# Disable all notifications (for debugging)\nnotify = [\"env\", \"VIBE_NOTIFICATION_NOTIFY=0\", \"VIBE_NOTIFICATION_SOUND=0\", \"python3\", \"-m\", \"vibe_notification\"]\n\n# Enable debug logging\nnotify = [\"env\", \"VIBE_NOTIFICATION_LOG_LEVEL=DEBUG\", \"python3\", \"-m\", \"vibe_notification\"]\n```\n\nClaude Code example:\n\n```json\n{\n  \"hooks\": {\n    \"Stop\": [\n      {\n        \"hooks\": [\n          {\n            \"type\": \"command\",\n            \"command\": \"env VIBE_NOTIFICATION_SOUND=0 python -m vibe_notification\"\n          }\n        ]\n      }\n    ]\n  }\n}\n```\n\nCLI tests:\n\n```bash\nVIBE_NOTIFICATION_SOUND=0 python -m vibe_notification --test\nVIBE_NOTIFICATION_SOUND=0 VIBE_NOTIFICATION_NOTIFY=0 python -m vibe_notification --test\nVIBE_NOTIFICATION_LOG_LEVEL=DEBUG python -m vibe_notification --test\n```\n\n### Sound type\n\nAvailable macOS sound types: `Glass` (default), `Ping`, `Pop`, `Tink`, `Basso`.\n\n```toml\nnotify = [\"env\", \"VIBE_NOTIFICATION_SOUND_TYPE=Ping\", \"python3\", \"-m\", \"vibe_notification\"]\n# Low tone\nnotify = [\"env\", \"VIBE_NOTIFICATION_SOUND_TYPE=Basso\", \"python3\", \"-m\", \"vibe_notification\"]\n```\n\nClaude Code:\n\n```json\n{\n  \"hooks\": {\n    \"Stop\": [\n      {\n        \"hooks\": [\n          {\n            \"type\": \"command\",\n            \"command\": \"env VIBE_NOTIFICATION_SOUND_TYPE=Pop python -m vibe_notification\"\n          }\n        ]\n      }\n    ]\n  }\n}\n```\n\nTest different sounds:\n\n```bash\nVIBE_NOTIFICATION_SOUND_TYPE=Tink python -m vibe_notification --test\nVIBE_NOTIFICATION_SOUND_TYPE=Ping python -m vibe_notification --test\n```\n\n### Volume control\n\nVolume range is `0.0–1.0`.\n\n```toml\nnotify = [\"env\", \"VIBE_NOTIFICATION_SOUND_VOLUME=0.2\", \"python3\", \"-m\", \"vibe_notification\"]\nnotify = [\"env\", \"VIBE_NOTIFICATION_SOUND_VOLUME=0.5\", \"python3\", \"-m\", \"vibe_notification\"]\nnotify = [\"env\", \"VIBE_NOTIFICATION_SOUND_VOLUME=0\", \"python3\", \"-m\", \"vibe_notification\"] # mute\n```\n\nClaude Code:\n\n```json\n{\n  \"hooks\": {\n    \"Stop\": [\n      {\n        \"hooks\": [\n          {\n            \"type\": \"command\",\n            \"command\": \"env VIBE_NOTIFICATION_SOUND_VOLUME=0.3 python -m vibe_notification\"\n          }\n        ]\n      }\n    ]\n  }\n}\n```\n\nQuick test:\n\n```bash\nVIBE_NOTIFICATION_SOUND_VOLUME=0.1 python -m vibe_notification --test\nVIBE_NOTIFICATION_SOUND_VOLUME=0.8 python -m vibe_notification --test\n```\n\n### Notification timeout\n\nEdit `~/.config/vibe-notification/config.json`:\n\n```json\n{\n  \"enable_sound\": true,\n  \"enable_notification\": true,\n  \"notification_timeout\": 5000,\n  \"sound_type\": \"Glass\",\n  \"sound_volume\": 0.1,\n  \"log_level\": \"INFO\"\n}\n```\n\n- `5000` = 5s auto-dismiss\n- `10000` = 10s (default)\n- `30000` = 30s\n- `0` = sticky, manual close\n\nOr use the interactive config:\n\n```bash\npython -m vibe_notification --config\n```\n\n### Prebuilt combos\n\nFocus mode (low volume + toast only + short display):\n\n```toml\nnotify = [\"env\", \"VIBE_NOTIFICATION_SOUND_VOLUME=0.1\", \"VIBE_NOTIFICATION_SOUND_TYPE=Basso\", \"python3\", \"-m\", \"vibe_notification\"]\n```\n\nMeeting mode (sound only, louder, specific tone):\n\n```toml\nnotify = [\"env\", \"VIBE_NOTIFICATION_NOTIFY=0\", \"VIBE_NOTIFICATION_SOUND_VOLUME=0.7\", \"VIBE_NOTIFICATION_SOUND_TYPE=Ping\", \"python3\", \"-m\", \"vibe_notification\"]\n```\n\nDebug mode (all on + debug logs):\n\n```toml\nnotify = [\"env\", \"VIBE_NOTIFICATION_LOG_LEVEL=DEBUG\", \"python3\", \"-m\", \"vibe_notification\"]\n```\n\n## CLI Reference\n\n### Command-line options\n\n| Option | Type | Default | Description |\n|--------|------|---------|-------------|\n| `event_json` | positional | - | Optional Codex event JSON string |\n| `--test` | flag | - | Send a test notification |\n| `--config` | flag | - | Interactive configuration |\n| `--sound {0,1}` | choice | config value | Enable/disable sound (0=off, 1=on) |\n| `--notification {0,1}` | choice | config value | Enable/disable system notification (0=off, 1=on) |\n| `--log-level {DEBUG,INFO,WARNING,ERROR}` | choice | config value | Set log level |\n| `--version` | flag | - | Show version |\n\n### Config file\n\nLocation: `~/.config/vibe-notification/config.json`\n\n| Key | Type | Default | Description |\n|-----|------|---------|-------------|\n| `enable_sound` | bool | `true` | Enable sound |\n| `enable_notification` | bool | `true` | Enable system notification |\n| `notification_timeout` | int | `10000` | Duration in ms |\n| `sound_type` | string | `\"default\"` | Sound type |\n| `log_level` | string | `\"INFO\"` | Log level |\n| `detect_conversation_end` | bool | `true` | Detect end of conversation |\n\n### Environment variables\n\n| Env | Description | Example |\n|-----|-------------|---------|\n| `VIBE_NOTIFICATION_SOUND` | Override sound setting | `VIBE_NOTIFICATION_SOUND=0` |\n| `VIBE_NOTIFICATION_NOTIFY` | Override notification setting | `VIBE_NOTIFICATION_NOTIFY=0` |\n| `VIBE_NOTIFICATION_LOG_LEVEL` | Override log level | `VIBE_NOTIFICATION_LOG_LEVEL=DEBUG` |\n\n### Typical commands\n\n```bash\n# Test (toast + sound)\npython -m vibe_notification --test\n\n# Toast only\npython -m vibe_notification --sound 0 --test\n\n# Sound only\npython -m vibe_notification --notification 0 --test\n\n# Debug logs\npython -m vibe_notification --log-level DEBUG --test\n```\n\n### Hook usage examples\n\nClaude Code:\n\n```bash\necho '{\"toolName\": \"Bash\"}' | python -m vibe_notification\nVIBE_NOTIFICATION_SOUND=0 echo '{\"toolName\": \"Task\"}' | python -m vibe_notification\nVIBE_NOTIFICATION_NOTIFY=0 python -m vibe_notification\n```\n\nCodex:\n\n```bash\npython -m vibe_notification '{\"type\":\"agent-turn-complete\",\"thread-id\":\"thread-1\",\"turn-id\":\"turn-1\",\"cwd\":\"/tmp/project\",\"input-messages\":[\"fix tests\"],\"last-assistant-message\":\"Done\"}'\npython -m vibe_notification '{\"type\":\"agent-turn-complete\",\"thread-id\":\"thread-1\",\"turn-id\":\"turn-1\",\"cwd\":\"/tmp/project\",\"input-messages\":[\"fix tests\"],\"last-assistant-message\":\"Done\"}' --notification 1 --sound 0\nVIBE_NOTIFICATION_SOUND=1 VIBE_NOTIFICATION_NOTIFY=1 python -m vibe_notification '{\"type\":\"agent-turn-complete\",\"thread-id\":\"thread-1\",\"turn-id\":\"turn-1\",\"cwd\":\"/tmp/project\",\"input-messages\":[\"fix tests\"],\"last-assistant-message\":\"Done\"}'\n```\n\n## Publishing to PyPI\n\n1. Bump the version in `pyproject.toml` (single source of truth).\n2. Install tooling: `python -m pip install --upgrade build twine`.\n3. Build: `python -m build` (creates `.tar.gz` and `.whl` under `dist/`).\n4. Validate: `python -m twine check dist/*`.\n5. Upload: `TWINE_USERNAME=__token__ TWINE_PASSWORD=\u003cpypi-token\u003e python -m twine upload dist/*` (use `--repository testpypi` to dry run).\n6. Install + verify: `pip install -U vibe-notification` then `python -m vibe_notification --test`.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhuangwb8%2Fvibenotification","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhuangwb8%2Fvibenotification","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhuangwb8%2Fvibenotification/lists"}