Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
067b977
extend warp claude plugin to include more detailed hooks and structur…
harryalbert Mar 19, 2026
daf3622
add test file for hooks
harryalbert Mar 19, 2026
0e9636f
centralize hook structure
harryalbert Mar 19, 2026
ce6db78
add warp terminal guard and start notificatoin
harryalbert Mar 19, 2026
84e8e1a
add prompt submit and post-tool-use hooks
harryalbert Mar 19, 2026
616bb70
fix if then else
harryalbert Mar 19, 2026
9b1f308
add test action
harryalbert Mar 19, 2026
71c64d1
Merge pull request #1 from warpdotdev/harry/extend-warp-claude-plugin
harryalbert Mar 20, 2026
304b9b8
show message when jq is not installed
harryalbert Mar 20, 2026
5369f3e
make language shell-agnostic
harryalbert Mar 20, 2026
ecf7c7a
Merge pull request #2 from warpdotdev/harry/app-3555-ensure-that-clau…
harryalbert Mar 23, 2026
e29c64c
have claude read max accepted client version and use it when sending …
harryalbert Mar 20, 2026
f0ac3a1
rename max to current
harryalbert Mar 23, 2026
8060b34
Merge pull request #3 from warpdotdev/harry/app-3556-add-two-way-hand…
harryalbert Mar 23, 2026
55c1d9e
auto-discover hooks
harryalbert Mar 24, 2026
abcb3b2
Merge pull request #4 from warpdotdev/harry/fix-hook-detection
harryalbert Mar 24, 2026
e4364ba
trust protocol version declaration instead of term program to get plu…
harryalbert Mar 26, 2026
bc80ff4
Merge pull request #5 from warpdotdev/harry/app-3620-get-claude-code-…
harryalbert Mar 26, 2026
67e1e6f
Make plugin work for legacy users as it did before (#6)
harryalbert Mar 27, 2026
eef4c5c
update README (#7)
harryalbert Mar 27, 2026
60de9e2
add client version check to fix bad client version
harryalbert Mar 30, 2026
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": "1.1.0",
"version": "2.0.0",
"category": "productivity",
"tags": ["notifications", "terminal", "warp"]
}
Expand Down
22 changes: 22 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
name: Plugin Tests
on:
pull_request:
push:
branches: [main]

jobs:
plugin-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run tests
# Auto-discovers and runs all test-*.sh scripts under any tests/ directory.
# To add a new test, just drop a test-*.sh file in a tests/ folder.
run: |
shopt -s globstar nullglob
failed=0
for f in **/tests/test-*.sh; do
echo "--- $f ---"
bash "$f" || failed=1
done
exit $failed
52 changes: 28 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,18 @@ Official [Warp](https://warp.dev) terminal integration for [Claude Code](https:/

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

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.

**Example notification:**
```
"what's 1+1" → 2
```
### 📡 Session Status

The plugin keeps Warp informed of Claude's current state by emitting structured events on every session transition:
- **Prompt submitted** — you sent a prompt, Claude is working
- **Tool completed** — a tool call finished, Claude is back to running

This powers Warp's inline status indicators for Claude Code sessions.

## Installation

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

> ⚠️ **Important**: After installing, **restart Claude Code** for notifications to activate.
> ⚠️ **Important**: After installing, **restart Claude Code or run /reload-plugins** for the plugin to activate.

Once restarted, you'll see a confirmation message and notifications will appear automatically when Claude completes tasks.
Once restarted, you'll see a confirmation message and notifications will appear automatically.

## Requirements

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

## How It Works

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:
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.

1. Reads the session transcript to extract your original prompt and Claude's response
2. Formats a concise notification message
3. Sends an OSC 777 escape sequence to Warp, which displays a native notification
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.

The plugin registers three hooks:
- **SessionStart** — shows a welcome message confirming the plugin is active
- **Stop** — fires when Claude finishes responding
- **Notification** — fires when Claude needs user input
The plugin registers six hooks:
- **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
- **Notification** (`idle_prompt`) — fires when Claude has been idle and needs your 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
- **PostToolUse** — fires when a tool call completes, signaling the session is no longer blocked

## Configuration
### Legacy Support

Older Warp clients that predate the structured notification protocol are still supported — they receive plain-text notifications for SessionStart, Stop, and Notification hooks.

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).

## Roadmap
## Configuration

Future Warp integrations planned:
- Warp AI context sharing
- Warp Drive integration for sharing Claude Code configurations
- Custom slash commands
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).

