Skip to content

ChanMeng666/echook

Project Banner

echook

AI-operated audio notification system for Claude Code, Cursor IDE, and Codex CLI.
You type one slash command at install time. Then natural language forever.
26 hook events, 2 audio themes, rate-limit alerts, webhooks, TTS, context monitor β€” all operated by your AI agent on your behalf.
v6.0.0 β€” refocus + AI-agent-first. echook is now two tracks only β€” audio/notification and the status line β€” with every operation driven by a non-interactive CLI. No human menus, no curl | bash, nothing to hand-edit. See the CHANGELOG.

5.2.x β€” echook rebrand + Codex support. Renamed claude-code-audio-hooks β†’ echook (Echo + Hook) in 5.2.1 β€” a door-only rename with zero migration (CLI, marketplace name, and state dirs all unchanged; existing installs keep working). 5.2.0 added Codex CLI as a third editor target. Full history in the CHANGELOG.

License: MIT Latest Release Release Date CI Platform Editors Install

Share This Project


Promotional Video

promo-video.mp4

Built with Remotion, Claude Code, ElevenLabs & Suno. Source: echook-promo-video

πŸ€– This is an AI-Agent-first project β€” you don't read this, your agent does

Humans: don't install or configure echook by hand, and don't spend time studying this README. Point your AI agent β€” Claude Code, Cursor, or Codex β€” at this repo and say:

"Install echook from github.com/ChanMeng666/echook and set it up for me."

Your agent reads the docs, runs every command, verifies the result, and reports back. The agent-facing source of truth is AGENTS.md + llms.txt + the live audio-hooks manifest β€” your agent already knows where to look. The only thing a human ever types by hand is one /reload-plugins slash command (Claude Code has no CLI equivalent for it).

All a human needs to know is what echook does β€” skim Key Features below β€” so you can ask your agent for it in plain English: "mute audio for an hour", "switch to chimes", "watch my .env file", "put a context-usage bar in my status line". Not sure what's possible? Just ask your agent "what can I configure in echook?" and it will list every option for you.

Table of Contents

What's New

v6.0.0 β€” Refocus + AI-agent-first hardening

echook is now two tracks only β€” audio/notification and the status line β€” and every operation is a non-interactive audio-hooks CLI command. This release sharpened the product and removed everything a human used to have to do by hand.

What Detail
Two-track scope Removed the off-track Focus Flow (breathing/wellness) feature. echook does notifications (sound, spoken summary, desktop toast, webhook) and the status line β€” nothing else.
AI-agent-first, no human menus Removed all human-interactive scripts (configure.sh, test-audio.sh, snooze.sh, diagnose.py) and the curl | bash lite tier. The install/uninstall scripts are now always non-interactive β€” never prompt. Everything is an audio-hooks subcommand or a one-line agent prompt.
Safer notifications TTS reply-reading and verbose desktop toasts now route through a shared sanitizer that strips code/markdown and redacts secrets (API keys, tokens, JWTs) before anything is spoken or shown.
Leaner & more honest Removed dead config and a dead bash hook runtime; fixed the script installer to ship hook_runner.py's dependencies. Added llms.txt (AI-agent entrypoint).
Tested Full suite is now 174 tests green on the Ubuntu/Windows/macOS Γ— Python 3.9/3.12/3.13 matrix, including a new sanitizer test module.

The headline 5.2.x callout near the top covers the echook rebrand + Codex CLI support. The v5.0 redesign β€” the AI-first foundation that made all of this possible β€” lives below for reference.

v5.0 β€” AI-first redesign (click to expand)

v5.0 is an AI-first redesign. Every project surface is now machine-operable end-to-end so Claude Code can install, configure, snooze, troubleshoot, and upgrade the project on a human's behalf without any clicks, prompts, or doc reading.

Highlight Effect
audio-hooks JSON CLI Single binary with 27 subcommands. JSON to stdout, stable error codes + suggested commands.
/audio-hooks SKILL Natural-language activation: "snooze audio for an hour" β€” Claude runs it for you.
NDJSON structured logging Schema audio-hooks.v1 with stable error code enums.
Plugin-native install Two commands and you're done.
4 new hook events PermissionDenied, CwdChanged, FileChanged, TaskCreated β€” 26 total.
Native matcher routing hooks.json registers per-matcher handlers with unique audio per variant.
Rate-limit alerts One-shot warning at 80%/95% of your 5h or 7d quota.
TTS speak Claude's reply Instead of "Task completed", TTS speaks Claude's actual final message.
Status line with context monitor Color-coded Context and API Quota bars with /compact reminders.
ElevenLabs audio generator One-line manifest edit + one-command rebuild for new audio.

v5.0 in Action

Plugin Installed
Plugin Installed β€” 26 hooks registered
SKILL Registration
/audio-hooks SKILL active
Status Line
Context monitor status line
Marketplace
Marketplace registration

See CHANGELOG.md for full details.


Pick Your Platform

This is an AI-first project. You don't follow install steps yourself β€” you tell your AI agent (Claude Code, Cursor's agent, or Codex CLI) what to do, in plain English, and it runs every shell command, reads every JSON output, and reports back. Pick your platform:

flowchart TB
    REPO["github.com/ChanMeng666/echook<br/>(source + GitHub Releases)"]

    REPO --> CCP["Claude Code<br/>plugin marketplace"]
    REPO --> CURB["Cursor 3.2.16+<br/>auto-bridge (Path A)"]
    REPO --> CURN["Cursor native<br/>install --cursor (Path B)"]
    REPO --> CXP["Codex plugin<br/>marketplace"]
    REPO --> CXN["Codex native<br/>install --codex"]

    CCP --> CLI["audio-hooks CLI<br/>+ JSON + /audio-hooks SKILL<br/>(identical everywhere)"]
    CURB --> CLI
    CURN --> CLI
    CXP --> CLI
    CXN --> CLI

    CLI --> OUT["26 hook events Β· 2 themes Β· webhooks<br/>TTS Β· rate-limit alerts Β· status line"]

    style REPO fill:#4A90E2,color:#fff
    style CLI fill:#7ED321,color:#000
    style OUT fill:#F5A623,color:#000
