Skip to content

Commit 067b977

Browse files
committed
extend warp claude plugin to include more detailed hooks and structured responses
1 parent 26ef589 commit 067b977

5 files changed

Lines changed: 129 additions & 33 deletions

File tree

plugins/warp/hooks/hooks.json

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,24 @@
2424
],
2525
"Notification": [
2626
{
27-
"matcher": "*",
27+
"matcher": "idle_prompt",
2828
"hooks": [
2929
{
3030
"type": "command",
3131
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/on-notification.sh"
3232
}
3333
]
3434
}
35+
],
36+
"PermissionRequest": [
37+
{
38+
"hooks": [
39+
{
40+
"type": "command",
41+
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/on-permission-request.sh"
42+
}
43+
]
44+
}
3545
]
3646
}
3747
}
Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,31 @@
11
#!/bin/bash
2-
# Hook script for Claude Code Notification event
3-
# Sends a Warp notification when Claude needs user input
2+
# Hook script for Claude Code Notification event (idle_prompt only)
3+
# Sends a structured Warp notification when Claude has been idle
44

55
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
66

77
# Read hook input from stdin
88
INPUT=$(cat)
99

10-
# Extract the notification message
10+
# Extract metadata from the hook input
11+
NOTIF_TYPE=$(echo "$INPUT" | jq -r '.notification_type // "unknown"' 2>/dev/null)
12+
SESSION_ID=$(echo "$INPUT" | jq -r '.session_id // empty' 2>/dev/null)
1113
MSG=$(echo "$INPUT" | jq -r '.message // "Input needed"' 2>/dev/null)
1214
[ -z "$MSG" ] && MSG="Input needed"
15+
CWD=$(echo "$INPUT" | jq -r '.cwd // empty' 2>/dev/null)
16+
PROJECT=""
17+
if [ -n "$CWD" ]; then
18+
PROJECT=$(basename "$CWD")
19+
fi
1320

