Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"name": "warp",
"description": "Native Warp notifications when Claude completes tasks or needs input",
"source": "./plugins/warp",
"version": "2.3.0",
"version": "2.4.0",
"category": "productivity",
"tags": ["notifications", "terminal", "warp"]
},
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ Payloads include a protocol version negotiated between the plugin and Warp (`min

The plugin registers lifecycle hooks including:
- **SessionStart** — emits the plugin version and a welcome system message
- **Stop** — reads the transcript to extract your prompt and Claude's response, then sends a task-complete notification
- **Stop** — reads the transcript to extract your prompt and Claude's response, then sends a task-complete notification that also reports how many background tasks and scheduled wakeups are still pending, so Warp can tell a finished session from one waiting on background work
- **Notification** (`idle_prompt`, `agent_needs_input`) — fires when Claude is waiting for user input
- **PermissionRequest** — fires when Claude wants to run a tool, includes the tool name and a preview of its input
- **UserPromptSubmit** — fires when you submit a prompt, signaling the session is active again
Expand Down
2 changes: 1 addition & 1 deletion plugins/warp/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "warp",
"description": "Warp terminal integration for Claude Code - native notifications, and more to come",
"version": "2.3.0",
"version": "2.4.0",
"author": {
"name": "Warp",
"url": "https://warp.dev"
Expand Down
10 changes: 9 additions & 1 deletion plugins/warp/scripts/on-stop.sh
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,17 @@ if [ -n "$TRANSCRIPT_PATH" ] && [ -f "$TRANSCRIPT_PATH" ]; then
fi
fi

# Claude Code wakes the agent again when a background task finishes or a session cron fires,
# so a Stop with either pending is a pause rather than the end of the session. Default to 0
# so --argjson always receives a number and the notification is never dropped.
BACKGROUND_TASK_COUNT=$(echo "$INPUT" | jq -r '.background_tasks // [] | length' 2>/dev/null)
SESSION_CRON_COUNT=$(echo "$INPUT" | jq -r '.session_crons // [] | length' 2>/dev/null)

BODY=$(build_payload "$INPUT" "stop" \
--arg query "$QUERY" \
--arg response "$RESPONSE" \
--arg transcript_path "$TRANSCRIPT_PATH")
--arg transcript_path "$TRANSCRIPT_PATH" \
--argjson background_task_count "${BACKGROUND_TASK_COUNT:-0}" \
--argjson session_cron_count "${SESSION_CRON_COUNT:-0}")

"$SCRIPT_DIR/warp-notify.sh" "warp://cli-agent" "$BODY"
13 changes: 12 additions & 1 deletion plugins/warp/tests/test-hooks.sh
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ MARKETPLACE_VERSION=$(jq -r '.plugins[] | select(.name == "warp") | .version' \
"$REPO_DIR/.claude-plugin/marketplace.json")
NOTIFICATION_MATCHER=$(jq -r '.hooks.Notification[0].matcher' "$PLUGIN_DIR/hooks/hooks.json")

assert_eq "plugin version bumped" "2.3.0" "$PLUGIN_VERSION"
assert_eq "plugin version bumped" "2.4.0" "$PLUGIN_VERSION"
assert_eq "marketplace version matches plugin" "$PLUGIN_VERSION" "$MARKETPLACE_VERSION"
assert_eq "notification matcher includes input-required events" \
"idle_prompt|agent_needs_input" \
Expand Down Expand Up @@ -102,6 +102,17 @@ assert_json_field "query present" "$PAYLOAD" ".query" "write a haiku"
assert_json_field "response present" "$PAYLOAD" ".response" "Memory is safe, the borrow checker stands guard"
assert_json_field "transcript_path present" "$PAYLOAD" ".transcript_path" "/tmp/transcript.jsonl"

echo ""
echo "--- Stop event pending background work ---"
STOP_INPUT='{"session_id":"s1","cwd":"/tmp/proj","background_tasks":[{"id":"t1","type":"shell","status":"running"},{"id":"t2","type":"subagent","status":"running"}],"session_crons":[{"id":"c1","schedule":"* * * * *"}]}'
PAYLOAD=$(build_payload "$STOP_INPUT" "stop" \
--argjson background_task_count "$(echo "$STOP_INPUT" | jq -r '.background_tasks // [] | length')" \
--argjson session_cron_count "$(echo "$STOP_INPUT" | jq -r '.session_crons // [] | length')")
assert_json_field "background_task_count is numeric" "$PAYLOAD" ".background_task_count | type" "number"
assert_json_field "background_task_count merged" "$PAYLOAD" ".background_task_count" "2"
assert_json_field "session_cron_count merged" "$PAYLOAD" ".session_cron_count" "1"
assert_eq "missing arrays count as 0" "0" "$(echo '{"session_id":"s1"}' | jq -r '.background_tasks // [] | length')"

echo ""
echo "--- Permission request event ---"
PAYLOAD=$(build_payload '{"session_id":"s1","cwd":"/tmp/proj"}' "permission_request" \
Expand Down
Loading