Loading
Your editor / CLI Tell your AI agent Jump to
Claude Code "Install the audio-hooks plugin from github.com/ChanMeng666/echook." Install in 60 Seconds
Cursor IDE (with Claude Code installed too β€” most users) "Run audio-hooks status and confirm editor_targets.cursor.state is bridged-via-claude-code." (Cursor 3.2.16+ auto-bridges Claude Code plugins.) Cursor IDE β€” Path A
Cursor IDE (without Claude Code) "Clone github.com/ChanMeng666/echook into ~/audio-hooks, run python ~/audio-hooks/bin/audio-hooks install --cursor, then verify with audio-hooks status + audio-hooks test all." Cursor IDE β€” Path B
Codex "Install the audio-hooks Codex plugin from github.com/ChanMeng666/echook, then verify with audio-hooks status + audio-hooks test all." Native fallback: clone the repo and run python ~/audio-hooks/bin/audio-hooks install --codex. Codex β€” Plugin or Native Install

Once installed (any path), every natural-language prompt in Just Say It below works identically β€” same audio-hooks CLI, same JSON, same skill, regardless of which AI agent is driving.


Install in 60 Seconds

For 99% of operations, you just talk to Claude Code in plain English. The exception is the one-time install: /reload-plugins has no CLI equivalent, so the human types it exactly once. Everything else β€” configure, snooze, theme, webhook, troubleshoot, uninstall β€” is pure natural language.

Step 1 β€” Open Claude Code

claude

Step 2 β€” Install with one prompt

Paste this into Claude Code:

Please install the audio-hooks plugin from github.com/ChanMeng666/echook. Use the Bash tool to run claude plugin marketplace add ChanMeng666/echook then claude plugin install audio-hooks@chanmeng-audio-hooks. After both commands complete, tell me to type /reload-plugins to activate the plugin.

Step 3 β€” Type /reload-plugins (the one manual slash command)

/reload-plugins

Step 4 β€” Verify and configure with one prompt

Verify audio-hooks works by running audio-hooks diagnose and audio-hooks test all. Then switch to the chime audio theme.

Total: 1 shell command + 2 natural-language prompts + 1 slash command = 4 things. From here on: natural language forever.

Install flow diagram
sequenceDiagram
    participant Human
    participant Terminal
    participant Claude as Claude Code
    participant Bash as Bash tool
    participant Skill as /audio-hooks SKILL
    participant CLI as audio-hooks CLI

    Human->>Terminal: type `claude` to start a session
    Human->>Claude: "install audio-hooks plugin from<br/>github.com/ChanMeng666/echook via<br/>`claude plugin marketplace add` and `claude plugin install`"
    Claude->>Bash: claude plugin marketplace add ChanMeng666/echook
    Bash-->>Claude: "Successfully added marketplace"
    Claude->>Bash: claude plugin install audio-hooks@chanmeng-audio-hooks
    Bash-->>Claude: "Installed audio-hooks. Run /reload-plugins to apply."
    Claude-->>Human: "Plugin installed.<br/>Type /reload-plugins to activate it."

    rect rgb(255, 230, 200)
        Note over Human: The one manual slash command in the whole flow
        Human->>Claude: /reload-plugins
        Claude-->>Human: "Reloaded: 8 plugins, 37 hooks, 5 skills"
    end

    Human->>Claude: "verify audio-hooks works and switch to chime audio"
    Claude->>Skill: SKILL activates on intent
    Claude->>Bash: audio-hooks diagnose
    Bash-->>Claude: {"ok":true,"errors":[],"warnings":[]}
    Claude->>Bash: audio-hooks test all
    Bash-->>Claude: {"ok":true,"passed":6,"failed":0}
    Claude->>Bash: audio-hooks theme set custom
    Bash-->>Claude: {"ok":true,"theme":"custom"}
    Claude-->>Human: "Done. 26 hooks active, all tests passed,<br/>audio theme set to chimes."

    Note over Human,CLI: From here on: pure natural language forever.
Loading

Cursor IDE β€” Same Project, Two Install Paths

The project ships AI-first install paths for Cursor IDE 3.2.16+ users. Pick the one that matches your setup β€” both end at the same place: every audio-hooks subcommand below works identically on Cursor.

Path A β€” Cursor + Claude Code (most users)

You already get audio-hooks for free. Cursor 3.2.16+ auto-bridges any Claude Code plugin you have installed (per cursor.com/docs/reference/third-party-hooks). Just install via the Claude Code flow above, enable Cursor Settings β†’ "Third-party skills", then ask your agent:

Run audio-hooks status and confirm editor_targets.cursor.state is bridged-via-claude-code.

8 of 10 hook events bridge to Cursor. Notification and PermissionRequest are absent because Cursor has no equivalent β€” that is by Cursor's design, not a project bug. Stop, SubagentStop, PreToolUse, PostToolUse, SessionStart, SessionEnd, PreCompact, and UserPromptSubmit all fire identically across both editors.