14-
"$SCRIPT_DIR/warp-notify.sh" "Claude Code" "$MSG"
21+
# Build structured JSON payload
22+
BODY=$(jq -nc \
23+
--arg agent "claude" \
24+
--arg event "$NOTIF_TYPE" \
25+
--arg session_id "$SESSION_ID" \
26+
--arg cwd "$CWD" \
27+
--arg project "$PROJECT" \
28+
--arg summary "$MSG" \
29+
'{v:1, agent:$agent, event:$event, session_id:$session_id, cwd:$cwd, project:$project, summary:$summary}')
30+
31+
"$SCRIPT_DIR/warp-notify.sh" "warp://cli-agent" "$BODY"
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
#!/bin/bash
2+
# Hook script for Claude Code PermissionRequest event
3+
# Sends a structured Warp notification when Claude needs permission to run a tool
4+
5+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
6+
7+
# Read hook input from stdin
8+
INPUT=$(cat)
9+
10+
# Extract metadata from the hook input
11+
SESSION_ID=$(echo "$INPUT" | jq -r '.session_id // empty' 2>/dev/null)
12+
TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // "unknown"' 2>/dev/null)
13+
TOOL_INPUT=$(echo "$INPUT" | jq -c '.tool_input // {}' 2>/dev/null)
14+
# Fallback to empty object if jq failed or returned empty
15+
[ -z "$TOOL_INPUT" ] && TOOL_INPUT='{}'
16+
CWD=$(echo "$INPUT" | jq -r '.cwd // empty' 2>/dev/null)
17+
PROJECT=""
18+
if [ -n "$CWD" ]; then
19+
PROJECT=$(basename "$CWD")
20+
fi
21+
22+
# Build a human-readable summary for the notification body
23+
TOOL_PREVIEW=$(echo "$INPUT" | jq -r '.tool_input | if .command then .command elif .file_path then .file_path else (tostring | .[0:80]) end // ""' 2>/dev/null)
24+
SUMMARY="Wants to run $TOOL_NAME"
25+
if [ -n "$TOOL_PREVIEW" ]; then
26+
if [ ${#TOOL_PREVIEW} -gt 120 ]; then
27+
TOOL_PREVIEW="${TOOL_PREVIEW:0:117}..."
28+
fi
29+
SUMMARY="$SUMMARY: $TOOL_PREVIEW"
30+
fi
31+
32+
# Build structured JSON payload
33+
# tool_input is passed as raw JSON (not a string) so Warp can inspect it directly
34+
BODY=$(jq -nc \
35+
--arg agent "claude" \
36+
--arg event "permission_request" \
37+
--arg session_id "$SESSION_ID" \
38+
--arg cwd "$CWD" \
39+
--arg project "$PROJECT" \
40+
--arg summary "$SUMMARY" \
41+
--arg tool_name "$TOOL_NAME" \
42+
--argjson tool_input "$TOOL_INPUT" \
43+
'{v:1, agent:$agent, event:$event, session_id:$session_id, cwd:$cwd, project:$project, summary:$summary, tool_name:$tool_name, tool_input:$tool_input}')
44+
45+
"$SCRIPT_DIR/warp-notify.sh" "warp://cli-agent" "$BODY"

plugins/warp/scripts/on-stop.sh

Lines changed: 49 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,69 @@
11
#!/bin/bash
22
# 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
44

55
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
66

77
# Read hook input from stdin
88
INPUT=$(cat)
99

10-
# Extract transcript path from the hook input
11-
TRANSCRIPT_PATH=$(echo "$INPUT" | jq -r '.transcript_path // empty' 2>/dev/null)
10+
# Skip if a stop hook is already active (prevents double-notification)
11+
STOP_HOOK_ACTIVE=$(echo "$INPUT" | jq -r '.stop_hook_active // false' 2>/dev/null)
12+
if [ "$STOP_HOOK_ACTIVE" = "true" ]; then
13+
exit 0
14+
fi
1215

13-
# Default message
14-
MSG="Task completed"
16+
# Extract metadata from the hook input
17+
SESSION_ID=$(echo "$INPUT" | jq -r '.session_id // empty' 2>/dev/null)
18+
TRANSCRIPT_PATH=$(echo "$INPUT" | jq -r '.transcript_path // empty' 2>/dev/null)
19+
CWD=$(echo "$INPUT" | jq -r '.cwd // empty' 2>/dev/null)
20+
PROJECT=""
21+
if [ -n "$CWD" ]; then
22+
PROJECT=$(basename "$CWD")
23+
fi
1524

16-
# Try to extract prompt and response from the transcript (JSONL format)
25+
# Extract the last user prompt and assistant response from the transcript.
26+
# Small delay to allow Claude Code to flush the current turn to the transcript file.
27+
# The Stop hook fires before the transcript is fully written.
28+
sleep 0.3
29+
QUERY=""
30+
RESPONSE=""
1731
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
32+
# Get the last user prompt (most recent turn, not the first in the session)
33+
# .message.content can be a string or an array of {type, text} objects
34+
QUERY=$(jq -rs '
35+
[.[] | select(.type == "user")] | last |
36+
if .message.content | type == "array"
37+
then [.message.content[] | select(.type == "text") | .text] | join(" ")
38+
else .message.content // empty
39+
end
2140
' "$TRANSCRIPT_PATH" 2>/dev/null)
22-
41+
2342
# Get the last assistant response
2443
RESPONSE=$(jq -rs '
2544
[.[] | select(.type == "assistant" and .message.content)] | last |
2645
[.message.content[] | select(.type == "text") | .text] | join(" ")
2746
' "$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"
47+
48+
# Truncate for notification display
49+
if [ -n "$QUERY" ] && [ ${#QUERY} -gt 200 ]; then
50+
QUERY="${QUERY:0:197}..."
51+
fi
52+
if [ -n "$RESPONSE" ] && [ ${#RESPONSE} -gt 200 ]; then
53+
RESPONSE="${RESPONSE:0:197}..."
4554
fi
4655
fi
4756

48-
"$SCRIPT_DIR/warp-notify.sh" "Claude Code" "$MSG"
57+
# Build structured JSON payload
58+
BODY=$(jq -nc \
59+
--arg agent "claude" \
60+
--arg event "stop" \
61+
--arg session_id "$SESSION_ID" \
62+
--arg cwd "$CWD" \
63+
--arg project "$PROJECT" \
64+
--arg query "$QUERY" \
65+
--arg response "$RESPONSE" \
66+
--arg transcript_path "$TRANSCRIPT_PATH" \
67+
'{v:1, agent:$agent, event:$event, session_id:$session_id, cwd:$cwd, project:$project, query:$query, response:$response, transcript_path:$transcript_path}')
68+
69+
"$SCRIPT_DIR/warp-notify.sh" "warp://cli-agent" "$BODY"

plugins/warp/scripts/warp-notify.sh

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
#!/bin/bash
22
# Warp notification utility using OSC escape sequences
33
# Usage: warp-notify.sh <title> <body>
4+
#
5+
# For structured Warp notifications, title should be "warp://cli-agent"
6+
# and body should be a JSON string matching the cli-agent notification schema.
47

58
TITLE="${1:-Notification}"
69
BODY="${2:-}"

0 commit comments

Comments
 (0)