Summary
Notification hooks configured with matcher permission_prompt|idle_prompt in ~/.claude/settings.json never fire in the Claude Desktop app, even though the same hook mechanism works correctly for other events (e.g. Stop) in the same app, and manually invoking the configured hook command directly reproduces the intended behavior without issue.
Setup
"hooks": {
"Notification": [
{
"matcher": "permission_prompt|idle_prompt",
"hooks": [
{ "type": "command", "command": "osascript -e 'display notification ...'", "timeout": 1 },
{ "type": "command", "command": "~/.claude/hooks/notify-say.sh", "timeout": 2, "async": true }
]
}
]
}
notify-say.sh just runs say "Claude needs your input" & in the background — no stdin parsing, so payload shape isn't the issue.
Steps to reproduce
- Add the above
Notification hook to ~/.claude/settings.json.
- Use Claude Desktop (not the CLI terminal) and trigger a real permission-approval dialog (a tool call requiring approval) or leave a session idle waiting on input for an extended period.
- Observe: the visual permission dialog / idle state appears normally, but the hook command never executes — no sound, no visible side effect.
What I verified to isolate the cause
- Manually piping a synthetic payload directly to the hook script (
echo '{...}' | ~/.claude/hooks/notify-say.sh) works every time — confirms the script itself, say, and audio output are all fine.
- The same settings file's
Stop hook (used for a separate text-to-speech feature reading Claude's final response) does fire reliably in this same Desktop app session — confirms hooks in general are supported and ~/.claude/settings.json is being read.
- Ruled out a sandboxed/VM execution context for Desktop sessions (found
claude-code-vm under Application Support, but a diagnostic command run inside an affected chat executed directly against the real filesystem and produced audible output, same as a normal terminal session) — so this isn't a lost-in-a-sandbox issue.
- Checked
~/Library/Logs/Claude/main.log for any trace of the hook being invoked (successfully or with error) — found no reference to the hook script by name, but also found no reference to the known-working Stop hook script either, so the log isn't granular enough to be conclusive either way.
/hooks slash command is not available in the Desktop app UI (/hooks isn't available in this environment), so there's no in-app way to confirm hook registration or force a reload.
- Tested
idle_prompt specifically by leaving a Desktop chat idle for extended periods (well beyond any reasonable idle threshold) — never fired.
Expected behavior
Notification hooks with matcher permission_prompt and idle_prompt should fire in Claude Desktop the same way they do in the CLI terminal, since Desktop shows equivalent visual permission-approval dialogs and idle states.
Environment
- Claude Desktop app on macOS
- Hooks otherwise functional in the same settings file (
Stop hook confirmed working)
Summary
Notificationhooks configured with matcherpermission_prompt|idle_promptin~/.claude/settings.jsonnever fire in the Claude Desktop app, even though the same hook mechanism works correctly for other events (e.g.Stop) in the same app, and manually invoking the configured hook command directly reproduces the intended behavior without issue.Setup
notify-say.shjust runssay "Claude needs your input" &in the background — no stdin parsing, so payload shape isn't the issue.Steps to reproduce
Notificationhook to~/.claude/settings.json.What I verified to isolate the cause
echo '{...}' | ~/.claude/hooks/notify-say.sh) works every time — confirms the script itself,say, and audio output are all fine.Stophook (used for a separate text-to-speech feature reading Claude's final response) does fire reliably in this same Desktop app session — confirms hooks in general are supported and~/.claude/settings.jsonis being read.claude-code-vmunder Application Support, but a diagnostic command run inside an affected chat executed directly against the real filesystem and produced audible output, same as a normal terminal session) — so this isn't a lost-in-a-sandbox issue.~/Library/Logs/Claude/main.logfor any trace of the hook being invoked (successfully or with error) — found no reference to the hook script by name, but also found no reference to the known-workingStophook script either, so the log isn't granular enough to be conclusive either way./hooksslash command is not available in the Desktop app UI (/hooks isn't available in this environment), so there's no in-app way to confirm hook registration or force a reload.idle_promptspecifically by leaving a Desktop chat idle for extended periods (well beyond any reasonable idle threshold) — never fired.Expected behavior
Notificationhooks with matcherpermission_promptandidle_promptshould fire in Claude Desktop the same way they do in the CLI terminal, since Desktop shows equivalent visual permission-approval dialogs and idle states.Environment
Stophook confirmed working)