Warp terminal integration for Pi — real-time tab status, animated spinners, and rich session notifications.
When you run Pi inside Warp, this extension sends structured lifecycle events so Warp can display the agent's status directly in its sidebar tab:
| Status | Tab indicator | Meaning |
|---|---|---|
| InProgress | ● spinner | Agent is thinking or executing tools |
| Success | ✓ checkmark | Agent turn completed |
| Blocked | ! exclamation | Agent needs user input (future) |
It also updates the terminal title with an animated braille spinner while the agent is working, matching the behavior of Codex CLI and Claude Code.
# From npm (recommended)
pi install npm:@capyup/pi-warp
# Or from git
pi install git:github.com/capyup/pi-warpThat's it — the extension auto-activates when Pi detects a Warp terminal (TERM_PROGRAM=WarpTerminal). In non-Warp terminals it does nothing.
Modern terminals like Warp integrate with CLI agents through two complementary mechanisms:
Standard escape sequence (\x1b]0;title\x07) that updates the window/tab title. Pi-warp sets:
- Working:
⠋ π session — project(animated braille spinner) - Ready:
π session — project(static, no spinner)
Most terminals display this in their tab bar as a basic activity indicator.
Warp defines a private protocol where CLI agents send structured notifications via OSC 777:
ESC ] 777 ; notify ; warp://cli-agent ; {"v":1,"agent":"pi","event":"stop",...} BEL
This gives Warp fine-grained session state: who's running, what they're doing, when they're blocked, and when they're done.
| Pi lifecycle event | Warp event | Status transition |
|---|---|---|
session_start |
session_start |
Initialize tracking |
agent_start |
session_start + prompt_submit |
Reattach listener, → InProgress |
message_start (user) |
prompt_submit |
Correct/update query, → InProgress |
| active turn heartbeat (~15s) | session_start + prompt_submit |
Recover dropped tab state |
tool_result |
tool_complete |
Tool context update |
agent_end |
stop |
→ Success ✓ |
agent_end + 300ms |
idle_prompt |
(no change) |
We reverse-engineered Warp's open-source codebase (warpdotdev/Warp) and cross-referenced it with Codex CLI (openai/codex) and Claude Code to understand the full integration story. Here's what we found.
Warp maintains a CLIAgentSessionsModel — a per-tab state machine that tracks:
CLIAgentSessionStatus:
InProgress → the agent is working
Success → the agent's turn is done
Blocked → the agent needs user input
It detects CLI agents by matching the command against known prefixes (claude, codex, gemini, pi, amp, etc.) and subscribes to their PTY output for event signals.
Codex uses two mechanisms (neither of which is the structured JSON protocol):
- OSC 0 terminal title — Rich dynamic titles like
⠋ codex - project | Workingthat Warp reads as a fallback tab label - OSC 9 plain-text notifications — Simple text like
"Agent turn complete"that Warp'sCodexSessionHandlerparses into a Stop event
Codex does not send OSC 777 JSON events. Its Warp integration is lower-fidelity — all OSC 9 notifications are treated as "success/stop" since there's no way to distinguish event types from plain text.
Claude Code uses the full OSC 777 warp://cli-agent protocol with structured JSON events. This is the gold standard — Warp gets precise prompt_submit, tool_complete, stop, permission_request, etc. events with rich metadata.
Warp uses DefaultSessionListener for Claude Code (and also for Pi, Gemini, OpenCode, Auggie), which expects structured JSON events via OSC 777.
| Capability | Codex | Claude Code | Pi (before) | Pi (with pi-warp) |
|---|---|---|---|---|
| OSC 0 dynamic title with spinner | ✅ rich | ✅ | ❌ static | ✅ |
OSC 777 session_start |
❌ | ✅ | ❌ | ✅ |
OSC 777 prompt_submit |
❌ | ✅ | ❌ | ✅ |
OSC 777 tool_complete |
❌ | ✅ | ❌ | ✅ |
OSC 777 stop (success) |
❌ | ✅ | ❌ | ✅ |
OSC 777 idle_prompt |
❌ | ✅ | ❌ | ✅ |
| Warp tab status display | ✅ full | ❌ none | ✅ full |
Pi had CLIAgent::Pi registered in Warp's codebase (with a brand color and DefaultSessionListener), but since Pi never sent any events, the handler had nothing to process.
Pi's extension API provides all the necessary hooks:
session_start/session_shutdown→ session lifecycleagent_start/agent_end→ turn lifecyclemessage_start→ capture user promptstool_result→ tool completion eventsctx.ui.setTitle()→ terminal title controlprocess.stdout.write()→ raw escape sequence output
This makes it possible to implement the full warp://cli-agent protocol as a pure extension, without any changes to Pi's core.
- Warp event protocol:
app/src/terminal/cli_agent_sessions/event/ - Warp CLI agent enum:
app/src/terminal/cli_agent.rs - Warp session handler:
app/src/terminal/cli_agent_sessions/listener/mod.rs - Codex terminal title:
codex-rs/tui/src/terminal_title.rs - Codex status surfaces:
codex-rs/tui/src/chatwidget/status_surfaces.rs
# Install dev dependencies
npm install
# Run tests
npx vitest run
# Test locally without installing
pi -e ./extensions/warp-integration.tspermission_request/question_askedevents: Pi's extension API doesn't currently expose a "waiting for user approval" hook. When Pi adds this, the extension can emitpermission_requestto show the Blocked status in Warp's tab.WARP_CLI_AGENT_PROTOCOL_VERSIONnegotiation: Warp exports this env var on the PTY. Future versions could use it to negotiate payload format.
MIT
{ "v": 1, // Protocol version "agent": "pi", // CLIAgent identifier "event": "prompt_submit", // Event type "session_id": "a1b2c3d4-...", // Stable per-session UUID "cwd": "/Users/you/project", // Working directory "project": "project", // Project folder name "query": "fix the failing tests", // User's prompt (truncated) "response": "Done! All tests pass.", // Agent's reply (truncated) "tool_name": "bash", // Last tool used "tool_input": {"command": "npm test"},// Tool arguments preview "plugin_version": "0.1.2" // Extension version }