## Uninstall

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

## Contributing
## Versioning

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

## License

Expand Down
5 changes: 3 additions & 2 deletions plugins/warp/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
{
"name": "warp",
"description": "Warp terminal integration for Claude Code - native notifications, and more to come",
"version": "1.1.0",
"version": "2.0.0",
"author": {
"name": "Warp",
"url": "https://warp.dev"
},
"homepage": "https://github.com/warpdotdev/claude-code-warp"
"homepage": "https://github.com/warpdotdev/claude-code-warp",
"strict": false
}
32 changes: 31 additions & 1 deletion plugins/warp/hooks/hooks.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,44 @@
],
"Notification": [
{
"matcher": "*",
"matcher": "idle_prompt",
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/on-notification.sh"
}
]
}
],
"PermissionRequest": [
{
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/on-permission-request.sh"
}
]
}
],
"UserPromptSubmit": [
{
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/on-prompt-submit.sh"
}
]
}
],
"PostToolUse": [
{
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/on-post-tool-use.sh"
}
]
}
]
}
}
58 changes: 58 additions & 0 deletions plugins/warp/scripts/build-payload.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
#!/bin/bash
# Builds a structured JSON notification payload for warp://cli-agent.
#
# Usage: source this file, then call build_payload with event-specific fields.
#
# Example:
# source "$(dirname "${BASH_SOURCE[0]}")/build-payload.sh"
# BODY=$(build_payload "$INPUT" "stop" \
# --arg query "$QUERY" \
# --arg response "$RESPONSE" \
# --arg transcript_path "$TRANSCRIPT_PATH")
#
# The function extracts common fields (session_id, cwd, project) from the
# hook's stdin JSON (passed as $1), then merges any extra jq args you pass.

# The current protocol version this plugin knows how to produce.
PLUGIN_CURRENT_PROTOCOL_VERSION=1

# Negotiate the protocol version with Warp.
# Uses min(plugin_current, warp_declared), falling back to 1 if Warp doesn't advertise a version.
negotiate_protocol_version() {
local warp_version="${WARP_CLI_AGENT_PROTOCOL_VERSION:-1}"
if [ "$warp_version" -lt "$PLUGIN_CURRENT_PROTOCOL_VERSION" ] 2>/dev/null; then
echo "$warp_version"
else
echo "$PLUGIN_CURRENT_PROTOCOL_VERSION"
fi
}

build_payload() {
local input="$1"
local event="$2"
shift 2

local protocol_version
protocol_version=$(negotiate_protocol_version)

# Extract common fields from the hook input
local session_id cwd project
session_id=$(echo "$input" | jq -r '.session_id // empty' 2>/dev/null)
cwd=$(echo "$input" | jq -r '.cwd // empty' 2>/dev/null)
project=""
if [ -n "$cwd" ]; then
project=$(basename "$cwd")
fi

# Build the payload: common fields + any extra args passed by the caller.
# Extra args should be jq flag pairs like: --arg key "value" or --argjson key '{"a":1}'
jq -nc \
--argjson v "$protocol_version" \
--arg agent "claude" \
--arg event "$event" \
--arg session_id "$session_id" \
--arg cwd "$cwd" \
--arg project "$project" \
"$@" \
'{v:$v, agent:$agent, event:$event, session_id:$session_id, cwd:$cwd, project:$project} + $ARGS.named'
}
14 changes: 14 additions & 0 deletions plugins/warp/scripts/legacy/on-notification.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#!/bin/bash
# Hook script for Claude Code Notification event
# Sends a Warp notification when Claude needs user input

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

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

# Extract the notification message
MSG=$(echo "$INPUT" | jq -r '.message // "Input needed"' 2>/dev/null)
[ -z "$MSG" ] && MSG="Input needed"

"$SCRIPT_DIR/warp-notify.sh" "Claude Code" "$MSG"
20 changes: 20 additions & 0 deletions plugins/warp/scripts/legacy/on-session-start.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#!/bin/bash
# Hook script for Claude Code SessionStart event
# Shows welcome message and Warp detection status