To disable bridging entirely, turn off Cursor Settings β†’ "Third-party skills" (this is Cursor's global toggle, not per-plugin).

Path B β€” Cursor without Claude Code

Paste this single prompt into Cursor's agent chat:

Clone https://github.com/ChanMeng666/echook into ~/audio-hooks, then run python ~/audio-hooks/bin/audio-hooks install --cursor. After it succeeds, restart Cursor and run audio-hooks status followed by audio-hooks test all.

Cursor's agent runs git clone, runs the install (which writes ~/.cursor/hooks.json and seeds ~/.cursor/audio-hooks-data/user_preferences.json), then verifies. The install is non-interactive end-to-end β€” no prompts, no menus, no human in the loop.

audio-hooks install --cursor registers 11 Cursor-native event types β€” the 8 bridgeable ones above plus subagentStart, postToolUseFailure, and afterFileEdit (Cursor-only events that have no Claude Code equivalent and so cannot be bridged from a Claude Code plugin). Each entry is tagged _managed_by: "audio-hooks" so uninstall removes only ours and leaves any other Cursor hooks alone.

Already have Claude Code? Don't run Path B β€” install --cursor aborts with DUPLICATE_BRIDGE to prevent double-firing. Use Path A instead. (--force overrides the check if you really want both paths active.)

Upgrading a Cursor-only install

cd ~/audio-hooks && git pull && python bin/audio-hooks install --cursor

Re-running the install is idempotent and preserves ~/.cursor/audio-hooks-data/user_preferences.json β€” no data loss, no re-configuration.

Uninstalling

Setup Tell your agent
Path A (auto-bridge) "Uninstall the audio-hooks plugin from Claude Code." β€” Cursor stops bridging automatically once the plugin is gone.
Path B (native install) "Run python ~/audio-hooks/bin/audio-hooks uninstall --cursor." Pass --purge to also delete ~/.cursor/audio-hooks-data/.

Cursor + audio-hooks: natural-language control still works

Once installed (either path), every prompt in Just Say It below works on Cursor too. "Switch the audio theme to chimes", "Snooze audio for an hour", "Enable rate-limit alerts at 80% and 95%" β€” same CLI, same JSON, same skill.


Codex β€” Plugin or Native Install

Codex does NOT auto-bridge Claude Code plugins (unlike Cursor). Use the Codex plugin path when available; keep the native ~/.codex/hooks.json path for users who prefer a cloned repo or are on an older Codex build.

Plugin install prompt:

Install the audio-hooks Codex plugin from github.com/ChanMeng666/echook by running codex plugin marketplace add ChanMeng666/echook and codex plugin add audio-hooks@chanmeng-audio-hooks. Then ask me to reload plugins if Codex requires it, and verify with audio-hooks status plus audio-hooks test all.

Native install prompt:

Clone https://github.com/ChanMeng666/echook into ~/audio-hooks, then run python ~/audio-hooks/bin/audio-hooks install --codex. Read the JSON output: only follow next_steps if feature_flag_state is disabled, disabled_legacy, or parse_error. Then restart Codex and run audio-hooks status to confirm editor_targets.codex.state == "active".

What the install does:

  1. Writes ~/.codex/hooks.json registering the 10 hook events Codex supports (SessionStart, PreToolUse, PermissionRequest, PostToolUse, PreCompact, PostCompact, UserPromptSubmit, SubagentStart, SubagentStop, Stop). Each entry is tagged _managed_by: "audio-hooks" so uninstall removes only ours.
  2. Seeds ~/.codex/audio-hooks-data/user_preferences.json from the project default. Subsequent re-installs preserve this file.
  3. Checks the hooks feature state without touching user TOML. Codex hooks are enabled by default. If ~/.codex/config.toml explicitly has [features].hooks = false, install emits a machine-readable next_steps instruction so the calling AI agent can remove that opt-out or set hooks = true. We never round-trip the user's TOML, because that destroys comments and formatting.
  4. Writes an install marker at ~/.codex/audio-hooks-data/install_marker.json so audio-hooks status can render editor_targets.codex.state.

The audio-hooks canonical events with no Codex equivalent (Notification, SessionEnd, WorktreeCreate, Elicitation, etc.) no-op cleanly under the codex invoker with a debug NDJSON event β€” they never block, error, or interfere with Codex's session.

Uninstalling Codex audio-hooks

Run python ~/audio-hooks/bin/audio-hooks uninstall --codex. Pass --purge to also delete ~/.codex/audio-hooks-data/. The uninstall never touches ~/.codex/config.toml.

Codex + audio-hooks: natural-language control still works

Once installed, every prompt in Just Say It below works under Codex too. "Switch the audio theme to chimes", "Snooze audio for an hour" β€” same CLI, same JSON output. Just point your Codex agent at the project root or use audio-hooks from PATH.


Just Say It β€” Natural Language Control

Once installed (Claude Code, Cursor, or Codex β€” same CLI everywhere), you operate the project the same way: talk to your AI agent in plain English, it runs the right audio-hooks subcommand, reads the JSON output, and reports back. Every configuration is one message. The diagrams below show "Your AI Agent" generically β€” substitute Claude Code, Cursor's agent, or Codex CLI as appropriate.

sequenceDiagram
    actor You as You
    participant CC as Your AI Agent

    rect rgb(219, 234, 254)
    Note over You,CC: Audio Theme
    You->>CC: Switch audio-hooks to the chime theme.
    CC-->>You: audio-hooks theme set custom β€” switched to chimes.
    You->>CC: Switch audio-hooks to the voice theme.
    CC-->>You: audio-hooks theme set default β€” switched to ElevenLabs Jessica.
    end

    rect rgb(220, 252, 231)
    Note over You,CC: Snooze & Mute
    You->>CC: Snooze audio for 30 minutes.
    CC-->>You: audio-hooks snooze 30m β€” muted until 3:45 PM.
    You->>CC: Snooze audio for 8 hours.
    CC-->>You: audio-hooks snooze 8h β€” quiet for the rest of the day.
    You->>CC: Unmute audio.
    CC-->>You: audio-hooks snooze off β€” audio resumed.
    You->>CC: Is audio currently muted?
    CC-->>You: audio-hooks snooze status β€” not snoozed.
    end

    rect rgb(254, 243, 199)
    Note over You,CC: Hook Selection & Notification Mode
    You->>CC: Configure audio-hooks to only fire on stop,<br/>notification, and permission_request β€”<br/>disable everything else.
    CC-->>You: audio-hooks hooks enable-only stop notification<br/>permission_request β€” 3 hooks active, rest disabled.
    You->>CC: Enable session_start and session_end hooks<br/>so I hear when sessions begin and end.
    CC-->>You: session_start and session_end enabled.
    You->>CC: Switch audio-hooks to audio-only mode,<br/>no desktop popups.
    CC-->>You: Notification mode set to audio_only.
    You->>CC: Switch to notification-only mode β€”<br/>desktop popups but no audio.
    CC-->>You: Notification mode set to notification_only.
    You->>CC: For the stop hook only, use desktop<br/>notification without audio.
    CC-->>You: Per-hook override: stop β†’ notification_only.
    You->>CC: Make notifications more detailed.
    CC-->>You: Detail level set to verbose.
    end
Loading
sequenceDiagram
    actor You as You
    participant CC as Your AI Agent

    rect rgb(237, 233, 254)
    Note over You,CC: Webhooks & Integrations
    You->>CC: Send audio-hooks alerts to my Slack webhook<br/>at https://hooks.slack.com/services/... and test it.
    CC-->>You: Webhook set to slack format. Test delivered.
    You->>CC: Send alerts to my Discord webhook instead.
    CC-->>You: Webhook set to discord format. Test delivered.
    You->>CC: Send alerts to ntfy at<br/>https://ntfy.sh/my-channel. Test it.
    CC-->>You: Webhook set to ntfy format. Test delivered.
    You->>CC: Only send stop and stop_failure events<br/>to the webhook, nothing else.
    CC-->>You: Webhook hook_types set to [stop, stop_failure].
    end

    rect rgb(254, 226, 226)
    Note over You,CC: TTS & Rate Limits
    You->>CC: Enable audio-hooks TTS and have it speak<br/>Claude's actual final message instead of<br/>a generic announcement.
    CC-->>You: TTS enabled with speak_assistant_message = true.
    You->>CC: Set the stop hook TTS message to<br/>"Build finished" instead of the default.
    CC-->>You: Custom TTS message for stop set.
    You->>CC: Make sure audio-hooks rate-limit alerts are<br/>enabled with 80% and 95% thresholds for both<br/>5-hour and 7-day windows.
    CC-->>You: Rate-limit alerts on β€” 80%, 95% for both windows.
    end

    rect rgb(207, 250, 254)
    Note over You,CC: Status Line & Context Monitor
    You->>CC: Install the audio-hooks status line<br/>in my Claude Code settings.
    CC-->>You: audio-hooks statusline install β€” restart to see it.
    You->>CC: Configure the status line to only show<br/>context usage.
    CC-->>You: Visible segments set to [context].
    You->>CC: Show only context and API quota<br/>in the status line.
    CC-->>You: Visible segments set to [context, api_quota].
    You->>CC: Reset the status line to show all segments.
    CC-->>You: Visible segments reset to all.
    end

    rect rgb(229, 231, 235)
    Note over You,CC: Monitor, Debug & Uninstall
    You->>CC: Enable the audio-hooks file_changed hook and<br/>configure it to watch .env and .envrc.
    CC-->>You: file_changed enabled, watching [.env, .envrc].
    You->>CC: Test all my audio-hooks hooks and tell me<br/>if any failed.
    CC-->>You: audio-hooks test all β€” 26/26 passed.
    You->>CC: What's the current state of audio-hooks?
    CC-->>You: audio-hooks status β€” theme: default,<br/>18 hooks enabled, 0 errors.
    You->>CC: Show me the last 20 errors and clear the log.
    CC-->>You: 2 errors found (WEBHOOK_TIMEOUT). Log cleared.
    You->>CC: What version of audio-hooks am I running?
    CC-->>You: v6.0.0, plugin install.
    You->>CC: Please uninstall audio-hooks completely.
    CC-->>You: Plugin uninstalled. All hooks removed.
    end
Loading

Each prompt is one message. Your AI agent (Claude Code, Cursor, or Codex) parses it, runs the right audio-hooks subcommand(s), and reports back. You don't memorise anything.

Plain-text prompt reference β€” copy-friendly table
Goal Paste this into your AI agent (Claude Code / Cursor / Codex)
Audio Theme
Switch to chime sounds "Switch audio-hooks to the chime theme."
Switch to voice sounds "Switch audio-hooks to the voice theme."
Snooze & Mute
Mute for 30 minutes "Snooze audio for 30 minutes."
Mute for the rest of the day "Snooze audio for 8 hours."
Unmute "Unmute audio."
Check mute status "Is audio-hooks currently muted?"
Hook Selection
Only keep critical alerts "Only fire audio-hooks on stop, notification, and permission_request. Disable everything else."
Enable session start/end sounds "Enable the session_start and session_end hooks."
Enable tool execution sounds "Enable pretooluse and posttooluse audio."
Notification Mode
Audio only, no desktop popups "Switch audio-hooks to audio-only mode."
Desktop popups only, no audio "Switch audio-hooks to notification-only mode."
Per-hook override "For the stop hook, use desktop notification without audio."
Make notifications verbose "Make audio-hooks notifications more detailed."
Disable all notifications "Disable all audio-hooks notifications entirely."
Webhooks
Send alerts to Slack "Send audio-hooks alerts to my Slack webhook at https://hooks.slack.com/services/... and test it."
Send alerts to Discord "Send audio-hooks alerts to my Discord webhook at https://discord.com/api/webhooks/... and test it."
Send alerts to Teams "Send audio-hooks alerts to my Teams webhook. Test it."
Send alerts to ntfy "Send audio-hooks alerts to https://ntfy.sh/my-topic in ntfy format. Test it."
Only webhook certain events "Only send stop and stop_failure events to the webhook."
Disable webhook "Disable the audio-hooks webhook."
TTS (Text-to-Speech)
Speak Claude's reply out loud "Enable audio-hooks TTS and speak Claude's actual final message."
Custom TTS message for a hook "Set the audio-hooks stop TTS message to 'Build finished'."
Limit spoken message length "Limit audio-hooks TTS to 300 characters."
Rate-limit Alerts
Enable with custom thresholds "Enable audio-hooks rate-limit alerts at 80% and 95% for both windows."
Adjust 5-hour thresholds "Set audio-hooks 5-hour rate-limit thresholds to 75% and 90%."
Status Line
Add a status bar "Install the audio-hooks status line."
Status bar: context only "Only show context usage in the audio-hooks status line."
Status bar: context + API quota "Show context and API quota in the audio-hooks status line."
Status bar: show everything "Reset the audio-hooks status line to show all segments."
Remove status bar "Uninstall the audio-hooks status line."
File Watching
Watch .env for changes "Enable the audio-hooks file_changed hook and watch .env and .envrc."
Monitor & Debug
Test all hooks "Test all audio-hooks and tell me if any failed."
Show current state "Show the current audio-hooks status β€” enabled hooks, theme, and recent errors."
Why no sound? "Audio-hooks isn't playing sounds. Diagnose and fix it."
Show recent errors "Show me the last 20 audio-hooks errors."
Clear the log "Clear the audio-hooks event log."
Check version "What version of audio-hooks am I running?"
Adjust debounce timing "Set audio-hooks debounce to 1000ms."
Uninstall
Uninstall "Please uninstall audio-hooks completely."

How It Works

flowchart LR
    CC[Claude Code event] -->|stdin JSON| MR{native matcher<br/>routing}
    MR -->|session_start_resume| HR[hook_runner.py]
    MR -->|stop_failure_rate_limit| HR
    MR -->|notification_idle_prompt| HR
    MR -->|...| HR

    HR -->|reads| RL[rate-limit pre-check<br/>marker debounce]
    HR -->|reads| CFG[user_preferences.json]
    HR -->|reads| MARK[snooze markers]

    HR -->|fires| AUDIO[Audio playback<br/>26 MP3s, 2 themes]
    HR -->|fires| NOTIF[Desktop notification]
    HR -->|fires| TTS[TTS announcement]
    HR -->|fires| WH[Webhook subprocess<br/>fire-and-forget]
    HR -->|writes| LOG[(NDJSON event log<br/>schema audio-hooks.v1)]

    style CC fill:#4A90E2,color:#fff
    style HR fill:#7ED321,color:#000
    style RL fill:#F5A623,color:#000
    style AUDIO fill:#F5A623,color:#000
    style WH fill:#9013FE,color:#fff
    style LOG fill:#50E3C2,color:#000
Loading

Claude Code fires hook events as JSON on stdin. Native matchers in hooks.json route each event to hook_runner.py with a synthetic event name. The runner checks snooze state, rate-limit thresholds, debounce, and user filters β€” then fires audio playback, desktop notifications, TTS, and webhooks as configured.

Β Β AI Control Surface
flowchart TB
    USER[Human] -->|natural language| CLAUDE[Claude Code]
    CLAUDE -->|activates| SKILL[/audio-hooks SKILL/]
    SKILL -->|invokes via Bash tool| BIN[bin/audio-hooks]

    BIN -->|reads| CONFIG[(user_preferences.json<br/>in plugin data dir)]
    BIN -->|reads| MARKERS[(snooze markers)]
    BIN -->|reads| LOGS[(NDJSON event log)]
    BIN -->|writes| CONFIG
    BIN -->|writes| MARKERS

    BIN -->|invokes| HR[hook_runner.run_hook<br/>for `audio-hooks test`]

    BIN -->|JSON to stdout| CLAUDE
    CLAUDE -->|reports back| USER

    style USER fill:#4A90E2,color:#fff
    style CLAUDE fill:#9013FE,color:#fff
    style SKILL fill:#7ED321,color:#000
    style BIN fill:#F5A623,color:#000
Loading
Β Β Hook Lifecycle
flowchart TD
    EVT[Hook event fires<br/>e.g. Stop] --> SNOOZE{snooze<br/>active?}
    SNOOZE -->|yes| EXIT0[exit 0 silent]
    SNOOZE -->|no| RL{rate_limits<br/>in stdin?}
    RL -->|yes| RLCHK{crossed<br/>threshold?}
    RLCHK -->|yes| RLAUDIO[play warning audio<br/>+ write debounce marker]
    RLCHK -->|no| DEB
    RL -->|no| DEB{debounced<br/>recently?}
    RLAUDIO --> DEB
    DEB -->|yes| EXIT0
    DEB -->|no| FILTER{user filter<br/>matches?}
    FILTER -->|yes, exclude| EXIT0
    FILTER -->|no| ENABLED{hook<br/>enabled?}
    ENABLED -->|no| EXIT0
    ENABLED -->|yes| FIRE[play audio + notif + TTS + webhook]
    FIRE --> LOG[write NDJSON event]

    style EVT fill:#4A90E2,color:#fff
    style FIRE fill:#7ED321,color:#000
    style RLAUDIO fill:#F5A623,color:#000
    style EXIT0 fill:#999,color:#fff
Loading
Β Β Plugin Layout
flowchart TB
    REPO[echook/]

    REPO --> CP[.claude-plugin/marketplace.json]
    REPO --> PLUGINS[plugins/audio-hooks/]
    REPO --> CANON[CANONICAL SOURCE]

    PLUGINS --> P_MANIFEST[.claude-plugin/plugin.json]
    PLUGINS --> P_HOOKS[hooks/hooks.json<br/>matcher-scoped]
    PLUGINS --> P_RUNNER[runner/run.py]
    PLUGINS --> P_SKILL[skills/audio-hooks/SKILL.md]
    PLUGINS --> P_BIN[bin/ β€” audio-hooks + statusline]
    PLUGINS --> P_AUDIO[audio/ β€” bundled MP3s]
    PLUGINS --> P_CONFIG[config/default_preferences.json]

    CANON --> C_HOOKS[hooks/hook_runner.py]
    CANON --> C_BIN[bin/audio-hooks + audio-hooks.py + .cmd]
    CANON --> C_AUDIO[audio/default + audio/custom]
    CANON --> C_CONFIG[config/]
    CANON --> C_SCRIPTS[scripts/ β€” install + build-plugin + uninstall]

    C_HOOKS -.->|build-plugin.sh syncs| P_HOOKS
    C_BIN -.->|build-plugin.sh syncs| P_BIN
    C_AUDIO -.->|build-plugin.sh syncs| P_AUDIO

    style REPO fill:#4A90E2,color:#fff
    style PLUGINS fill:#7ED321,color:#000
    style CANON fill:#F5A623,color:#000
Loading

Key Features

Status Line with Context Monitor

Real-time context window and API quota bars β€” color-coded warnings before Claude enters the "agent dumb zone".

Status Line β€” context window monitor

[Opus] πŸ“ D:\…\claude-code-audio-hooks | echook v6.0.0 | 6/26 Sounds | Webhook: ntfy | Theme: Voice
[MUTED 23m]  feat/audio-v5  API Quota: 78%  Context: 65% (130K/200K)  /compact

Status line anatomy β€” two lines, 10 freely-combinable segments:

flowchart LR
    subgraph L1["Line 1 β€” identity"]
        direction LR
        M["model<br/>[Opus]"] --- CWD["πŸ“ cwd<br/>…/echook"] --- V["version"] --- S["sounds"] --- W["webhook"] --- T["theme"]
    end
    subgraph L2["Line 2 β€” live state"]
        direction LR
        SN["snooze"] --- B["🌿 branch"] --- AQ["api_quota bar"] --- CX["context bar<br/>+ /compact hint"]
    end
    L1 ~~~ L2

    style CWD fill:#7ED321,color:#000
    style CX fill:#F5A623,color:#000
    style AQ fill:#F5A623,color:#000
Loading

The green cwd segment shows which project a session is in. Read more under 10 customisable segments below.

Color Range Meaning Action
Green < 50% Safe β€” agent performs well Keep working
Yellow 50-80% Caution β€” entering the "dumb zone" Type /compact or /clear
Red > 80% Danger β€” agent makes frequent errors Type /compact immediately

Absolute counts (v5.1.3+). When Claude Code reports the context window size, the segment also shows current/max tokens, e.g. Context: 83% (166K/200K). This makes the math obvious when you /model-switch between context-window variants β€” switching from Opus 4.7 (1M) to Sonnet 4.6 (200K) keeps your tokens identical but shrinks the denominator 5Γ—, so a sudden jump from 17% to 83% is expected, not a bug. See docs/TROUBLESHOOTING.md#context-97-or-any-sudden-jump-right-after-switching-models for the full explanation.

10 customisable segments
Segment Shows
model Model name (e.g. [Opus])
cwd Current working directory (abbreviated path β€” tells you which project you're in)
version echook version
sounds Enabled sound count
webhook Webhook status
theme Audio theme
snooze Mute countdown (when active)
branch Git branch name
api_quota API usage quota bar
context Context window usage bar

Audio Themes

Theme Style Source
default ElevenLabs Jessica voice β€” short spoken phrases like "Task completed" audio/default/*.mp3
custom Modern UI sound effects (chimes, beeps) audio/custom/chime-*.mp3

Say "switch to chimes" or "switch to voice" β€” Claude Code handles the rest.

26 Hook Events

26 events covering the full Claude Code lifecycle β€” from session start to file changes, permission requests to rate-limit warnings. 6 are enabled by default; toggle any with natural language.

Full hook events table
Hook Default Audio file Native matchers
notification on notification-urgent.mp3 permission_prompt / idle_prompt / auth_success / elicitation_dialog
stop on task-complete.mp3
subagent_stop on subagent-complete.mp3 agent type
permission_request on permission-request.mp3 tool name
permission_denied on permission-denied.mp3
task_created on task-created.mp3
task_completed team-task-done.mp3
session_start session-start.mp3 startup / resume / clear / compact
session_end session-end.mp3 clear / resume / logout / prompt_input_exit
pretooluse task-starting.mp3 tool name
posttooluse task-progress.mp3 tool name
posttoolusefailure tool-failed.mp3 tool name
userpromptsubmit prompt-received.mp3
subagent_start subagent-start.mp3 agent type
precompact / postcompact notification-info.mp3 / post-compact.mp3 manual / auto
stop_failure stop-failure.mp3 rate_limit / authentication_failed / billing_error / server_error / unknown
teammate_idle teammate-idle.mp3
config_change config-change.mp3
instructions_loaded instructions-loaded.mp3
worktree_create / worktree_remove worktree-create.mp3 / worktree-remove.mp3
elicitation / elicitation_result elicitation.mp3 / elicitation-result.mp3
cwd_changed cwd-changed.mp3
file_changed file-changed.mp3 literal filenames

Webhooks

Fan out hook events to Slack, Discord, Teams, ntfy, or any HTTP endpoint. Versioned audio-hooks.webhook.v1 payload. Fire-and-forget via subprocess β€” never blocks the hook. Say "send alerts to my Slack" and Claude Code sets it up.

Rate-limit Alerts

Watches every hook's stdin for rate_limits and plays a one-shot warning at configurable thresholds (default 80%/95%). Each (window, threshold, resets_at) fires exactly once β€” warned at 80%, again at 95%, never spammed.

TTS β€” Speak Claude's Reply

Instead of a static "Task completed", TTS can speak a sanitized summary of Claude's actual final message (truncated to 200 chars on a sentence boundary; code blocks and secrets are stripped automatically before anything is spoken). Useful when you're away from the screen. Off by default. Say "speak Claude's actual reply when done" to enable.


Technical Reference

CLI, configuration, environment variables, error codes, logging, manual install (click to expand)

audio-hooks CLI

Single Python binary on PATH. JSON output, no prompts, no spinners.

Subcommand Purpose
audio-hooks manifest Canonical introspection β€” every subcommand, hook, config key, error code, env var
audio-hooks manifest --schema JSON Schema for user_preferences.json
audio-hooks status Full state snapshot
audio-hooks version Version + install mode detection
audio-hooks get <dotted.key> Read any config key
audio-hooks set <dotted.key> <value> Write any config key (auto-coerces)
audio-hooks hooks list All 26 hooks with current state
audio-hooks hooks enable/disable <name> Toggle a hook
audio-hooks hooks enable-only <a> <b> Exclusive enable
audio-hooks theme list/set <name> Audio theme
audio-hooks snooze [duration]/off/status Mute hooks (default 30m)
audio-hooks webhook/set/clear/test Webhook config + test
audio-hooks tts set ... TTS config
audio-hooks rate-limits set ... Rate-limit alert thresholds
audio-hooks test <hook|all> Smoke-test hooks
audio-hooks diagnose System check
audio-hooks logs tail/clear NDJSON event log
audio-hooks install/uninstall Non-interactive install/uninstall
audio-hooks statusline show/install/uninstall Status line management

Configuration Keys

Key Type Default Effect
audio_theme default | custom default Voice recordings vs chimes
enabled_hooks.<hook> bool varies Per-hook toggle
playback_settings.debounce_ms int 500 Min ms between same hook firing
notification_settings.mode enum audio_and_notification audio_only / notification_only / audio_and_notification / disabled
notification_settings.detail_level enum standard minimal / standard / verbose
webhook_settings.enabled bool false Webhook fan-out
webhook_settings.url string "" Target URL
webhook_settings.format enum raw slack / discord / teams / ntfy / raw
webhook_settings.hook_types array ["stop","notification",...] Which hooks fire the webhook
tts_settings.enabled bool false TTS announcements
tts_settings.speak_assistant_message bool false TTS Claude's actual reply on stop
tts_settings.assistant_message_max_chars int 200 Truncation cap
rate_limit_alerts.enabled bool true Watch stdin rate_limits
rate_limit_alerts.five_hour_thresholds int[] [80, 95] 5h window thresholds
rate_limit_alerts.seven_day_thresholds int[] [80, 95] 7d window thresholds
statusline_settings.visible_segments string[] [] (all) Status line segments to show

Environment Variables

Variable Purpose
CLAUDE_PLUGIN_DATA Plugin install state directory (auto-set by Claude Code)
CLAUDE_PLUGIN_ROOT Plugin install root (auto-set)
CLAUDE_AUDIO_HOOKS_DATA Explicit override for state directory
CLAUDE_AUDIO_HOOKS_PROJECT Explicit override for project root
CLAUDE_HOOKS_DEBUG 1 to write debug-level events to NDJSON log
ELEVENLABS_API_KEY Used by scripts/generate-audio.py (never logged)

Stable Error Codes

Code When Suggested fix
AUDIO_FILE_MISSING Audio file doesn't exist audio-hooks diagnose
AUDIO_PLAYER_NOT_FOUND No audio player binary audio-hooks diagnose
AUDIO_PLAY_FAILED Player exited with error audio-hooks test
INVALID_CONFIG user_preferences.json malformed audio-hooks manifest --schema
CONFIG_READ_ERROR Can't read config audio-hooks status
WEBHOOK_HTTP_ERROR Webhook returned non-2xx audio-hooks webhook test
WEBHOOK_TIMEOUT Webhook timed out audio-hooks webhook test
NOTIFICATION_FAILED Desktop notification failed audio-hooks diagnose
TTS_FAILED TTS engine failed audio-hooks tts set --enabled false
SETTINGS_DISABLE_ALL_HOOKS disableAllHooks: true in settings audio-hooks diagnose
DUAL_INSTALL_DETECTED Both install methods active audio-hooks uninstall
PROJECT_DIR_NOT_FOUND Can't locate project audio-hooks status
UNKNOWN_HOOK_TYPE Unrecognised hook name audio-hooks hooks list
INTERNAL_ERROR Unexpected error audio-hooks logs tail

NDJSON Event Log

Every event is one JSON object per line at ${CLAUDE_PLUGIN_DATA}/logs/events.ndjson. Schema audio-hooks.v1.

{"ts":"2026-04-11T10:23:45.123Z","schema":"audio-hooks.v1","level":"info","hook":"stop","session_id":"abc","action":"play_audio","audio_file":"chime-task-complete.mp3","duration_ms":42}

Levels: debug, info, warn, error. Log rotation: 5 MB cap, 3 files kept.

Manual Install Reference

Plugin install (two slash commands inside Claude Code):

/plugin marketplace add ChanMeng666/echook
/plugin install audio-hooks@chanmeng-audio-hooks
/reload-plugins

Script install (cloned-repo / non-plugin path β€” the agent runs this for you):

git clone https://github.com/ChanMeng666/echook.git
cd echook
bash scripts/install-complete.sh    # always non-interactive β€” never prompts

Both paths share the same hook_runner.py and audio-hooks CLI. They are mutually exclusive β€” audio-hooks diagnose reports DUAL_INSTALL_DETECTED if both are active.

ElevenLabs Audio Generator

scripts/generate-audio.py reads config/audio_manifest.json and regenerates audio via the ElevenLabs API:

ELEVENLABS_API_KEY=sk_... python scripts/generate-audio.py           # generate missing
ELEVENLABS_API_KEY=sk_... python scripts/generate-audio.py --force   # regenerate all
python scripts/generate-audio.py --dry-run                           # preview

To add a new audio file: edit config/audio_manifest.json, run the generator, then bash scripts/build-plugin.sh.


Platform Support

Platform Audio player Status
Windows (PowerShell / Git Bash / WSL2) PowerShell MediaPlayer Fully supported
macOS afplay Fully supported
Linux mpg123 / ffplay / paplay / aplay (auto-detected) Fully supported

Python 3.6+ is the only runtime requirement.


Troubleshooting

Β Β No sound at all

Run audio-hooks diagnose, look for any error code, and run its suggested_command. Or just say: "Audio-hooks isn't playing sounds. Diagnose and fix it."

Β Β Hearing double sounds

Both the script install and the plugin install are active. Diagnose reports DUAL_INSTALL_DETECTED. Fix: audio-hooks uninstall (removes the script install, preserves config + audio) β€” or just say "audio-hooks is playing double sounds, fix it."

Β Β Plugin won't install

Run claude plugin validate plugins/audio-hooks from the project root β€” surfaces manifest schema errors. v5.0.1+ verified clean on Claude Code v2.1.101.

Β Β pretooluse / posttooluse too noisy

They fire on every tool execution (Read, Glob, Grep, etc.) β€” disabled by default for this reason. Enable explicitly with "enable pretooluse and posttooluse audio".


Uninstall

Just tell your agent: "Uninstall audio-hooks completely." It picks the right path for your install. For reference:

Plugin install:

/plugin uninstall audio-hooks@chanmeng-audio-hooks

Script install (the agent runs this β€” always non-interactive):

audio-hooks uninstall            # preserve config + audio
audio-hooks uninstall --purge    # also remove config + audio

For Developers

Repository layout, workflow, and contribution guide (click to expand)

Repository Layout

echook/
β”œβ”€β”€ .claude-plugin/marketplace.json
β”œβ”€β”€ plugins/audio-hooks/              # plugin layout (populated by build-plugin.sh)
β”‚   β”œβ”€β”€ .claude-plugin/plugin.json
β”‚   β”œβ”€β”€ hooks/hooks.json
β”‚   β”œβ”€β”€ runner/run.py
β”‚   β”œβ”€β”€ skills/audio-hooks/SKILL.md
β”‚   β”œβ”€β”€ bin/
β”‚   β”œβ”€β”€ audio/
β”‚   β”œβ”€β”€ cursor-hooks/                 # mirror of canonical cursor-hooks/
β”‚   β”œβ”€β”€ codex-hooks/                  # mirror of canonical codex-hooks/
β”‚   └── config/default_preferences.json
β”œβ”€β”€ hooks/                            # CANONICAL
β”‚   β”œβ”€β”€ hook_runner.py
β”‚   β”œβ”€β”€ invoker.py                    # detect_invoker() β€” Claude Code / Cursor / Codex
β”‚   └── user_preferences.py
β”œβ”€β”€ bin/                              # CANONICAL
β”‚   β”œβ”€β”€ audio-hooks / audio-hooks.py / audio-hooks.cmd
β”‚   └── audio-hooks-statusline / .py / .cmd
β”œβ”€β”€ audio/                            # CANONICAL: 26 default + 26 custom
β”œβ”€β”€ config/
β”‚   β”œβ”€β”€ default_preferences.json
β”‚   β”œβ”€β”€ user_preferences.schema.json
β”‚   └── audio_manifest.json
β”œβ”€β”€ cursor-hooks/hooks.json           # CANONICAL: Cursor IDE install template
β”œβ”€β”€ codex-hooks/hooks.json            # CANONICAL: Codex CLI install template
β”œβ”€β”€ scripts/
β”‚   β”œβ”€β”€ install-complete.sh / install-windows.ps1
β”‚   β”œβ”€β”€ uninstall.sh / build-plugin.sh
β”‚   β”œβ”€β”€ bump-version.sh               # atomic version bump across 8 files
β”‚   β”œβ”€β”€ generate-audio.py
β”‚   └── ...
β”œβ”€β”€ tests/                            # 174 unittest cases (Cursor + Codex bridge contracts)
β”œβ”€β”€ CLAUDE.md
β”œβ”€β”€ README.md
└── CHANGELOG.md

Workflow

  1. Edit canonical files (/hooks/, /bin/, /audio/, /config/, /cursor-hooks/, /codex-hooks/)
  2. Run bash scripts/build-plugin.sh to sync into plugin layout
  3. CI verifies in-sync via bash scripts/build-plugin.sh --check
  4. Validate: claude plugin validate plugins/audio-hooks
  5. Test: python -m unittest discover -v tests (174 tests; Ubuntu/Windows/macOS Γ— Python 3.9/3.12/3.13 in CI)
  6. Bump version (when releasing): bash scripts/bump-version.sh <new_version> β€” atomically updates 8 canonical version locations and re-runs build-plugin.sh

Contributing

Pull requests welcome. Fork, clone, make changes to canonical files, run build-plugin.sh, validate, test end-to-end, and submit with a conventional commit message.


Documentation

Agents start here: an AI agent operating echook should read AGENTS.md (or llms.txt) and run audio-hooks manifest β€” that's the complete, live, truthful state of the project. Everything else below is for humans who are curious.

Document Purpose
AGENTS.md Agent-facing operating guide β€” the critical rules (CLI-only, manifest-first, two-track scope, AI-agent-first). Mirrored as CLAUDE.md.
llms.txt AI-agent entrypoint β€” orients any LLM/agent to install + operate via the CLI.
docs/INSTALLATION_GUIDE.md Full install/uninstall flow for Claude Code / Cursor / Codex
docs/TROUBLESHOOTING.md Diagnostic recipes for common issues
docs/ARCHITECTURE.md System architecture and design decisions
CHANGELOG.md Detailed version history
GitHub Releases Tagged, versioned distribution β€” every release from v6.0.0 back, with notes. Latest release.
CI (smoke.yml) Live build status across Ubuntu/Windows/macOS Γ— Python 3.9/3.12/3.13.
audio-hooks manifest Live source of truth β€” includes subcommands, hooks, config_keys, error_codes, env_vars, editor_targets, supported_editors, and pointers (paths to every doc above). Always up to date.

Design Philosophy β€” This project is AI-operated, not AI-assisted. A typical CLI tool: the human learns the tool. echook: the human says what they want, Claude Code learns the tool and does the work. The human is upstream of Claude Code, not downstream of the CLI.


License

This project is licensed under the MIT License β€” see LICENSE for details.

  • Commercial use allowed
  • Modification allowed
  • Distribution allowed
  • Private use allowed

Author

Chan Meng
Chan Meng

Creator & Lead Developer

GitHub LinkedIn Website

Buy Me A Coffee



Chan Meng

Chan Meng
Need a custom app like this one? I build them β€” let's talk.

Email Chan Meng Chan Meng on GitHub

About

πŸ”Š echook β€” AI-operated audio notifications for Claude Code, Cursor IDE & Codex CLI β€” 26 hooks, voice + chime themes, TTS, webhooks, rate-limit alerts, status line. Tell your AI agent to install β€” natural language forever after.

Topics

Resources

License

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Sponsor this project

  •  

Packages

 
 
 

Contributors