Operate your local desktop Claude Code on your phone
TypeScript
9
724 commits
updated Oct 6, 2026
Control Claude Code, Codex, opencode, and Grok from anywhere. Answer prompts from your phone while your agent runs on your desktop.
Crabigator is a terminal wrapper that runs Claude Code, Codex CLI, opencode, or Grok with real-time status widgets and remote control from your phone. The assistant runs natively on your machine exactly as intended, while Crabigator streams the session to a web dashboard where you can:
npm install -g crabigator
git clone https://github.com/samuelclay/crabigator.git
cd crabigator
cargo install --path .
# Run with your default platform (Claude Code unless configured otherwise)
crabigator
# Or pick the platform explicitly
crabigator claude
crabigator codex
The first time you run Crabigator, it prompts you to pair with your phone:
Once paired, your sessions automatically stream to the dashboard.
Answer Claude's prompts from your phone when you're away from your desk. Permission requests, questions, and plan approvals all work remotely.
Real-time widgets below the assistant's interface show:
After each turn, Crabigator generates a short recap of what the assistant did — visible in the terminal, on the dashboard, and on the PR board. Recaps are generated locally from your transcripts; only the finished recap is sent to the cloud. Enable with crabigator recap enable (requires an Anthropic API key).
crabigator prs opens a live board of every pull request your sessions are working on, across all running sessions, grouped by repository. Sessions classify their PRs as primary or secondary, and the board shows progress, review state, and activity.
Board keys. A letter that appears in the default label is underlined there.
s — Flip between session view and PR view. Session view is the default: one block per session, with the pull requests it touches underneath. PR view is one block per primary PR.l — Toggle between live sessions and the durable cloud record, which includes ended sessionso — This computer only, or this computer plus the account's other computers. The board opens on both.r — Show or hide complete recapsa — Cycle the activity age. The board opens at the last 24 hours.w — Watch any PR by URL or owner/repo#123, session or not; "track PR " typed in a session does the same/ — Search, including a grep of each live session's transcript with matched excerpts inline (Tab toggles surrounding context)f — Fullscreen the selected session and type into it. The header shows this only while a session is selected.The board saves these view choices between sessions. The full history also lives on the dashboard's PR board.
ide setting in config)When the Mac display is asleep, new Ghostty sessions start in the background so you can use them from the dashboard immediately. Ghostty attaches to the same session when the display wakes. This requires tmux (brew install tmux). The Mac must remain awake and connected to the network.
Supports Claude Code (Anthropic), Codex CLI (OpenAI), opencode, and Grok.
Point a remote MCP client at your live sessions:
https://drinkcrabigator.com/mcp
The client signs in with GitHub or Google. If this account has no desktop yet, enter a pairing code from crabigator pair. After that, the agent can list sessions, read screens, answer prompts, and drive the PR board — the same actions as the dashboard. Every tool, with example output, is listed at drinkcrabigator.com/mcp-tools.
Claude Desktop, Claude.ai custom connectors, Cursor, Grok, and the MCP Inspector all use that URL. In Grok:
grok mcp add --transport http crabigator https://drinkcrabigator.com/mcp
Then open /mcps and press i to sign in. Self-hosted Workers expose {origin}/mcp when features.mcp is enabled (the default).
Sessions that never stream to the cloud are invisible here, same as on the website.
To see whether a client call reached the server, call get_mcp_logs or follow drinkcrabigator.com/mcp-tools#logs. Match the client's X-Mcp-Request-Id response header to request_id.
┌─────────────────────────────────────┐
│ │
│ Claude Code (PTY) │ ← Runs exactly as normal
│ │
├─────────────────────────────────────┤
│ Recap · Tracked PRs │ ← Handoff strip
│ Stats │ Git Status │ File Changes │ ← Status widgets
└─────────────────────────────────────┘
│
▼
┌───────────────┐
│ Cloud Relay │ ← Official or self-hosted Cloudflare Worker
└───────────────┘
│
▼
┌───────────────┐
│ Your Phone │ ← Answer prompts remotely
└───────────────┘
Crabigator spawns the assistant CLI in a pseudo-terminal and uses ANSI scroll region escape sequences to confine its output to the top portion of your terminal. Status widgets render below using raw escape codes — no intermediate rendering library. Session state streams to Cloudflare Workers over WebSocket for the mobile dashboard, with automatic reconnection.
crabigator # Start with your default platform
crabigator claude # Use Claude Code
crabigator codex # Use Codex CLI
crabigator opencode # Use opencode
crabigator grok # Use Grok Build (also: xai)
crabigator resume # Resume last session (also: r, --resume)
crabigator continue # Continue last conversation (also: c, --continue)
crabigator prs # Live cross-session PR board
crabigator prs --once # Print one frame of the board and exit
crabigator inspect # List other running instances
crabigator inspect /path # Filter instances by working directory
crabigator inspect --watch # Continuous monitoring
crabigator inspect --raw # Raw JSON output
crabigator inspect --history # Hook event history for debugging
crabigator recap enable # Turn on per-turn recaps (prompts for API key)
crabigator recap disable # Turn off recaps and remove the stored key
crabigator recap status # Show recap configuration
crabigator key <api-key> # Save an Anthropic API key for recaps
crabigator cloud status # Show the active cloud service and local state path
crabigator cloud set <origin> # Verify and use a compatible self-hosted Worker
crabigator cloud reset # Return to the official Crabigator service
crabigator pair # Generate a dashboard pairing code
crabigator install-launcher # Install the macOS crabigator:// URL handler
crabigator --no-capture # Run without writing scrollback.log/screen.txt
Any unrecognized arguments pass through to the underlying assistant CLI.
Preferences live in ~/.crabigator/config.toml:
default_platform = "claude" # or "codex"
ide = "vscode" # clickable file links: vscode, cursor, idea, zed, sublime, none
terminal = "ghostty" # terminal override: terminal, ghostty (auto-detects if unset)
check_for_updates = true # check GitHub Releases on startup
recap_enabled = true # per-turn recaps (needs an API key: crabigator key)
recap_model = "claude-haiku-4-5" # optional model override for recaps
[cloud]
# url = "https://crabigator.example.com" # omit to use the official service
[pr_board] # crabigator prs view preferences (saved automatically)
include_ended = false # live sessions; true also shows ended ones
include_remote = true # include sessions on the account's other computers
detail = 0 # 0 compact, 1 complete recaps
oldest_visible_hours = 24 # activity age filter; 0 shows every age
view = "sessions" # "sessions" or "prs"
crabigator cloud set accepts HTTPS origins. It also accepts HTTP for loopback
development, such as http://localhost:8787. The command checks /api/health
before it saves the URL. Use --force only when the service is temporarily
unreachable but you know it is compatible.
Crabigator keeps each custom host's device identity, pairing cache, and offline
queue under ~/.crabigator/cloud/<origin-hash>/. The official service keeps its
existing files directly under ~/.crabigator/. Switching hosts does not copy
devices, sessions, or other data between services.
Claude Code hooks are installed to ~/.claude/crabigator/ for tracking session state and statistics. They are versioned and reinstall themselves automatically when Crabigator updates.
The Worker in workers/crabigator-api/ contains the relay, dashboard, pairing,
Durable Objects, D1 database, and KV-backed tokens. A basic deployment needs
only a Cloudflare account. Optional hosted-service features stay off unless you
enable and configure them.
cd workers/crabigator-api
npm install
cp wrangler.example.jsonc wrangler.jsonc
wrangler.example.jsonc is the annotated, tracked template. wrangler.jsonc
is ignored by Git so it can hold your account's resource IDs, routes, and public
settings. Do not put API keys in either file.
The example deploys to workers.dev. For a custom domain, set workers_dev to
false and add this top-level setting:
"routes": [
{ "pattern": "crabigator.example.com", "custom_domain": true }
]
Both forms run the same Worker. Set APP_CONFIG.public_origin to your final
HTTPS origin when you enable email, payments, or traffic alerts. Otherwise,
leave it blank and web pages use the incoming request origin.
npm run db:migrate:local
npm run dev
curl http://localhost:8787/api/health
Wrangler uses local D1, KV, and Durable Object storage for this flow. All D1
migrations in migrations/ are applied in order.
wrangler login
npm run deploy
npm run db:migrate:remote
curl https://your-worker.example/api/health
The template omits D1 and KV IDs, so Wrangler can create and bind those resources during the first deploy. Keep every Durable Object migration entry in the template. Removing old entries can break an existing deployment.
You can select another config or Wrangler profile without editing scripts:
WRANGLER_CONFIG=wrangler.staging.jsonc WRANGLER_PROFILE=my-profile npm run deploy
wrangler.production.jsonc is the official drinkcrabigator.com deployment's
config. It is tracked as a working reference, but it names that account's
resources and routes, so it only deploys with that account's Wrangler profile.
crabigator cloud set https://your-worker.example
crabigator cloud status
crabigator pair
Open the dashboard URL shown by Crabigator and enter the pairing code. Use
crabigator cloud reset to return to the official service.
The Worker test-state.sh, test-events.sh, and test-answer.sh helpers also
use the selected cloud URL and its host-specific device identity. Set
CLOUD_URL and CRABIGATOR_STATE_DIR only when testing without an installed
crabigator command.
Enable a feature in APP_CONFIG.features, then add its required secrets with
wrangler secret put NAME. A requested feature stays unavailable until all of
its required values exist. /api/health lists active capabilities and missing
configuration.
| Feature | Public configuration | Secrets |
|---|---|---|
| Core relay, dashboard, pairing, PR board | None | None |
| Voice transcription | features.transcription | OPENAI_API_KEY |
| Billing | features.billing, display price, provider mode, and visible-session limit | Stripe live or test keys, or PayPal client, secret, webhook ID, and plan ID |
| Gifts | features.gifts and billing | Same payment provider values as billing |
| Outbound gift email | features.outbound_email, Mailgun domain and sender | MAILGUN_API_KEY |
| Marketing analytics | features.marketing_analytics; optional Meta Pixel ID | None |
| Traffic alerts | features.traffic_alerts, public origin, Mailgun values, alert recipient | MAILGUN_API_KEY |
| Staff tools | features.staff | STAFF_ACCESS_KEY |
Use a long, randomly generated STAFF_ACCESS_KEY. The /staff login creates a
12-hour, Secure, HttpOnly, SameSite=Strict session in KV. Changing the access
key invalidates existing sessions. Staff-changing requests also require a
same-origin browser request.
Stripe secrets are STRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRET, and
STRIPE_PRICE_ID; append _TEST for test mode. PayPal secrets are
PAYPAL_CLIENT_ID, PAYPAL_CLIENT_SECRET, PAYPAL_WEBHOOK_ID, and
PAYPAL_PLAN_ID.
Each session creates /tmp/crabigator-{session_id}/ containing:
scrollback.log — Session transcript, built from the platform's JSONL logscreen.txt — Current screen snapshotinspect.json — Widget state for external tools (crabigator inspect reads these)hooks.log — Hook invocation log (Claude Code)The desktop app is Rust; the cloud backend is a Cloudflare Workers project. The full module map lives in AGENTS.md — highlights:
| Area | Where | What |
|---|---|---|
| App loop | src/app.rs | Scroll region layout, event polling, PTY passthrough |
| Terminal | src/terminal/ | PTY management, input encoding, ANSI escape sequences |
| Widgets | src/ui/ | Status bar, git, changes, stats, handoff strip, pairing banners |
| Diff parsing | src/parsers/ | Semantic diffs per language, scope attribution |
| Platforms | src/platforms/ | Claude Code hooks and transcript parsing; Codex session logs; opencode event stream; Grok session logs |
| Recaps & PRs | src/recap.rs, src/pr.rs, src/prs_board.rs | Turn recaps, PR tracking and classification, the prs board |
| Cloud client | src/cloud/ | Device identity, HMAC auth, event queue, WebSocket streaming |
| Cloud backend | workers/crabigator-api/ | Durable Objects, D1, dashboard, landing page |
It's a quadruple wordplay:
Contributions welcome! Please feel free to submit a Pull Request.
# Development
cargo build # Debug build
cargo test # Run tests (fixture snapshots: make test-update)
cargo clippy # Lint
# Cloud development
cd workers/crabigator-api
npm run dev # Local dev server
MIT License - see LICENSE for details.
Operate your local desktop Claude Code on your phone
TypeScript
9
724 commits
updated Oct 6, 2026
Control Claude Code, Codex, opencode, and Grok from anywhere. Answer prompts from your phone while your agent runs on your desktop.
Crabigator is a terminal wrapper that runs Claude Code, Codex CLI, opencode, or Grok with real-time status widgets and remote control from your phone. The assistant runs natively on your machine exactly as intended, while Crabigator streams the session to a web dashboard where you can:
npm install -g crabigator
git clone https://github.com/samuelclay/crabigator.git
cd crabigator
cargo install --path .
# Run with your default platform (Claude Code unless configured otherwise)
crabigator
# Or pick the platform explicitly
crabigator claude
crabigator codex
The first time you run Crabigator, it prompts you to pair with your phone:
Once paired, your sessions automatically stream to the dashboard.
Answer Claude's prompts from your phone when you're away from your desk. Permission requests, questions, and plan approvals all work remotely.
Real-time widgets below the assistant's interface show:
After each turn, Crabigator generates a short recap of what the assistant did — visible in the terminal, on the dashboard, and on the PR board. Recaps are generated locally from your transcripts; only the finished recap is sent to the cloud. Enable with crabigator recap enable (requires an Anthropic API key).
crabigator prs opens a live board of every pull request your sessions are working on, across all running sessions, grouped by repository. Sessions classify their PRs as primary or secondary, and the board shows progress, review state, and activity.
Board keys. A letter that appears in the default label is underlined there.
s — Flip between session view and PR view. Session view is the default: one block per session, with the pull requests it touches underneath. PR view is one block per primary PR.l — Toggle between live sessions and the durable cloud record, which includes ended sessionso — This computer only, or this computer plus the account's other computers. The board opens on both.r — Show or hide complete recapsa — Cycle the activity age. The board opens at the last 24 hours.w — Watch any PR by URL or owner/repo#123, session or not; "track PR " typed in a session does the same/ — Search, including a grep of each live session's transcript with matched excerpts inline (Tab toggles surrounding context)f — Fullscreen the selected session and type into it. The header shows this only while a session is selected.The board saves these view choices between sessions. The full history also lives on the dashboard's PR board.
ide setting in config)When the Mac display is asleep, new Ghostty sessions start in the background so you can use them from the dashboard immediately. Ghostty attaches to the same session when the display wakes. This requires tmux (brew install tmux). The Mac must remain awake and connected to the network.
Supports Claude Code (Anthropic), Codex CLI (OpenAI), opencode, and Grok.
Point a remote MCP client at your live sessions:
https://drinkcrabigator.com/mcp
The client signs in with GitHub or Google. If this account has no desktop yet, enter a pairing code from crabigator pair. After that, the agent can list sessions, read screens, answer prompts, and drive the PR board — the same actions as the dashboard. Every tool, with example output, is listed at drinkcrabigator.com/mcp-tools.
Claude Desktop, Claude.ai custom connectors, Cursor, Grok, and the MCP Inspector all use that URL. In Grok:
grok mcp add --transport http crabigator https://drinkcrabigator.com/mcp
Then open /mcps and press i to sign in. Self-hosted Workers expose {origin}/mcp when features.mcp is enabled (the default).
Sessions that never stream to the cloud are invisible here, same as on the website.
To see whether a client call reached the server, call get_mcp_logs or follow drinkcrabigator.com/mcp-tools#logs. Match the client's X-Mcp-Request-Id response header to request_id.
┌─────────────────────────────────────┐
│ │
│ Claude Code (PTY) │ ← Runs exactly as normal
│ │
├─────────────────────────────────────┤
│ Recap · Tracked PRs │ ← Handoff strip
│ Stats │ Git Status │ File Changes │ ← Status widgets
└─────────────────────────────────────┘
│
▼
┌───────────────┐
│ Cloud Relay │ ← Official or self-hosted Cloudflare Worker
└───────────────┘
│
▼
┌───────────────┐
│ Your Phone │ ← Answer prompts remotely
└───────────────┘
Crabigator spawns the assistant CLI in a pseudo-terminal and uses ANSI scroll region escape sequences to confine its output to the top portion of your terminal. Status widgets render below using raw escape codes — no intermediate rendering library. Session state streams to Cloudflare Workers over WebSocket for the mobile dashboard, with automatic reconnection.
crabigator # Start with your default platform
crabigator claude # Use Claude Code
crabigator codex # Use Codex CLI
crabigator opencode # Use opencode
crabigator grok # Use Grok Build (also: xai)
crabigator resume # Resume last session (also: r, --resume)
crabigator continue # Continue last conversation (also: c, --continue)
crabigator prs # Live cross-session PR board
crabigator prs --once # Print one frame of the board and exit
crabigator inspect # List other running instances
crabigator inspect /path # Filter instances by working directory
crabigator inspect --watch # Continuous monitoring
crabigator inspect --raw # Raw JSON output
crabigator inspect --history # Hook event history for debugging
crabigator recap enable # Turn on per-turn recaps (prompts for API key)
crabigator recap disable # Turn off recaps and remove the stored key
crabigator recap status # Show recap configuration
crabigator key <api-key> # Save an Anthropic API key for recaps
crabigator cloud status # Show the active cloud service and local state path
crabigator cloud set <origin> # Verify and use a compatible self-hosted Worker
crabigator cloud reset # Return to the official Crabigator service
crabigator pair # Generate a dashboard pairing code
crabigator install-launcher # Install the macOS crabigator:// URL handler
crabigator --no-capture # Run without writing scrollback.log/screen.txt
Any unrecognized arguments pass through to the underlying assistant CLI.
Preferences live in ~/.crabigator/config.toml:
default_platform = "claude" # or "codex"
ide = "vscode" # clickable file links: vscode, cursor, idea, zed, sublime, none
terminal = "ghostty" # terminal override: terminal, ghostty (auto-detects if unset)
check_for_updates = true # check GitHub Releases on startup
recap_enabled = true # per-turn recaps (needs an API key: crabigator key)
recap_model = "claude-haiku-4-5" # optional model override for recaps
[cloud]
# url = "https://crabigator.example.com" # omit to use the official service
[pr_board] # crabigator prs view preferences (saved automatically)
include_ended = false # live sessions; true also shows ended ones
include_remote = true # include sessions on the account's other computers
detail = 0 # 0 compact, 1 complete recaps
oldest_visible_hours = 24 # activity age filter; 0 shows every age
view = "sessions" # "sessions" or "prs"
crabigator cloud set accepts HTTPS origins. It also accepts HTTP for loopback
development, such as http://localhost:8787. The command checks /api/health
before it saves the URL. Use --force only when the service is temporarily
unreachable but you know it is compatible.
Crabigator keeps each custom host's device identity, pairing cache, and offline
queue under ~/.crabigator/cloud/<origin-hash>/. The official service keeps its
existing files directly under ~/.crabigator/. Switching hosts does not copy
devices, sessions, or other data between services.
Claude Code hooks are installed to ~/.claude/crabigator/ for tracking session state and statistics. They are versioned and reinstall themselves automatically when Crabigator updates.
The Worker in workers/crabigator-api/ contains the relay, dashboard, pairing,
Durable Objects, D1 database, and KV-backed tokens. A basic deployment needs
only a Cloudflare account. Optional hosted-service features stay off unless you
enable and configure them.
cd workers/crabigator-api
npm install
cp wrangler.example.jsonc wrangler.jsonc
wrangler.example.jsonc is the annotated, tracked template. wrangler.jsonc
is ignored by Git so it can hold your account's resource IDs, routes, and public
settings. Do not put API keys in either file.
The example deploys to workers.dev. For a custom domain, set workers_dev to
false and add this top-level setting:
"routes": [
{ "pattern": "crabigator.example.com", "custom_domain": true }
]
Both forms run the same Worker. Set APP_CONFIG.public_origin to your final
HTTPS origin when you enable email, payments, or traffic alerts. Otherwise,
leave it blank and web pages use the incoming request origin.
npm run db:migrate:local
npm run dev
curl http://localhost:8787/api/health
Wrangler uses local D1, KV, and Durable Object storage for this flow. All D1
migrations in migrations/ are applied in order.
wrangler login
npm run deploy
npm run db:migrate:remote
curl https://your-worker.example/api/health
The template omits D1 and KV IDs, so Wrangler can create and bind those resources during the first deploy. Keep every Durable Object migration entry in the template. Removing old entries can break an existing deployment.
You can select another config or Wrangler profile without editing scripts:
WRANGLER_CONFIG=wrangler.staging.jsonc WRANGLER_PROFILE=my-profile npm run deploy
wrangler.production.jsonc is the official drinkcrabigator.com deployment's
config. It is tracked as a working reference, but it names that account's
resources and routes, so it only deploys with that account's Wrangler profile.
crabigator cloud set https://your-worker.example
crabigator cloud status
crabigator pair
Open the dashboard URL shown by Crabigator and enter the pairing code. Use
crabigator cloud reset to return to the official service.
The Worker test-state.sh, test-events.sh, and test-answer.sh helpers also
use the selected cloud URL and its host-specific device identity. Set
CLOUD_URL and CRABIGATOR_STATE_DIR only when testing without an installed
crabigator command.
Enable a feature in APP_CONFIG.features, then add its required secrets with
wrangler secret put NAME. A requested feature stays unavailable until all of
its required values exist. /api/health lists active capabilities and missing
configuration.
| Feature | Public configuration | Secrets |
|---|---|---|
| Core relay, dashboard, pairing, PR board | None | None |
| Voice transcription | features.transcription | OPENAI_API_KEY |
| Billing | features.billing, display price, provider mode, and visible-session limit | Stripe live or test keys, or PayPal client, secret, webhook ID, and plan ID |
| Gifts | features.gifts and billing | Same payment provider values as billing |
| Outbound gift email | features.outbound_email, Mailgun domain and sender | MAILGUN_API_KEY |
| Marketing analytics | features.marketing_analytics; optional Meta Pixel ID | None |
| Traffic alerts | features.traffic_alerts, public origin, Mailgun values, alert recipient | MAILGUN_API_KEY |
| Staff tools | features.staff | STAFF_ACCESS_KEY |
Use a long, randomly generated STAFF_ACCESS_KEY. The /staff login creates a
12-hour, Secure, HttpOnly, SameSite=Strict session in KV. Changing the access
key invalidates existing sessions. Staff-changing requests also require a
same-origin browser request.
Stripe secrets are STRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRET, and
STRIPE_PRICE_ID; append _TEST for test mode. PayPal secrets are
PAYPAL_CLIENT_ID, PAYPAL_CLIENT_SECRET, PAYPAL_WEBHOOK_ID, and
PAYPAL_PLAN_ID.
Each session creates /tmp/crabigator-{session_id}/ containing:
scrollback.log — Session transcript, built from the platform's JSONL logscreen.txt — Current screen snapshotinspect.json — Widget state for external tools (crabigator inspect reads these)hooks.log — Hook invocation log (Claude Code)The desktop app is Rust; the cloud backend is a Cloudflare Workers project. The full module map lives in AGENTS.md — highlights:
| Area | Where | What |
|---|---|---|
| App loop | src/app.rs | Scroll region layout, event polling, PTY passthrough |
| Terminal | src/terminal/ | PTY management, input encoding, ANSI escape sequences |
| Widgets | src/ui/ | Status bar, git, changes, stats, handoff strip, pairing banners |
| Diff parsing | src/parsers/ | Semantic diffs per language, scope attribution |
| Platforms | src/platforms/ | Claude Code hooks and transcript parsing; Codex session logs; opencode event stream; Grok session logs |
| Recaps & PRs | src/recap.rs, src/pr.rs, src/prs_board.rs | Turn recaps, PR tracking and classification, the prs board |
| Cloud client | src/cloud/ | Device identity, HMAC auth, event queue, WebSocket streaming |
| Cloud backend | workers/crabigator-api/ | Durable Objects, D1, dashboard, landing page |
It's a quadruple wordplay:
Contributions welcome! Please feel free to submit a Pull Request.
# Development
cargo build # Debug build
cargo test # Run tests (fixture snapshots: make test-update)
cargo clippy # Lint
# Cloud development
cd workers/crabigator-api
npm run dev # Local dev server
MIT License - see LICENSE for details.