Sound notifications for AI coding agents: Claude Code, Codex, and OpenCode. Plays a sound when an agent finishes its turn - very useful when you do other things while waiting for your agent to finish his work. Cross-platform (Windows / macOS / Linux), zero dependencies beyond Python 3.6+.
The installer detects which agents are installed on your machine and wires the sound into each one:
| Agent | User dir | Sound | Config change |
|---|---|---|---|
| Codex | ~/.codex ($CODEX_HOME) |
knock-knock-knock-knock.wav |
notify line in config.toml |
| Claude Code | ~/.claude ($CLAUDE_CONFIG_DIR) |
ding-ding.wav |
Stop / PreToolUse / PermissionRequest hooks in settings.json |
| OpenCode | ~/.config/opencode/plugins ($OPENCODE_CONFIG_DIR / $XDG_CONFIG_HOME) |
tap-tap-tap.wav |
none (plugins auto-load) |
One-liner - downloads and runs install.py straight from the repo, no clone needed:
python -c "import urllib.request;exec(urllib.request.urlopen('https://raw.githubusercontent.com/zeljkoavramovic/tap-tap-notifier/main/install.py').read())"Clone the repo and run the installer locally. Same effect as the one-liner, but the asset files are copied from your checkout instead of downloaded:
git clone https://github.com/zeljkoavramovic/tap-tap-notifier
cd tap-tap-notifier
python install.pyAfter install, restart Codex and/or OpenCode. Claude Code picks up hooks on the next turn (hot-reload).
macOS / Linux: use
python3instead ofpythonif that's how your interpreter is named.
- Python 3.6+ (stdlib only - no
pippackages). The installer checks this up front and exits cleanly on older versions. - At least one of: Codex, Claude Code, OpenCode.
- An audio path: built-in on Windows (
winsound) and macOS (afplay); on Linux, one ofpw-play/paplay/aplay(standard on desktop distros).
Each agent invokes play_sound.py <wav> on the relevant event. The script plays the wav via the host OS's native audio path. play_sound.py requires the wav argument; a bare filename (no path) is resolved next to the script, so each agent's config references just the filename.
- Codex appends a JSON payload as the final arg - the script ignores it.
- Claude Code delivers its payload on stdin - the script ignores it.
- OpenCode uses a small
play-sound.jsplugin (auto-loaded from the plugins dir) that shells out toplay_sound.py.
- Codex auto-manages the
notifyline: on restart it re-wraps your entry into its computer-useturn-endedchain via--previous-notify. Expected and harmless - the sound still fires. - Claude Code Desktop known issue on Windows: anthropics/claude-code#26770 -
Stophooks registered insettings.jsonmay not fire; plugin-based Stop hooks work. If you hit this, the workaround is a plugin-based hook. - Re-running the installer is safe: it refreshes its own files and config entries in place and leaves your other hooks and settings untouched.
If no sound plays, isolate the cause before anything else - run the script directly:
python play_sound.py tap-tap-tap.wavThen report what happened:
- Sound heard - audio works; the issue is the agent's hook/config not firing the script. Check the relevant config entry in the Install table for your agent.
- No sound - the issue is audio playback on your machine, not the agent wiring. Open an issue and include your OS, any error output, and the audio path the script reported.
macOS / Linux: use
python3instead ofpythonif that's how your interpreter is named.
- Codex: remove the
notifyline from~/.codex/config.toml; deleteplay_sound.py+knock-knock-knock-knock.wavfrom~/.codex/. - Claude Code: remove the
play_sound.pyhook entries from~/.claude/settings.json(underStop,PreToolUse,PermissionRequest); deleteplay_sound.py+ding-ding.wavfrom~/.claude/. - OpenCode: delete
play-sound.js,play_sound.py,tap-tap-tap.wavfrom~/.config/opencode/plugins/.
Restart the relevant agent afterward.
The one-liner executes code downloaded over HTTPS from this repository. Review install.py before running.
If like this, support is welcome:
MIT - use freely, improve openly, credit kindly.