English | 中文
Stop waiting when vibe coding — Give a notification when Claude Code or Codex finishes replies —
- Stable (PyPI):
pip install vibe-notification - Dev:
pip install -e . - Optional venv:
python -m venv venv && source venv/bin/activate - Verify:
python -m vibe_notification --test(should toast and chime when enabled) - Interactive setup:
python -m vibe_notification --config- Default config file:
~/.config/vibe-notification/config.json - Make sure both sound and system notifications are enabled
- Default config file:
- Hooks you can use:
Stop(on every reply),SessionEnd(when the session ends),SubagentStop(Task tool completes) - Edit
~/.claude/settings.jsonand add a Stop hook:
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "python -m vibe_notification"
}
]
}
]
}
}- Example full settings snippet with environment variables:
{
"$schema": "https://json.schemastore.org/claude-code-settings.json",
"env": {
"ANTHROPIC_AUTH_TOKEN": "xxx",
"ANTHROPIC_BASE_URL": "https://open.bigmodel.cn/api/anthropic",
"ANTHROPIC_DEFAULT_HAIKU_MODEL": "glm-4.6",
"ANTHROPIC_DEFAULT_OPUS_MODEL": "glm-4.6",
"ANTHROPIC_DEFAULT_SONNET_MODEL": "glm-4.6",
"ANTHROPIC_MODEL": "glm-4.6",
"CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC": "1",
"DISABLE_ERROR_REPORTING": "1",
"DISABLE_TELEMETRY": "1",
"MCP_TIMEOUT": "60000"
},
"hooks": {
"Stop": [
{
"hooks": [
{
"command": "python -m vibe_notification",
"type": "command"
}
]
}
]
},
"includeCoAuthoredBy": false,
"outputStyle": "engineer-professional"
}- Session end only:
{
"hooks": {
"SessionEnd": [
{
"hooks": [
{
"type": "command",
"command": "python -m vibe_notification"
}
]
}
]
}
}- Combine multiple hooks (Stop + SessionEnd):
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "python -m vibe_notification"
}
]
}
],
"SessionEnd": [
{
"hooks": [
{
"type": "command",
"command": "python -m vibe_notification"
}
]
}
]
}
}Add a notifier command to ~/.codex/config.toml so Codex triggers VibeNotification on every agent-turn-complete:
notify = ["python3", "-m", "vibe_notification"]Typical placement in config.toml:
model_provider = "xxx"
model = "gpt-5.1-codex-max"
model_reasoning_effort = "medium"
disable_response_storage = true
notify = ["python3", "-m", "vibe_notification"]
[model_providers.xxx]
name = "xxx"
base_url = "https://xxx/v1"
wire_api = "responses"
requires_openai_auth = true
[tui]
notifications = true- Codex
~/.codex/config.toml:
notify = ["python3", "-m", "vibe_notification", "--sound", "0"]- Claude Code
~/.claude/settings.json:
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "python -m vibe_notification --sound 0"
}
]
}
]
}
}- Quick test:
python -m vibe_notification --sound 0 --test- Codex:
notify = ["python3", "-m", "vibe_notification", "--notification", "0"]- Claude Code:
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "python -m vibe_notification --notification 0"
}
]
}
]
}
}- Quick test:
python -m vibe_notification --notification 0 --testVIBE_NOTIFICATION_SOUND=0— mute soundVIBE_NOTIFICATION_NOTIFY=0— disable system notificationVIBE_NOTIFICATION_LOG_LEVEL=DEBUG— enable debug logging
Codex examples:
# Temporarily mute sound
notify = ["env", "VIBE_NOTIFICATION_SOUND=0", "python3", "-m", "vibe_notification"]
# Disable all notifications (for debugging)
notify = ["env", "VIBE_NOTIFICATION_NOTIFY=0", "VIBE_NOTIFICATION_SOUND=0", "python3", "-m", "vibe_notification"]
# Enable debug logging
notify = ["env", "VIBE_NOTIFICATION_LOG_LEVEL=DEBUG", "python3", "-m", "vibe_notification"]Claude Code example:
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "env VIBE_NOTIFICATION_SOUND=0 python -m vibe_notification"
}
]
}
]
}
}CLI tests:
VIBE_NOTIFICATION_SOUND=0 python -m vibe_notification --test
VIBE_NOTIFICATION_SOUND=0 VIBE_NOTIFICATION_NOTIFY=0 python -m vibe_notification --test
VIBE_NOTIFICATION_LOG_LEVEL=DEBUG python -m vibe_notification --testAvailable macOS sound types: Glass (default), Ping, Pop, Tink, Basso.
notify = ["env", "VIBE_NOTIFICATION_SOUND_TYPE=Ping", "python3", "-m", "vibe_notification"]
# Low tone
notify = ["env", "VIBE_NOTIFICATION_SOUND_TYPE=Basso", "python3", "-m", "vibe_notification"]Claude Code:
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "env VIBE_NOTIFICATION_SOUND_TYPE=Pop python -m vibe_notification"
}
]
}
]
}
}Test different sounds:
VIBE_NOTIFICATION_SOUND_TYPE=Tink python -m vibe_notification --test
VIBE_NOTIFICATION_SOUND_TYPE=Ping python -m vibe_notification --testVolume range is 0.0–1.0.
notify = ["env", "VIBE_NOTIFICATION_SOUND_VOLUME=0.2", "python3", "-m", "vibe_notification"]
notify = ["env", "VIBE_NOTIFICATION_SOUND_VOLUME=0.5", "python3", "-m", "vibe_notification"]
notify = ["env", "VIBE_NOTIFICATION_SOUND_VOLUME=0", "python3", "-m", "vibe_notification"] # muteClaude Code:
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "env VIBE_NOTIFICATION_SOUND_VOLUME=0.3 python -m vibe_notification"
}
]
}
]
}
}Quick test:
VIBE_NOTIFICATION_SOUND_VOLUME=0.1 python -m vibe_notification --test
VIBE_NOTIFICATION_SOUND_VOLUME=0.8 python -m vibe_notification --testEdit ~/.config/vibe-notification/config.json:
{
"enable_sound": true,
"enable_notification": true,
"notification_timeout": 5000,
"sound_type": "Glass",
"sound_volume": 0.1,
"log_level": "INFO"
}5000= 5s auto-dismiss10000= 10s (default)30000= 30s0= sticky, manual close
Or use the interactive config:
python -m vibe_notification --configFocus mode (low volume + toast only + short display):
notify = ["env", "VIBE_NOTIFICATION_SOUND_VOLUME=0.1", "VIBE_NOTIFICATION_SOUND_TYPE=Basso", "python3", "-m", "vibe_notification"]Meeting mode (sound only, louder, specific tone):
notify = ["env", "VIBE_NOTIFICATION_NOTIFY=0", "VIBE_NOTIFICATION_SOUND_VOLUME=0.7", "VIBE_NOTIFICATION_SOUND_TYPE=Ping", "python3", "-m", "vibe_notification"]Debug mode (all on + debug logs):
notify = ["env", "VIBE_NOTIFICATION_LOG_LEVEL=DEBUG", "python3", "-m", "vibe_notification"]| Option | Type | Default | Description |
|---|---|---|---|
event_json |
positional | - | Optional Codex event JSON string |
--test |
flag | - | Send a test notification |
--config |
flag | - | Interactive configuration |
--sound {0,1} |
choice | config value | Enable/disable sound (0=off, 1=on) |
--notification {0,1} |
choice | config value | Enable/disable system notification (0=off, 1=on) |
--log-level {DEBUG,INFO,WARNING,ERROR} |
choice | config value | Set log level |
--version |
flag | - | Show version |
Location: ~/.config/vibe-notification/config.json
| Key | Type | Default | Description |
|---|---|---|---|
enable_sound |
bool | true |
Enable sound |
enable_notification |
bool | true |
Enable system notification |
notification_timeout |
int | 10000 |
Duration in ms |
sound_type |
string | "default" |
Sound type |
log_level |
string | "INFO" |
Log level |
detect_conversation_end |
bool | true |
Detect end of conversation |
| Env | Description | Example |
|---|---|---|
VIBE_NOTIFICATION_SOUND |
Override sound setting | VIBE_NOTIFICATION_SOUND=0 |
VIBE_NOTIFICATION_NOTIFY |
Override notification setting | VIBE_NOTIFICATION_NOTIFY=0 |
VIBE_NOTIFICATION_LOG_LEVEL |
Override log level | VIBE_NOTIFICATION_LOG_LEVEL=DEBUG |
# Test (toast + sound)
python -m vibe_notification --test
# Toast only
python -m vibe_notification --sound 0 --test
# Sound only
python -m vibe_notification --notification 0 --test
# Debug logs
python -m vibe_notification --log-level DEBUG --testClaude Code:
echo '{"toolName": "Bash"}' | python -m vibe_notification
VIBE_NOTIFICATION_SOUND=0 echo '{"toolName": "Task"}' | python -m vibe_notification
VIBE_NOTIFICATION_NOTIFY=0 python -m vibe_notificationCodex:
python -m vibe_notification '{"type":"agent-turn-complete","agent":"codex","message":"tool Bash done"}'
python -m vibe_notification '{"type":"agent-turn-complete","agent":"codex"}' --notification 1 --sound 0
VIBE_NOTIFICATION_SOUND=1 VIBE_NOTIFICATION_NOTIFY=1 python -m vibe_notification '{"type":"agent-turn-complete"}'- Bump the version in
pyproject.toml(single source of truth). - Install tooling:
python -m pip install --upgrade build twine. - Build:
python -m build(creates.tar.gzand.whlunderdist/). - Validate:
python -m twine check dist/*. - Upload:
TWINE_USERNAME=__token__ TWINE_PASSWORD=<pypi-token> python -m twine upload dist/*(use--repository testpypito dry run). - Install + verify:
pip install -U vibe-notificationthenpython -m vibe_notification --test.
