A lightweight orchestration layer that turns any phone into a remote command & control surface for long-running Claude Code sessions — built to accelerate multi-agent development workflows.
Works with both Claude Code surfaces: the claude CLI (stream-json
mode) and the Claude Code VS Code extension (hook-based ingress).
A single mobile app drives either, or both at once.
While architecting a 460K+ LOC trading infrastructure, I was leaning heavily on Anthropic's Claude Code CLI for long-running, multi-step engineering tasks. Before first-party remote execution support landed, the workflow had a real bottleneck:
- Tasks ran for 10+ minutes; you had to sit at the terminal.
- No programmatic way to nudge an in-flight session from elsewhere.
- SSH/VNC is a poor mobile UX. Cloud sandboxes move code off-machine.
- "Approve this tool call?" prompts blocked everything if you stepped away.
I wanted to step away from the laptop — to the gym, a call, or another screen — without either babysitting the agent or giving up steering control.
Pulse is an opinionated orchestration layer that wraps Claude Code and fans its lifecycle events out to a mobile-first control surface in real time. It splits the workload across three tiers:
| Tier | Role | Stack |
|---|---|---|
| Observer | Hooks into Claude Code, parses events, streams output. | Python 3.10+ · pexpect · pyte |
| Relay | Stateless WebSocket bus. Buffers on disconnect, gates approvals. | Node.js 18+ · Express · Socket.IO |
| Commander | Mobile PWA. Heatmap, decision cards, voice input, config panel. | React 18 · Three.js · Framer Motion |
| MCP Bridge | MCP server the Claude Code IDE calls to check a mobile inbox. | Node.js 18+ · @modelcontextprotocol/sdk |
Concretely, Pulse gives you:
- Automated remote execution — start, steer, and resume sessions from any device on the same relay.
- Environment & session state tracking — cost, turns, tokens, model, approvals, and file activity captured per session.
- Structured conversation mirror — human prompts, tool use, tool results, and assistant text replayed on reconnect (5-minute buffer).
- Hook-mediated approvals —
PreToolUseblocks the CLI until mobile approves/denies, driven entirely by Claude Code's native hook system. - Bidirectional messaging — the MCP bridge exposes a tool surface for Claude to read mobile-sent messages mid-session.
┌──────────────────────────────────────────────────────────────┐
│ LAPTOP (local) │
│ ┌──────────────┐ ┌──────────────────┐ ┌──────────────┐ │
│ │ Claude Code │──▶│ Observer (CLI) │ │ MCP Bridge │ │
│ │ CLI │ │ + Hook Handler │ │ (stdio) │ │
│ └──────────────┘ └────────┬─────────┘ └──────┬───────┘ │
└─────────────────────────────┬┴─────────────────────┴─────────┘
│ HTTP + WebSocket (Socket.IO)
▼
┌──────────────────────────────────────────────────────────────┐
│ RELAY (any host) │
│ Stateless pipe · Session registry · 5-min replay buffer │
│ REST: /api/sessions · /api/hook · /api/inbox · /api/approval
│ WS: observer:* · commander:* · approval:* · conversation │
└──────────────────────────────────────────────────────────────┘
▲
│ HTTPS + WebSocket
│
┌──────────────────────────────────────────────────────────────┐
│ COMMANDER (mobile PWA) │
│ Heatmap · Decision Cards · Voice Input · Config · E-Stop │
└──────────────────────────────────────────────────────────────┘
Two ingestion paths feed the same session model — pick whichever matches your surface; the commander treats both identically:
- CLI Observer — for the
claudeCLI. A Python process spawnsclaude --output-format stream-json, parses the stream, and forwards structured events to the relay over WebSocket. Best for headless runs, full cost tracking, and--resumefollow-up tasks. - Hook-Observed IDE Session — for the Claude Code VS Code extension
(and any other Claude Code surface that fires hooks). Claude Code's
lifecycle hooks (
SessionStart,UserPromptSubmit,PreToolUse,PostToolUse,Notification,Stop,SessionEnd) POST to/api/hook; Pulse auto-creates a session keyed onclaudeSessionId. The MCP bridge gives Claude a first-class tool surface to drain the mobile inbox mid-turn. No CLI wrapping required.
- Node.js 18+, Python 3.10+
- At least one of:
claudeCLI —npm i -g @anthropic-ai/claude-code(or invoke vianpx)- Claude Code VS Code extension — install from the VS Code Marketplace and sign in (hooks fire automatically once Pulse is registered)
- A host reachable by both your laptop and your phone (localhost on the same Wi-Fi works; see Deployment for remote setups)
cd relay
npm install
cp .env.example .env # adjust PORT / AUTH_TOKEN if needed
npm start
# → http://localhost:3001python setup.py # registers hooks + MCP server in ~/.claude/settings.jsonInspect with python setup.py --verify. Uninstall cleanly with
python setup.py --remove.
cd commander
npm install
cp .env.example .env # point REACT_APP_RELAY_URL at your relay
npm start # http://localhost:3000On your phone, navigate to http://<laptop-ip>:3000 and Add to Home
Screen to install the PWA.
Pulse supports both Claude Code surfaces. Pick whichever you use.
A. VS Code extension (recommended for most users)
Open any project in VS Code, open the Claude Code panel, and start a turn.
The SessionStart hook fires automatically and the session appears on the
commander within a second. No additional commands — just code.
B. claude CLI (headless / scripted runs)
cd observer
pip install -r requirements.txt
python sidecar.py start "Refactor the authentication module"The relay ID prints to the terminal. Tap it on your phone to take over.
You can mix both: a long-running CLI refactor and an ad-hoc VS Code session will appear side-by-side on the commander's session list.
All components read from environment variables. See each component's
.env.example for the full list.
| Variable | Component | Default | Purpose |
|---|---|---|---|
PORT |
relay | 3001 |
HTTP/WS port |
ALLOWED_ORIGINS |
relay | * |
CORS allowlist (comma-separated) |
REQUIRE_AUTH |
relay | false |
Gate connections with AUTH_TOKEN |
AUTH_TOKEN |
relay | — | Shared secret (observer ↔ relay) |
SIDECAR_RELAY_URL |
observer | http://localhost:3001 |
Where the observer/hooks send events |
SIDECAR_AUTH_TOKEN |
observer | — | Must match relay AUTH_TOKEN when auth is on |
SIDECAR_APPROVAL_TIMEOUT |
observer | 300 |
Seconds a PreToolUse hook waits for mobile |
REACT_APP_RELAY_URL |
commander | http://localhost:3001 |
Relay URL consumed by the PWA |
Note on env var names.
SIDECAR_*prefixes are preserved for backward compatibility with existingsettings.jsonhook installations. They refer to the same Pulse relay — renaming is a planned breaking change for v2.
The Commander PWA ships with four distinct surfaces:
- Heatmap — live file-tree with activity coloring (green = created, amber = edited, red = deleted), access count badges, and a cool-to-hot recency gradient.
- Decision Cards — one card per pending approval. Swipe right to approve, left to deny, long-press to attach a voice note. State-version checks prevent stale approvals if the laptop has moved on.
- Conversation — structured thread mirroring the Claude session: human prompts, tool calls with collapsible previews, tool results, assistant text, and end-of-task summary cards with cost/turns/files/commands.
- Config Panel — model (Opus/Sonnet/Haiku), permission mode, max turns, max budget, per-tool allow/deny, system prompt append — all applied on the next task without killing the session.
A single large red Emergency Stop is pinned across every screen;
tapping it SIGINTs the active process and flips the session to stopped.
The relay exposes a REST + WebSocket API that is stable for the lifetime of a
session. Full reference: docs/API.md.
GET /health server health
GET /api/sessions list active sessions
POST /api/sessions create session
POST /api/sessions/:id/approve approve/deny a pending tool
POST /api/sessions/:id/config push config patch to observer
POST /api/sessions/:id/stop emergency stop
POST /api/hook unified hook ingress
GET /api/approval/:id poll approval (used by hook)
GET /api/inbox/:sessionId MCP reads & drains mobile inbox
POST /api/inbox/:sessionId queue message for MCP pickup
| Direction | Event | Purpose |
|---|---|---|
| Observer → Relay | observer:register |
Bind a CLI observer to a session |
| Observer → Relay | observer:event |
File/tool lifecycle event |
| Observer → Relay | observer:conversation_message |
Structured chat message |
| Observer → Relay | observer:heartbeat |
Liveness + cost/turns snapshot |
| Commander → Relay | commander:connect |
Join session (with replay cursor) |
| Commander → Relay | commander:approve |
Decide a pending approval |
| Commander → Relay | commander:new_task |
Follow-up task (uses --resume) |
| Commander → Relay | commander:emergency_stop |
Kill the active process |
| Relay → Commander | approval:request |
New decision card |
| Relay → Commander | conversation:message |
Append to thread |
| Relay → Commander | session:state |
Full snapshot on (re)connect |
| Relay → Commander | session:replay |
Buffered catch-up after reconnect |
Pulse/
├── commander/ React PWA (mobile command center)
├── relay/ Node/Express + Socket.IO relay server
├── observer/ Python CLI wrapper + Claude Code hook handler
│ └── hooks/ `sidecar_hook.py` — dispatched by Claude Code hooks
├── mcp-bridge/ MCP stdio server bridging Claude Code ↔ relay inbox
├── docs/ Architecture, deployment, API reference
├── docker-compose.yml Single-command relay + commander stack
└── setup.py Idempotent installer for hooks + MCP registration
For production, run the relay behind TLS and set REQUIRE_AUTH=true. A
single-command stack is provided:
docker compose up -d # relay :3001 + commander :3000See docs/DEPLOYMENT.md for hardening notes,
reverse-proxy recipes, and the mobile-PWA install checklist.
- End-to-end encryption between observer and commander
- Biometric auth gate for destructive tool calls
- Time-travel scrubber across buffered state
- First-class multi-agent fan-out (N sessions, one commander)
- Optional local-LLM summarizer to compress bandwidth
Issues and pull requests are welcome. Please read
CONTRIBUTING.md and
CODE_OF_CONDUCT.md before opening a PR.
Security disclosures: see SECURITY.md.
MIT © 2026 Hriday.
Note. Pulse was built internally to accelerate my own architectural workflows on a large trading-infrastructure codebase. It is open-sourced here as a reference for high-velocity prototyping with Claude Code — not as a hosted product. Expect sharp edges on non-happy paths and please file issues when you find them.