A Claude Code plugin that alerts you the moment Claude needs your input. On a
permission_prompt or idle_prompt notification it emits up to three additive,
independently-toggleable channels: an audible terminal bell, an OSC 9 terminal
notification, and, on macOS and Linux, an OS-native desktop toast.
- Fires on attention prompts. Only the
permission_promptandidle_promptnotification types trigger it; every other notification is a silent no-op. - Advisory, never blocking. The hook always exits
0(Notification hooks cannot block). - Three additive channels, each default on and independently mutable:
| Channel | Option | What it does |
|---|---|---|
bell |
desktop_notification_bell_enabled |
Audible terminal bell (bare BEL). |
terminal_notify |
desktop_notification_terminal_notify_enabled |
OSC 9 desktop notification, emitted via the hook's terminalSequence output (Claude Code v2.1.141+ writes it through its own terminal path). |
os_toast |
desktop_notification_os_toast_enabled |
OS-native toast (see per-OS table). |
Platform facts verified 2026-07-18: hook terminalSequence output landed in Claude Code
v2.1.141 per the Claude Code changelog;
channel semantics per the hooks reference.
| OS | Tool | Requirement |
|---|---|---|
| macOS | osascript … display notification |
Built-in. No dependency. First run prompts to allow notifications for the terminal app. |
| Linux | notify-send (libnotify) |
Install libnotify-bin (Debian/Ubuntu) or libnotify (Fedora). Absent → the os_toast channel is a silent no-op. |
| Windows / other | none | No OS-toast branch: a fire-and-forget hook process leaves no live activator host for a WinRT toast to render into, so it would never reliably surface. The terminal_notify channel (OSC 9) carries Windows attention. Windows Terminal handles OSC 9. |
The OS toast body includes the current git branch when the project is a git
repository (e.g. Waiting for your input — feat/my-branch); outside a repo it is
just the message.
The hook runs on Bash 3.2+. On native Windows, install
Git for Windows so Git Bash is
available. It needs Node.js on PATH: the hook launches through node hooks/exec-bash.mjs, and
Claude Code's native binary neither ships nor uses Node, so without it the hook does not launch and
notifications do not fire (install Node.js). It also needs jq on PATH; without jq, notifications
are disabled with a visible notice, once per session and agent, renewed every eighth skip. macOS needs nothing
further; Linux needs libnotify only for the os_toast channel; Windows needs
nothing (terminal channels only). Telemetry
timing uses EPOCHREALTIME (Bash 5.0+); on older bash the telemetry envelope is
skipped while notifications still fire.
Node.js claim verified 2026-09-29 per the
Claude Code setup page ("The installed claude binary
does not itself invoke Node") and the hooks reference
("Exec form and shell form"). Recheck when either page changes those statements.
/plugin marketplace add melodic-software/claude-code-plugins
/plugin install desktop-notification@<marketplace>Then verify prerequisites with /desktop-notification:setup check.
Every channel is toggled by its own userConfig boolean (default on; set to
false to mute that channel). Mute the OS toast and keep the rest:
| Option | What it controls |
|---|---|
desktop_notification_enabled |
Master toggle for the whole hook. |
desktop_notification_bell_enabled |
The bell channel. |
desktop_notification_terminal_notify_enabled |
The terminal_notify (OSC 9) channel. |
desktop_notification_os_toast_enabled |
The os_toast channel. |
Set them interactively with /plugin configure desktop-notification@<marketplace>, or headless
on the install command:
claude plugin install desktop-notification@<marketplace> --config desktop_notification_os_toast_enabled=falseOption scoping (user vs project settings, and the per-repository escape hatch) per "How to set these" below.
Set desktop_notification_enabled to false (via /plugin configure desktop-notification@<marketplace> or --config desktop_notification_enabled=false).
Generated from this plugin's .claude-plugin/plugin.json. Every option Claude Code
will prompt for when the plugin is enabled, with the environment variable each hook
reads it from.
| Option | Type | Default | Environment variable | Description |
|---|---|---|---|---|
desktop_notification_enabled |
boolean | true |
CLAUDE_PLUGIN_OPTION_DESKTOP_NOTIFICATION_ENABLED |
Master switch for the whole notification hook |
desktop_notification_bell_enabled |
boolean | true |
CLAUDE_PLUGIN_OPTION_DESKTOP_NOTIFICATION_BELL_ENABLED |
Audible terminal bell (bare BEL) |
desktop_notification_terminal_notify_enabled |
boolean | true |
CLAUDE_PLUGIN_OPTION_DESKTOP_NOTIFICATION_TERMINAL_NOTIFY_ENABLED |
OSC 9 terminal notification emitted via the hook's terminalSequence output |
desktop_notification_os_toast_enabled |
boolean | true |
CLAUDE_PLUGIN_OPTION_DESKTOP_NOTIFICATION_OS_TOAST_ENABLED |
OS-native desktop toast: macOS (osascript) or Linux (requires notify-send). No effect on Windows, where the terminal channels carry the alert. |
Three supported routes, in the order most people want them:
-
Interactively. Claude Code prompts for declared options when you enable the plugin. To change them later:
/plugin configure desktop-notification@<marketplace>. -
Headless. Repeat
--configfor each option. Replace<marketplace>with the marketplace you installed this plugin from:claude plugin install desktop-notification@<marketplace> -s <scope> --config desktop_notification_enabled=<value>
The same command reconfigures a plugin that is already installed: it prints
already installedand still writes the value. The short-circuit message is about the install, not the config write. Do notclaude plugin uninstallto reconfigure: uninstalling drops this plugin's whole storedpluginConfigsentry, resetting every option in the table above to its default.-sdefaults touser, so pass the scopeclaude plugin listreports for this plugin. The verified-version record lives in the plugin-reconfiguration convention.The value is stored immediately; the session you are in does not change. Hooks are handed their
CLAUDE_PLUGIN_OPTION_*when the session starts, so start a fresh Claude Code session before expecting new behavior. A check run in the old session still reports the old value, and that is not a failed write. -
By hand, in settings. Add the value under
pluginConfigsin your user settings (~/.claude/settings.json):{ "pluginConfigs": { "desktop-notification@<marketplace>": { "options": { "desktop_notification_enabled": <value> } } } }Plugin option values are read from user,
--settings, and managed settings only, not from a project's.claude/settings.json. To vary behavior per repository, enable or disable the plugin in that project'senabledPluginsinstead of setting an option there.
Do not set the CLAUDE_PLUGIN_OPTION_* variables yourself. They are how Claude Code
hands a configured value to a hook process; the value comes from the routes above.
- User configuration: the
userConfigschema and theCLAUDE_PLUGIN_OPTION_<KEY>export - Plugin install options: the
--configflag's reference entry - Plugins and skills settings:
enabledPlugins,extraKnownMarketplaces,pluginConfigs - Settings files and who they affect: user vs project vs local precedence
- Manage installed plugins: enabling, disabling,
/plugin list
When the consumer sets HOOK_TELEMETRY_SINK to an executable, the hook emits one
telemetry envelope per run.
hook: "desktop-notification", hook_event: "Notification", and a data payload
of notification_type plus the channels that fired. Unset → exact no-op.
MIT (SPDX-License-Identifier: MIT).