agentsclimarketplace

Claude code notifications

Skill Naz1anmak/claude-code-notifications

Interactive skill that wires Claude Code hooks for OS-native notifications with per-category sounds and focus guard.

Install
npx -y skills add Naz1anmak/claude-code-notifications

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Use when the user wants desktop notifications for the Claude Code CLI (terminal) — alerts when a turn finishes, when a tool needs permission, or when an interactive question (AskUserQuestion/ExitPlanMode) is waiting. Covers macOS, Linux, and Windows. Not for the Claude Code desktop app (which has its own in-app notifications). Triggers on phrases like "notify me when claude finishes", "desktop notification for permission prompts", "set up sounds for claude code", "I miss claude's responses when in another window".

SKILL.md

14.9 KB, ~3.7k tokens by cl100k_base, as published. Nobody here has run it

Claude Code CLI Notifications

Overview

A guided wizard that installs OS-native desktop notifications for the Claude Code CLI (the terminal tool) by wiring three hooks in settings.json. Note: the Claude Code desktop app has its own in-app notifications — this skill targets only the CLI, where hooks live.

Three hooks:

  • Stop — fires when Claude finishes its turn
  • PermissionRequest — fires when a tool (Bash/Edit/Write/AskUserQuestion/ExitPlanMode/…) waits for approval
  • (optional) Notification — fires on idle "still waiting for input" pings

Each hook calls a small platform-specific wrapper script that:

  1. Skips the alert if a terminal emulator is currently focused — the user can already see Claude Code, no need to interrupt.
  2. Shows a native toast with a custom title, body, sound, and icon.

Cross-platform: macOS uses terminal-notifier, Linux uses notify-send + paplay, Windows uses PowerShell BurntToast.

When to Use

  • User asks how to get desktop alerts from Claude Code
  • User says they miss Claude's responses while working in another window/space
  • User wants different sounds for different events (permission vs question vs done)
  • User asks about hooks in settings.json for notifications

