From 0e8018ae7bdf4116b1b75418ec654bd9e0a10a2f Mon Sep 17 00:00:00 2001 From: Bastien Chanot Date: Thu, 3 Sep 2026 03:20:04 +0200 Subject: [PATCH] fix(hooks): no turn-end signal while background work runs Ending a turn right after spawning a subagent fired the bell and a toast saying the response was finished, while the work continued. The Stop payload carries background_tasks, so skip the signal when it is not empty; the next turn end signals once the work is really done. Interaction requests still signal during background work. A missing field still signals, so an older client loses nothing. Also drop the message suffix when it merely restates the label. --- hooks/notify-attention.sh | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/hooks/notify-attention.sh b/hooks/notify-attention.sh index 09fec73..a6113cc 100755 --- a/hooks/notify-attention.sh +++ b/hooks/notify-attention.sh @@ -24,11 +24,22 @@ read_field() { | tr -d '\000-\037' | cut -c1-160 } +# How many background tasks are still running as the hook fires. +background_count() { + count=$(printf '%s' "$payload" | jq -r '(.background_tasks // []) | length' 2>/dev/null) + case "$count" in ''|*[!0-9]*) echo 0 ;; *) echo "$count" ;; esac +} + event=$(read_field '.notification_type') [ -n "$event" ] || event=$(read_field '.hook_event_name') + + case "$event" in - Stop) label="Finished responding" ;; + # Turn end while a subagent still runs is not the real end: stay silent, + # the next turn end will signal once the work is actually done. + Stop) [ "$(background_count)" -eq 0 ] || exit 0 + label="Finished responding" ;; permission_prompt) label="Needs your permission" ;; agent_needs_input) label="Asks you a question" ;; idle_prompt) label="Waiting for you" ;; @@ -39,7 +50,15 @@ case "$event" in esac detail=$(read_field '.message') -[ -z "$detail" ] || label="${label}: ${detail}" +if [ -n "$detail" ]; then + # Claude Code's own wording often restates the label ("Claude needs your + # permission"). Append it only when it actually adds something. + short=$(printf '%s' "$detail" | tr '[:upper:]' '[:lower:]' | sed 's/^claude //') + case "$(printf '%s' "$label" | tr '[:upper:]' '[:lower:]')" in + *"$short"*) : ;; + *) label="${label}: ${detail}" ;; + esac +fi bell=$(printf '\a') esc=$(printf '\033')