Skip to content

Commit 4367434

Browse files
authored
Merge pull request #9 from warpdotdev/claude-code-warp-internal
Merge changes from internal claude-code-warp repo
2 parents 26ef589 + 60de9e2 commit 4367434

19 files changed

Lines changed: 716 additions & 73 deletions

.claude-plugin/marketplace.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
"name": "warp",
1111
"description": "Native Warp notifications when Claude completes tasks or needs input",
1212
"source": "./plugins/warp",
13-
"version": "1.1.0",
13+
"version": "2.0.0",
1414
"category": "productivity",
1515
"tags": ["notifications", "terminal", "warp"]
1616
}

.github/workflows/test.yml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
name: Plugin Tests
2+
on:
3+
pull_request:
4+
push:
5+
branches: [main]
6+
7+
jobs:
8+
plugin-tests:
9+
runs-on: ubuntu-latest
10+
steps:
11+
- uses: actions/checkout@v4
12+
- name: Run tests
13+
# Auto-discovers and runs all test-*.sh scripts under any tests/ directory.
14+
# To add a new test, just drop a test-*.sh file in a tests/ folder.
15+
run: |
16+
shopt -s globstar nullglob
17+
failed=0
18+
for f in **/tests/test-*.sh; do
19+
echo "--- $f ---"
20+
bash "$f" || failed=1
21+
done
22+
exit $failed

README.md