Do NOT use for:

  • Mobile push notifications (use inputNeededNotifEnabled / agentPushNotifEnabled settings — they go through Anthropic's mobile app, not OS toasts)
  • Configuring sounds inside the terminal (that's the emulator's bell setting, not a Claude Code hook)

Workflow

Follow these steps in order. Use AskUserQuestion for every user-facing choice, not free-form text.

Step 1 — Detect platform

Run uname -s. Map: Darwin → macOS, Linux → Linux, MINGW*/MSYS*/CYGWIN* → Windows (also check $OS for Windows_NT).

If undetected: ask the user explicitly via AskUserQuestion.

Step 2 — Check & install dependencies

OSToolInstall commandWhere it lives
macOSterminal-notifierbrew install terminal-notifier/opt/homebrew/bin/terminal-notifier (Apple Silicon) or /usr/local/bin/... (Intel)
Linuxlibnotify-bin (notify-send), pulseaudio-utils (paplay)sudo apt install libnotify-bin pulseaudio-utils (Debian/Ubuntu); sudo dnf install libnotify (Fedora); sudo pacman -S libnotify (Arch)/usr/bin/notify-send, /usr/bin/paplay
WindowsBurntToast PowerShell moduleInstall-Module -Name BurntToast -Scope CurrentUser -ForcePSGallery

Run which terminal-notifier / which notify-send / Get-Module -ListAvailable BurntToast first. If missing, ask the user via AskUserQuestion whether to install. Never install without explicit consent — these touch the system.

macOS without brew fallback: osascript -e 'display notification ...'. Caveats: no custom icon, no sound control (always plays the system Default), no group dedup. Mention these tradeoffs to the user if they decline brew.

Step 3 — Ask which hooks to wire

AskUserQuestion (multiSelect: true):

  • ☑ "Claude finished turn" (Stop hook) — recommended
  • ☑ "Permission requests for Bash/Edit/Write/etc" (PermissionRequest, generic tools)
  • ☑ "Interactive questions: AskUserQuestion / ExitPlanMode" (PermissionRequest, question tools)
  • ☐ "Idle reminder after ~60s" (Notification) — usually overkill if Stop is on

Save which were chosen — only emit hooks for the chosen subset.

Step 4 — Ask language for notification text

AskUserQuestion: English / Russian / другое (ask user to provide strings).

Keep a small phrasebook keyed by event:

KeyEnglishRussian
done.body"Response ready""Ответ готов"
permission.subtitle"Permission needed""Нужно разрешение"
permission.body"Request: $TOOL""Запрос: $TOOL"
question.subtitle"Your input needed""Нужен ваш выбор"
question.body"Claude is waiting""Claude ждёт ответа"

Step 5 — Ask sounds per category

AskUserQuestion: "Same sound for everything" / "Different sound per category" / "Silent (visual only)".

If different, ask one sound per chosen category. Show platform-appropriate options:

  • macOS: Glass, Hero, Submarine, Blow, Tink, Ping, Pop, Sosumi (see /System/Library/Sounds/). User can preview with afplay /System/Library/Sounds/<Name>.aiff — suggest the one-liner from the README to audition all of them.
  • Linux: freedesktop event sound IDs (bell, complete, dialog-information, message-new-instant) or a path to a .wav / .ogg. Played via paplay (PulseAudio) or aplay (ALSA).
  • Windows: Default, IM, Mail, Reminder, SMS, or path to a .wav. BurntToast accepts these as -Sound values.

Step 6 — Ask about icon

AskUserQuestion:

  • "Use Claude Desktop's icon" — only offer if /Applications/Claude.app exists (macOS). Extract via sips -s format png /Applications/Claude.app/Contents/Resources/electron.icns --out ~/.claude/assets/claude-icon.png -Z 256.
  • "Use a custom file" — ask for absolute path. Validate it exists and is PNG/JPG/ICNS.
  • "Default (none)" — skip the -appIcon/-contentImage flags.

Save final icon path. On Linux/Windows: -contentImage equivalent is notify-send -i <path> and BurntToast -AppLogo <path>.

macOS caveat to mention: the app-icon (top-left badge) on macOS 13+ is always the sender's icon (terminal-notifier). The bundled icon shows up reliably only as -contentImage (the larger image on the right). This is a system limitation.

Step 7 — Focus guard

AskUserQuestion: "Suppress notifications when a terminal is focused?" → recommend yes (matches IDE/Slack behavior — apps in focus don't ping themselves).

Important: a naive "is any terminal in focus?" check fails for VS Code (and Cursor, Codium, Windsurf). VS Code itself isn't a "terminal" bundle ID, so the plashka used to fire even when the user was looking right at the Claude session.

Worse: the VS Code extension for Claude doesn't run inside the integrated terminal — it runs as a subprocess of the extension host, where $TERM_PROGRAM is not set. So $TERM_PROGRAM=vscode checks only catch the integrated-terminal case, not the plugin.

The robust fix uses three signals, in priority order:

  1. VS Code env vars$VSCODE_PID and $VSCODE_IPC_HOOK are injected by the extension host into both the integrated terminal and extension subprocesses. If either is non-empty, we're inside VS Code (or a fork). Compare against frontmost via bundle-ID substring match (*VSCode*, *Cursor*, *Windsurf*, ...).
  2. $CLAUDE_CODE_ENTRYPOINT=claude-desktop — set when Claude Code runs inside the Claude desktop app's Code section (the app spawns the CLI as a subprocess, and $TERM_PROGRAM is not set there). Here we suppress unconditionally: the desktop app owns notifications entirely — it stays silent while focused and fires its own native toast when backgrounded. If our wrapper also emitted, the user would get two toasts when away from the app (the app's native one + ours). No frontmost check needed.
  3. $TERM_PROGRAM — covers everything else (standalone emulators).
SignalHost app to match
$VSCODE_PID or $VSCODE_IPC_HOOK setbundle-ID substring match against VSCode / vscode / code-oss / VSCodium / Cursor / Windsurf / todesktop
CLAUDE_CODE_ENTRYPOINT=claude-desktopsuppress unconditionally — the desktop app sends its own native notifications
TERM_PROGRAM=Apple_Terminalcom.apple.Terminal
TERM_PROGRAM=iTerm.appcom.googlecode.iterm2
TERM_PROGRAM=ghosttycom.mitchellh.ghostty
TERM_PROGRAM=WarpTerminaldev.warp.Warp-Stable
TERM_PROGRAM=Hyperco.zeit.hyper
(none of the above)fall back to generic-terminal list

Detection of the frontmost app (same as before):

  • macOS: osascript -e 'tell application "System Events" to bundle identifier of first process whose frontmost is true'
  • Linux/X11: xdotool getactivewindow getwindowclassname
  • Linux/Wayland: best-effort via hyprctl activewindow (Hyprland) / swaymsg -t get_tree (Sway). Otherwise no-op (notify always).
  • Windows: P/Invoke GetForegroundWindowGet-Process.ProcessName.

The reference scripts in scripts/ already implement this logic — copy them, don't reinvent.

Step 8 — Install wrapper script

Create ~/.claude/bin/ if it doesn't exist (mkdir -p ~/.claude/bin), then copy the platform-specific script from scripts/ to ~/.claude/bin/notify.<sh|ps1>. chmod +x on POSIX. The script ships with the focus-guard list already populated; the wizard substitutes the user's choices (sounds, icon path, phrasebook) at install time.

See scripts/notify-macos.sh, scripts/notify-linux.sh, scripts/notify-windows.ps1 for the canonical implementations — they're already correct, don't rewrite them inline.

Step 9 — Patch ~/.claude/settings.json

Read existing file first, then merge. Do not overwrite. Use Edit, not Write.

For each chosen hook, append (or replace if a hook on the same event already exists — ask the user first) into .hooks.<EventName>[].hooks[]:

{
  "type": "command",
  "command": "<extract-payload>; $HOME/.claude/bin/notify.sh <args>"
}

<extract-payload> for PermissionRequest is TOOL=$(jq -r .tool_name); case \"$TOOL\" in AskUserQuestion|ExitPlanMode) … ;; *) … ;; esac.

For Stop hook the payload is fixed — no jq needed.

Validate with jq -e '.hooks | keys' ~/.claude/settings.json after writing.

Step 10 — Test

Pipe-test each hook command standalone. Cover both PermissionRequest branches and the Stop hook:

# PermissionRequest — generic tool branch (Hero sound, "Permission needed" subtitle)
echo '{"tool_name":"Bash"}' | bash -c '<PermissionRequest command body>'

# PermissionRequest — interactive question branch (Submarine sound, "Your input needed")
echo '{"tool_name":"AskUserQuestion"}' | bash -c '<PermissionRequest command body>'

# Stop — no stdin needed, just run the command directly
bash -c '<Stop command body>'

Tip: since the focus guard suppresses the toast when a terminal is in front, ask the user to switch to a different app (browser, etc.) before you run the tests — otherwise they'll see "exit=0" but no plashka and think it's broken. Alternatively, run a one-off copy of the wrapper with the guard removed to confirm the rest of the pipeline works:

sed '/# --- focus guard ---/,/esac/d' ~/.claude/bin/notify.sh > /tmp/notify-noguard.sh
chmod +x /tmp/notify-noguard.sh
/tmp/notify-noguard.sh -title "Claude Code" -message "Pipeline check" -sound Hero
rm /tmp/notify-noguard.sh

Confirm with the user that the toasts arrived. If not, walk the troubleshooting table.

Step 11 — Activation note

Hook watcher only re-reads settings.json if hooks already existed when the session started. If you just added a new event (e.g. Stop wasn't there before), tell the user to open /hooks or restart Claude Code for it to take effect. Existing hooks reload automatically on file change.

Quick Reference — Event → Defaults

EventRecommended sound (macOS)Body text (en)Notes
StopBlow"Response ready"One per turn end
PermissionRequest (Bash/Edit/Write/etc)Hero"Request: $TOOL"Most common
PermissionRequest (AskUserQuestion/ExitPlanMode)Submarine"Claude is waiting"Interactive choice
Notification (idle)Glassfrom .messageOften redundant with Stop

Common Mistakes

  • Overwriting settings.json: always Read → Edit → merge. Other settings (model, plugins, statusLine) MUST be preserved.
  • Hardcoding /opt/homebrew/bin/terminal-notifier on Intel Macs: use which terminal-notifier at install time, hardcode the resolved path.
  • Forgetting -group: without -group claude-code each notification stacks separately. Pass it so a new toast replaces the previous one.
  • Trying to override -appIcon on macOS 13+ and reporting success: it's silently ignored. Tell the user explicitly that the large -contentImage is the one that takes effect.
  • Installing brew/apt packages without consent: ALWAYS ask via AskUserQuestion before brew install or sudo apt install.
  • Skipping the focus guard: gets old fast — Claude Code pings itself while the user is staring at it.
  • Using Bash with multi-line heredocs in hook commands: hook commands are single-line shell strings in JSON. Keep them one-line, escape " properly.
  • Forgetting Linux Wayland limitation: focus detection on Wayland (non-Hyprland/Sway) is unreliable. Document it and fall back to "always notify".

Troubleshooting

SymptomLikely causeFix
No toast appears at allHook not registered, or invalid JSON in settings.jsonjq -e .hooks ~/.claude/settings.json; check /hooks UI
Toast appears, no soundmacOS Focus mode / Do Not Disturb; system Notification sound disabled for terminal-notifierSystem Settings → Notifications → terminal-notifier → Allow Sounds
New hook added but doesn't fireWatcher didn't see new event typeOpen /hooks or restart Claude Code session
Linux notify-send works in shell but not from hookDBus session env missing in hook envAdd eval "$(dbus-launch --sh-syntax)" to wrapper, or set DBUS_SESSION_BUS_ADDRESS
Icon doesn't show on macOSTrying to set top-left app iconOnly -contentImage (right-side large) is reliable on macOS 13+
Toast for every keystroke / spamWired Notification and Stop and PermissionRequest with overlapping triggersDrop Notification — Stop covers turn-end, PermissionRequest covers tool prompts

Implementation Files

  • scripts/notify-macos.sh — focus-guarded terminal-notifier wrapper
  • scripts/notify-linux.sh — focus-guarded notify-send wrapper, sound via paplay/aplay
  • scripts/notify-windows.ps1 — focus-guarded New-BurntToastNotification wrapper

The wizard copies the right script to ~/.claude/bin/notify.<sh|ps1> and patches settings.json to call it. The scripts themselves are stable — do not rewrite them per-user.

Icon sourcing: the wizard offers to extract the icon from Claude.app if it's installed locally (it ships in the user's own copy of Anthropic's Claude Desktop). This repo does NOT bundle any icon — copyright stays with the user's local install.

What ships with it: 8 files

27.3 KB alongside SKILL.md, 4 of them executable

scripts/

tests/

Gives 0 of the 12 instructions most context ai engineering skills give in ~3.7k tokens

Counted across 1,193 of the 1,976 authors here whose files we hold, read 2026-08-07

  • Dispatch a fresh implementer subagent per taskin 48 of 1193, across 19 files
  • Dispatch a final code reviewer after all tasksin 33 of 1193, across 8 files
  • Provide full task text to the subagentin 30 of 1193, across 9 files
  • Review spec compliance before code qualityin 27 of 1193, across 10 files
  • Make the hook script executablein 26 of 1193, across 8 files
  • Re-snapshot after navigation or DOM changesin 25 of 1193, across 19 files
  • Read files before editing themin 22 of 1193, across 11 files
  • Answer subagent questions before proceedingin 22 of 1193, across 7 files
  • Mark task complete in TodoWrite after approvalin 22 of 1193, across 6 files
  • Merge hook into existing settingsin 21 of 1193, across 3 files
  • Ask if installation is global or projectin 20 of 1193, across 2 files
  • Copy the hook script to target locationin 20 of 1193, across 2 files

Said here and by no other author read

  • use AskUserQuestion for every user-facing choice
  • detect the host operating system
  • use Edit to modify settings
  • copy reference scripts instead of writing new ones
  • test every notification hook branch
  • tell the user to reload after adding new events

Grouped from the skills themselves: near-identical wordings counted once, and counted by distinct author, so one author publishing three of these counts once. Length counted with cl100k_base; the agent that loads this file may tokenize it differently.

Keep looking

Skills are one crate of 327,069. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.