# Check if running in Warp terminal
if [ "$TERM_PROGRAM" = "WarpTerminal" ]; then
# Running in Warp - notifications will work
cat << 'EOF'
{
"systemMessage": "🔔 Warp plugin active. You'll receive native Warp notifications when tasks complete or input is needed."
}
EOF
else
# Not running in Warp - suggest installing
cat << 'EOF'
{
"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."
}
EOF
fi
48 changes: 48 additions & 0 deletions plugins/warp/scripts/legacy/on-stop.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#!/bin/bash
# Hook script for Claude Code Stop event
# Sends a Warp notification when Claude completes a task

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

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

# Extract transcript path from the hook input
TRANSCRIPT_PATH=$(echo "$INPUT" | jq -r '.transcript_path // empty' 2>/dev/null)

# Default message
MSG="Task completed"

# Try to extract prompt and response from the transcript (JSONL format)
if [ -n "$TRANSCRIPT_PATH" ] && [ -f "$TRANSCRIPT_PATH" ]; then
# Get the first user prompt
PROMPT=$(jq -rs '
[.[] | select(.type == "user")] | first | .message.content // empty
' "$TRANSCRIPT_PATH" 2>/dev/null)

# Get the last assistant response
RESPONSE=$(jq -rs '
[.[] | select(.type == "assistant" and .message.content)] | last |
[.message.content[] | select(.type == "text") | .text] | join(" ")
' "$TRANSCRIPT_PATH" 2>/dev/null)

if [ -n "$PROMPT" ] && [ -n "$RESPONSE" ]; then
# Truncate prompt to 50 chars
if [ ${#PROMPT} -gt 50 ]; then
PROMPT="${PROMPT:0:47}..."
fi
# Truncate response to 120 chars
if [ ${#RESPONSE} -gt 120 ]; then
RESPONSE="${RESPONSE:0:117}..."
fi
MSG="\"${PROMPT}\" → ${RESPONSE}"
elif [ -n "$RESPONSE" ]; then
# Fallback to just response if no prompt found
if [ ${#RESPONSE} -gt 175 ]; then
RESPONSE="${RESPONSE:0:172}..."
fi
MSG="$RESPONSE"
fi
fi

"$SCRIPT_DIR/warp-notify.sh" "Claude Code" "$MSG"
10 changes: 10 additions & 0 deletions plugins/warp/scripts/legacy/warp-notify.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#!/bin/bash
# Warp notification utility using OSC escape sequences
# Usage: warp-notify.sh <title> <body>

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

# OSC 777 format: \033]777;notify;<title>;<body>\007
# Write directly to /dev/tty to ensure it reaches the terminal
printf '\033]777;notify;%s;%s\007' "$TITLE" "$BODY" > /dev/tty 2>/dev/null || true
21 changes: 17 additions & 4 deletions plugins/warp/scripts/on-notification.sh
Original file line number Diff line number Diff line change
@@ -1,14 +1,27 @@
#!/bin/bash
# Hook script for Claude Code Notification event
# Sends a Warp notification when Claude needs user input
# Hook script for Claude Code Notification event (idle_prompt only)
# Sends a structured Warp notification when Claude has been idle

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/should-use-structured.sh"

# Legacy fallback for old Warp versions
if ! should_use_structured; then
[ "$TERM_PROGRAM" = "WarpTerminal" ] && exec "$SCRIPT_DIR/legacy/on-notification.sh"
exit 0
fi

source "$SCRIPT_DIR/build-payload.sh"

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

# Extract the notification message
# Extract notification-specific fields
NOTIF_TYPE=$(echo "$INPUT" | jq -r '.notification_type // "unknown"' 2>/dev/null)
MSG=$(echo "$INPUT" | jq -r '.message // "Input needed"' 2>/dev/null)
[ -z "$MSG" ] && MSG="Input needed"

"$SCRIPT_DIR/warp-notify.sh" "Claude Code" "$MSG"
BODY=$(build_payload "$INPUT" "$NOTIF_TYPE" \
--arg summary "$MSG")

"$SCRIPT_DIR/warp-notify.sh" "warp://cli-agent" "$BODY"
Loading
Loading