Lines changed: 28 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,18 @@ Official [Warp](https://warp.dev) terminal integration for [Claude Code](https:/
88

99
Get native Warp notifications when Claude Code:
1010
- **Completes a task** — with a summary showing your prompt and Claude's response
11-
- **Needs your input** — when Claude requires approval or has a question
11+
- **Needs your input** — when Claude has been idle and is waiting for you
12+
- **Requests permission** — when Claude wants to run a tool and needs your approval
1213

1314
Notifications appear in Warp's notification center and as system notifications, so you can context-switch while Claude works and get alerted when attention is needed.
1415

15-
**Example notification:**
16-
```
17-
"what's 1+1" → 2
18-
```
16+
### 📡 Session Status
17+
18+
The plugin keeps Warp informed of Claude's current state by emitting structured events on every session transition:
19+
- **Prompt submitted** — you sent a prompt, Claude is working
20+
- **Tool completed** — a tool call finished, Claude is back to running
21+
22+
This powers Warp's inline status indicators for Claude Code sessions.
1923

2024
## Installation
2125

@@ -27,9 +31,9 @@ Notifications appear in Warp's notification center and as system notifications,
2731
/plugin install warp@claude-code-warp
2832
```
2933

30-
> ⚠️ **Important**: After installing, **restart Claude Code** for notifications to activate.
34+
> ⚠️ **Important**: After installing, **restart Claude Code or run /reload-plugins** for the plugin to activate.
3135
32-
Once restarted, you'll see a confirmation message and notifications will appear automatically when Claude completes tasks.
36+
Once restarted, you'll see a confirmation message and notifications will appear automatically.
3337

3438
## Requirements
3539

@@ -39,27 +43,26 @@ Once restarted, you'll see a confirmation message and notifications will appear
3943

4044
## How It Works
4145

42-
This plugin uses Warp's [pluggable notifications](https://docs.warp.dev/features/notifications) feature via OSC escape sequences. When Claude Code triggers a hook event, the plugin:
46+
The plugin communicates with Warp via OSC 777 escape sequences. Each hook script builds a structured JSON payload (via `build-payload.sh`) and sends it to `warp://cli-agent`, where Warp parses it to drive notifications and session UI.
4347

44-
1. Reads the session transcript to extract your original prompt and Claude's response
45-
2. Formats a concise notification message
46-
3. Sends an OSC 777 escape sequence to Warp, which displays a native notification
48+
Payloads include a protocol version negotiated between the plugin and Warp (`min(plugin_version, warp_version)`), the session ID, working directory, and event-specific fields.
4749

48-
The plugin registers three hooks:
49-
- **SessionStart** — shows a welcome message confirming the plugin is active
50-
- **Stop** — fires when Claude finishes responding
51-
- **Notification** — fires when Claude needs user input
50+
The plugin registers six hooks:
51+
- **SessionStart** — emits the plugin version and a welcome system message
52+
- **Stop** — reads the transcript to extract your prompt and Claude's response, then sends a task-complete notification
53+
- **Notification** (`idle_prompt`) — fires when Claude has been idle and needs your input
54+
- **PermissionRequest** — fires when Claude wants to run a tool, includes the tool name and a preview of its input
55+
- **UserPromptSubmit** — fires when you submit a prompt, signaling the session is active again
56+
- **PostToolUse** — fires when a tool call completes, signaling the session is no longer blocked
5257

53-
## Configuration
58+
### Legacy Support
59+
60+
Older Warp clients that predate the structured notification protocol are still supported — they receive plain-text notifications for SessionStart, Stop, and Notification hooks.
5461

55-
Notifications work out of the box. To customize Warp's notification behavior (sounds, system notifications, etc.), see [Warp's notification settings](https://docs.warp.dev/features/notifications).
5662

57-
## Roadmap
63+
## Configuration
5864

59-
Future Warp integrations planned:
60-
- Warp AI context sharing
61-
- Warp Drive integration for sharing Claude Code configurations
62-
- Custom slash commands
65+
Notifications work out of the box. To customize Warp's notification behavior (sounds, system notifications, etc.), see [Warp's notification settings](https://docs.warp.dev/features/notifications).
6366

6467
## Uninstall
6568

@@ -68,9 +71,10 @@ Future Warp integrations planned:
6871
/plugin marketplace remove claude-code-warp
6972
```
7073

71-
## Contributing
74+
## Versioning
7275

73-
Contributions welcome! Please open an issue or PR on [GitHub](https://github.com/warpdotdev/claude-code-warp).
76+
The plugin version in `plugins/warp/.claude-plugin/plugin.json` is checked by the Warp client to detect outdated installations.
77+
When bumping the version here, also update `MINIMUM_PLUGIN_VERSION` in the Warp client.
7478

7579
## License
7680

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
{
22
"name": "warp",
33
"description": "Warp terminal integration for Claude Code - native notifications, and more to come",
4-
"version": "1.1.0",
4+
"version": "2.0.0",
55
"author": {
66
"name": "Warp",
77
"url": "https://warp.dev"
88
},
9-
"homepage": "https://github.com/warpdotdev/claude-code-warp"
9+
"homepage": "https://github.com/warpdotdev/claude-code-warp",
10+
"strict": false
1011
}

plugins/warp/hooks/hooks.json

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,44 @@
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+
}
45+
],
46+
"UserPromptSubmit": [
47+
{
48+
"hooks": [
49+
{
50+
"type": "command",
51+
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/on-prompt-submit.sh"
52+
}
53+
]
54+
}
55+
],
56+
"PostToolUse": [
57+
{
58+
"hooks": [
59+
{
60+
"type": "command",
61+
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/on-post-tool-use.sh"
62+
}
63+
]
64+
}
3565
]
3666
}
3767
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
#!/bin/bash
2+
# Builds a structured JSON notification payload for warp://cli-agent.
3+
#
4+
# Usage: source this file, then call build_payload with event-specific fields.
5+
#
6+
# Example:
7+
# source "$(dirname "${BASH_SOURCE[0]}")/build-payload.sh"
8+
# BODY=$(build_payload "$INPUT" "stop" \
9+
# --arg query "$QUERY" \
10+
# --arg response "$RESPONSE" \
11+
# --arg transcript_path "$TRANSCRIPT_PATH")
12+
#
13+
# The function extracts common fields (session_id, cwd, project) from the
14+
# hook's stdin JSON (passed as $1), then merges any extra jq args you pass.
15+
16+
# The current protocol version this plugin knows how to produce.
17+
PLUGIN_CURRENT_PROTOCOL_VERSION=1
18+
19+
# Negotiate the protocol version with Warp.
20+
# Uses min(plugin_current, warp_declared), falling back to 1 if Warp doesn't advertise a version.
21+
negotiate_protocol_version() {
22+
local warp_version="${WARP_CLI_AGENT_PROTOCOL_VERSION:-1}"
23+
if [ "$warp_version" -lt "$PLUGIN_CURRENT_PROTOCOL_VERSION" ] 2>/dev/null; then
24+
echo "$warp_version"
25+
else
26+
echo "$PLUGIN_CURRENT_PROTOCOL_VERSION"
27+
fi
28+
}
29+
30+
build_payload() {
31+
local input="$1"
32+
local event="$2"
33+
shift 2
34+
35+
local protocol_version
36+
protocol_version=$(negotiate_protocol_version)
37+
38+
# Extract common fields from the hook input
39+
local session_id cwd project
40+
session_id=$(echo "$input" | jq -r '.session_id // empty' 2>/dev/null)
41+
cwd=$(echo "$input" | jq -r '.cwd // empty' 2>/dev/null)
42+
project=""
43+
if [ -n "$cwd" ]; then
44+
project=$(basename "$cwd")
45+
fi
46+
47+
# Build the payload: common fields + any extra args passed by the caller.
48+
# Extra args should be jq flag pairs like: --arg key "value" or --argjson key '{"a":1}'
49+
jq -nc \
50+
--argjson v "$protocol_version" \
51+
--arg agent "claude" \
52+
--arg event "$event" \
53+
--arg session_id "$session_id" \
54+
--arg cwd "$cwd" \
55+
--arg project "$project" \
56+
"$@" \
57+
'{v:$v, agent:$agent, event:$event, session_id:$session_id, cwd:$cwd, project:$project} + $ARGS.named'
58+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#!/bin/bash
2+
# Hook script for Claude Code Notification event
3+
# Sends a Warp notification when Claude needs user input
4+
5+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
6+
7+
# Read hook input from stdin
8+
INPUT=$(cat)
9+
10+
# Extract the notification message
11+
MSG=$(echo "$INPUT" | jq -r '.message // "Input needed"' 2>/dev/null)
12+
[ -z "$MSG" ] && MSG="Input needed"
13+
14+
"$SCRIPT_DIR/warp-notify.sh" "Claude Code" "$MSG"
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#!/bin/bash
2+
# Hook script for Claude Code SessionStart event
3+
# Shows welcome message and Warp detection status
4+
5+
# Check if running in Warp terminal
6+
if [ "$TERM_PROGRAM" = "WarpTerminal" ]; then
7+
# Running in Warp - notifications will work
8+
cat << 'EOF'
9+
{
10+
"systemMessage": "🔔 Warp plugin active. You'll receive native Warp notifications when tasks complete or input is needed."
11+
}
12+
EOF
13+
else
14+
# Not running in Warp - suggest installing
15+
cat << 'EOF'
16+
{
17+
"systemMessage": "ℹ️ Warp plugin installed but you're not running in Warp terminal. Install Warp (https://warp.dev) to get native notifications when Claude completes tasks or needs input."
18+
}
19+
EOF
20+
fi
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
#!/bin/bash
2+
# Hook script for Claude Code Stop event
3+
# Sends a Warp notification when Claude completes a task
4+
5+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
6+
7+
# Read hook input from stdin
8+
INPUT=$(cat)
9+
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"
15+
16+
# Try to extract prompt and response from the transcript (JSONL format)
17+
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
21+
' "$TRANSCRIPT_PATH" 2>/dev/null)
22+
23+
# Get the last assistant response
24+
RESPONSE=$(jq -rs '
25+
[.[] | select(.type == "assistant" and .message.content)] | last |
26+
[.message.content[] | select(.type == "text") | .text] | join(" ")
27+
' "$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"
45+
fi
46+
fi
47+
48+
"$SCRIPT_DIR/warp-notify.sh" "Claude Code" "$MSG"
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#!/bin/bash
2+
# Warp notification utility using OSC escape sequences
3+
# Usage: warp-notify.sh <title> <body>
4+
5+
TITLE="${1:-Notification}"
6+
BODY="${2:-}"
7+
8+
# OSC 777 format: \033]777;notify;<title>;<body>\007
9+
# Write directly to /dev/tty to ensure it reaches the terminal
10+
printf '\033]777;notify;%s;%s\007' "$TITLE" "$BODY" > /dev/tty 2>/dev/null || true

0 commit comments

Comments
 (0)