|
1 | 1 | #!/bin/bash |
2 | 2 | # Hook script for Claude Code Stop event |
3 | | -# Sends a Warp notification when Claude completes a task |
| 3 | +# Sends a structured Warp notification when Claude completes a task |
4 | 4 |
|
5 | 5 | SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" |
| 6 | +source "$SCRIPT_DIR/build-payload.sh" |
6 | 7 |
|
7 | 8 | # Read hook input from stdin |
8 | 9 | INPUT=$(cat) |
9 | 10 |
|
10 | | -# Extract transcript path from the hook input |
11 | | -TRANSCRIPT_PATH=$(echo "$INPUT" | jq -r '.transcript_path // empty' 2>/dev/null) |
12 | | - |
13 | | -# Default message |
14 | | -MSG="Task completed" |
| 11 | +# Skip if a stop hook is already active (prevents double-notification) |
| 12 | +STOP_HOOK_ACTIVE=$(echo "$INPUT" | jq -r '.stop_hook_active // false' 2>/dev/null) |
| 13 | +if [ "$STOP_HOOK_ACTIVE" = "true" ]; then |
| 14 | + exit 0 |
| 15 | +fi |
15 | 16 |
|
16 | | -# Try to extract prompt and response from the transcript (JSONL format) |
| 17 | +# Extract the last user prompt and assistant response from the transcript. |
| 18 | +# Small delay to allow Claude Code to flush the current turn to the transcript file. |
| 19 | +# The Stop hook fires before the transcript is fully written. |
| 20 | +TRANSCRIPT_PATH=$(echo "$INPUT" | jq -r '.transcript_path // empty' 2>/dev/null) |
| 21 | +sleep 0.3 |
| 22 | +QUERY="" |
| 23 | +RESPONSE="" |
17 | 24 | if [ -n "$TRANSCRIPT_PATH" ] && [ -f "$TRANSCRIPT_PATH" ]; then |
18 | | - # Get the first user prompt |
19 | | - PROMPT=$(jq -rs ' |
20 | | - [.[] | select(.type == "user")] | first | .message.content // empty |
| 25 | + # Get the last human prompt from the transcript. |
| 26 | + # "user" type messages include both human prompts and tool-result messages. |
| 27 | + # Human prompts have content that is either a plain string or an array |
| 28 | + # containing {type:"text"} blocks. Tool-result messages have content arrays |
| 29 | + # containing only {type:"tool_result"} blocks. We filter to messages that |
| 30 | + # have at least one "text" block (or are a plain string). |
| 31 | + QUERY=$(jq -rs ' |
| 32 | + [ |
| 33 | + .[] | select(.type == "user") | |
| 34 | + if .message.content | type == "string" then . |
| 35 | + elif [.message.content[] | select(.type == "text")] | length > 0 then . |
| 36 | + else empty |
| 37 | + end |
| 38 | + ] | last | |
| 39 | + if .message.content | type == "array" |
| 40 | + then [.message.content[] | select(.type == "text") | .text] | join(" ") |
| 41 | + else .message.content // empty |
| 42 | + end |
21 | 43 | ' "$TRANSCRIPT_PATH" 2>/dev/null) |
22 | | - |
| 44 | + |
23 | 45 | # Get the last assistant response |
24 | 46 | RESPONSE=$(jq -rs ' |
25 | 47 | [.[] | select(.type == "assistant" and .message.content)] | last | |
26 | 48 | [.message.content[] | select(.type == "text") | .text] | join(" ") |
27 | 49 | ' "$TRANSCRIPT_PATH" 2>/dev/null) |
28 | | - |
29 | | - if [ -n "$PROMPT" ] && [ -n "$RESPONSE" ]; then |
30 | | - # Truncate prompt to 50 chars |
31 | | - if [ ${#PROMPT} -gt 50 ]; then |
32 | | - PROMPT="${PROMPT:0:47}..." |
33 | | - fi |
34 | | - # Truncate response to 120 chars |
35 | | - if [ ${#RESPONSE} -gt 120 ]; then |
36 | | - RESPONSE="${RESPONSE:0:117}..." |
37 | | - fi |
38 | | - MSG="\"${PROMPT}\" → ${RESPONSE}" |
39 | | - elif [ -n "$RESPONSE" ]; then |
40 | | - # Fallback to just response if no prompt found |
41 | | - if [ ${#RESPONSE} -gt 175 ]; then |
42 | | - RESPONSE="${RESPONSE:0:172}..." |
43 | | - fi |
44 | | - MSG="$RESPONSE" |
| 50 | + |
| 51 | + # Truncate for notification display |
| 52 | + if [ -n "$QUERY" ] && [ ${#QUERY} -gt 200 ]; then |
| 53 | + QUERY="${QUERY:0:197}..." |
| 54 | + fi |
| 55 | + if [ -n "$RESPONSE" ] && [ ${#RESPONSE} -gt 200 ]; then |
| 56 | + RESPONSE="${RESPONSE:0:197}..." |
45 | 57 | fi |
46 | 58 | fi |
47 | 59 |
|
48 | | -"$SCRIPT_DIR/warp-notify.sh" "Claude Code" "$MSG" |
| 60 | +BODY=$(build_payload "$INPUT" "stop" \ |
| 61 | + --arg query "$QUERY" \ |
| 62 | + --arg response "$RESPONSE" \ |
| 63 | + --arg transcript_path "$TRANSCRIPT_PATH") |
| 64 | + |
| 65 | +"$SCRIPT_DIR/warp-notify.sh" "warp://cli-agent" "$BODY" |
0 commit comments