System notifications when Claude finishes.
macOS and Linux system notifications for Claude Code, with a notification cooldown so rapid back-to-back responses don't spam you. When Claude finishes a long job in the background, a notification fires carrying the repo name, branch, and Claude's actual last message.
claude plugin install notify@apurvbazari-plugins
/notify:setup
From Claude stopping to a system alert
A linear pipeline runs on every hook event. Click a stage to see detail in the side panel.
The cooldown is the key primitive — each event carries its own minDurationSeconds value: at most one stop or subagentStop notification per that many seconds. Inside the window, the script exits silently. Otherwise it builds a repo / branch subtitle from git and extracts Claude's actual last message as notification content. The script always exits 0, so a missing backend or failed notification can never block Claude.
Intentionally minimal
notify does one thing well: native desktop alerts, with a cooldown and project context. Install globally to get alerts across every Claude Code session, or per-project to tune settings per repo. Both scopes can coexist; the hook reads one config, the project's own when it has one.
Three events, each independently configurable
Notification content is extracted from Claude's real last message. Each event has its own enabled flag, sound, and cooldown. Edit notify-config.json directly — changes take effect immediately, no re-running setup.
| Event | When | Default sound | minDurationSeconds | Default state |
|---|---|---|---|---|
| stop | Claude finishes a response | Hero | 0 | enabled |
| notification | Claude needs your attention | Glass | 0 | enabled |
| subagentStop | A subagent finishes work | — | — | disabled (too noisy) |
stop fires on every Claude response. Its default is 0 (no cooldown); setup recommends 30, so after one notification fires, further stop notifications stay quiet for 30 seconds. notification is never cooldown-filtered, because it means Claude is actively waiting on you. subagentStop is opt-in only.
Three user-facing skills
All under /notify:<name>. check is auto-invocable and read-only; setup and uninstall are destructive and require explicit user invocation.
| Skill | Invocation | What it does |
|---|---|---|
| /notify:setup | user-only | Detects platform, installs the backend (terminal-notifier via Homebrew on macOS, notify-send from libnotify on Linux), detects your editor for click-to-focus, asks global vs per-project, configures all three events, and sends a test notification. |
| /notify:check | auto | Health check — reports which scopes are installed and the current event configuration (sounds, cooldowns, enabled flags) for each, and offers to send a test notification on request. |
| /notify:uninstall | user-only | Surgically removes notify-owned hooks from settings.json and deletes notify-config.json. Leaves unrelated hooks untouched, and leaves the backend installed. |
| wizard | The interactive Q&A that customizes sounds, the notification cooldown, matcher regex, and per-event toggles. A building block invoked by setup (user-invocable: false) — not a command you run. |
Per-event knobs in notify-config.json
All configuration lives in notify-config.json, written by /notify:setup. Both scopes can coexist, but the hook reads exactly one file: the project-local one when it exists, otherwise the global one. Edit directly after setup; the hook reads it on every invocation.
Turn any event on or off without re-running setup. true or false per event.
14 macOS system sounds (Hero, Glass, Ping, Purr, Pop, Submarine, …). Maps to urgency level on Linux.
At most one stop / subagentStop notification per this many seconds, timed from the last one that fired. Default 0 (no cooldown). notification events are never cooldown-filtered.
Bundle ID of the app to bring forward on notification click (e.g. VS Code, Cursor, iTerm2). macOS only.
Shown when contextual content can't be extracted from Claude's response. Static safety net.
Install globally for all sessions, per-project to override. The project-local file is used whole when it exists — there is no key-level merge with the global one.
Cross-platform: macOS and Linux
Same hook, two backends. Cooldown and repo / branch subtitle work everywhere. Sounds and click-to-focus are richer on macOS.
14 system sounds, click-to-focus via bundle ID, title + subtitle + message, Homebrew install. JSON parsed via jq with python3 fallback.
Urgency levels (low/normal/critical mapped from sound setting), app-name, message body. Cooldown and repo/branch subtitle work identically.
notify-send reaches the Linux desktop only if a notification daemon is running. For Windows-side toasts, route through a Windows notifier (e.g. wsl-notify-send) or run Claude Code natively on Windows.