Skip to content

huangwb8/VibeNotification

Repository files navigation

VibeNotification

PyPI Python Platform License

English | 中文

Stop waiting when vibe coding — Give a notification when Claude Code or Codex finishes replies —

Blog walkthrough (Chinese): AI应用系列 一个简单的Vibe coding的通知系统

image-20251221214216954

Installation

  • 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

Quick Start

Claude Code

  • Hooks you can use: Stop (on every reply), SessionEnd (when the session ends), SubagentStop (Task tool completes)
  • Edit ~/.claude/settings.json and 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"
          }
        ]
      }
    ]
  }
}

Codex CLI

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

Configuration Recipes

Visual only (no sound)

  • 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

Sound only (no system toast)

  • 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 --test

Temporary toggles (environment variables)

  • VIBE_NOTIFICATION_SOUND=0 — mute sound
  • VIBE_NOTIFICATION_NOTIFY=0 — disable system notification
  • VIBE_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 --test

Sound type

Available 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 --test

Volume control

Volume 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"] # mute

Claude 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 --test

Notification timeout

Edit ~/.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-dismiss
  • 10000 = 10s (default)
  • 30000 = 30s
  • 0 = sticky, manual close

Or use the interactive config:

python -m vibe_notification --config

Prebuilt combos

Focus 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"]

CLI Reference

Command-line options

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

Config file

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

Environment variables

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

Typical commands

# 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 --test

Hook usage examples

Claude 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_notification

Codex:

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"}'

Publishing to PyPI

  1. Bump the version in pyproject.toml (single source of truth).
  2. Install tooling: python -m pip install --upgrade build twine.
  3. Build: python -m build (creates .tar.gz and .whl under dist/).
  4. Validate: python -m twine check dist/*.
  5. Upload: TWINE_USERNAME=__token__ TWINE_PASSWORD=<pypi-token> python -m twine upload dist/* (use --repository testpypi to dry run).
  6. Install + verify: pip install -U vibe-notification then python -m vibe_notification --test.

About

Notification system for vibe coding CLIs (Claude Code & OpenAI Codex)

Topics

Resources

License

Contributing

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages