feat(hooks): ring terminal on turn end via Stop hook

Notification matcher covers input-needed events only; end of turn had no
signal but idle_prompt, ~60s late. Wire notify-attention.sh on Stop too,
branching on hook_event_name for the message. Signal only: returns
terminalSequence + suppressOutput, never blocks (guard vs BDR-083).

Header documents both client-side prerequisites found in BLK-020.
This commit is contained in:
Bastien Chanot
2026-09-03 00:28:40 +02:00
parent de34e3f167
commit ea9e5c1dab
2 changed files with 33 additions and 7 deletions
+21 -7
View File
@@ -1,21 +1,35 @@
#!/usr/bin/env bash
# Notification hook — signal the user through the terminal when Claude
# needs input (permission prompt, question, idle wait).
# Notification + Stop hook — signal the user through the terminal when
# Claude needs input (permission prompt, question, idle wait) or has
# finished responding.
#
# Runs on the remote (Linux); the only channel that crosses SSH into the
# VS Code client is the terminal stream. Hooks have no controlling TTY,
# so the sequence goes through the supported `terminalSequence` JSON
# output field and Claude Code writes it to the terminal:
# - BEL x2 (double beep) -> sound, needs VS Code setting
# accessibility.signals.terminalBell { "sound": "on" }
# accessibility.signals.terminalBell { "sound": "on" } AND a non-zero
# volume for Code in the Windows volume mixer (BLK-020).
# - OSC 777 notify -> Windows toast via the client-side extension
# "Terminal Notification" (wenbopan.vscode-terminal-osc-notifier)
# "Terminal Notification" (wenbopan.vscode-terminal-osc-notifier).
# The extension only instruments terminals created AFTER it is
# active: install it first, then start or re-attach the session.
# Both are invisible no-ops in terminals that ignore them.
set -u
msg=$(jq -r '.message // .notification_type // empty' 2>/dev/null \
| tr -d '\000-\037' | cut -c1-200)
[ -n "$msg" ] || msg="Claude Code needs your input"
payload=$(cat 2>/dev/null)
read_field() {
printf '%s' "$payload" | jq -r "$1 // empty" 2>/dev/null \
| tr -d '\000-\037' | cut -c1-200
}
msg=$(read_field '.message // .notification_type')
if [ -z "$msg" ]; then
case "$(read_field '.hook_event_name')" in
Stop) msg="Claude has finished responding" ;;
*) msg="Claude Code needs your input" ;;
esac
fi
bell=$(printf '\a')
esc=$(printf '\033')
+12
View File
@@ -286,6 +286,18 @@
]
}
],
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "bash ~/.claude/hooks/notify-attention.sh",
"timeout": 5,
"statusMessage": "Ringing terminal bell..."
}
]
}
],
"UserPromptSubmit": [
{
"hooks": [