Web UI for Claude Code and Codex sessions.
Run multiple agents, inspect every tool call, and gate risky actions with explicit approvals.
Requirements: Bun + Claude Code and/or Codex CLI.
bunx iclaudeOpen localhost:3456. In production, the backend serves the built frontend on this port. In dev mode, open localhost:2345 for the Vite dev server with HMR.
bun install -g iclaude
# Register as a background service (launchd on macOS, systemd on Linux)
iclaude install
# Start the service
iclaude startOpen http://localhost:3456. The server runs in the background and survives reboots.
| Command | Description |
|---|---|
iclaude |
Start server in foreground (default) |
iclaude serve |
Start server in foreground (explicit) |
iclaude install |
Register as a background service (launchd/systemd) |
iclaude start |
Start the background service |
iclaude stop |
Stop the background service |
iclaude restart |
Restart the background service |
iclaude uninstall |
Remove the background service |
iclaude status |
Show service status |
iclaude logs |
Tail service log files |
Options: --port <n> overrides the default port (3456).
- Parallel sessions: work on multiple tasks without juggling terminals.
- Full visibility: see streaming output, tool calls, and tool results in one timeline.
- Permission control: approve/deny sensitive operations from the UI.
- Session recovery: restore work after process/server restarts.
- Dual-engine support: designed for both Claude Code and Codex-backed flows.
| Chat + tool timeline | Permission flow |
|---|---|
![]() |
![]() |
Browser (React)
<-> ws://localhost:3456/ws/browser/:session
Companion server (Bun + Hono)
<-> ws://localhost:3456/ws/cli/:session
Claude Code / Codex CLI
The bridge uses the CLI --sdk-url websocket path and NDJSON events.
The server auto-generates an auth token on first start, stored at ~/.companion/auth.json. You can also manage tokens manually:
# Show the current token (or auto-generate one)
cd web && bun run generate-token
# Force-regenerate a new token
cd web && bun run generate-token --forceOr set a token via environment variable (takes priority over the file):
COMPANION_AUTH_TOKEN="my-secret-token" bunx iclaudemake devManual:
cd web
bun install
bun run dev # backend on :3456 + Vite HMR on :2345The dev server runs two ports: backend API/WebSocket on :3456, frontend with HMR on :2345.
Production: bun run build && bun run start serves frontend + backend on a single port (:3456).
Checks:
cd web
bun run typecheck
bun run testEvery push to main publishes a preview artifact:
| Artifact | Tag / dist-tag | Example |
|---|---|---|
| Docker image (moving) | preview-main |
docker.io/pannous/iclaude:preview-main |
| Docker image (immutable) | preview-<sha> |
docker.io/pannous/iclaude:preview-abc1234... |
| npm package | next |
bunx iclaude@next |
Preview builds use a patch-core bump (e.g. 0.68.1-preview.* when stable is 0.68.0) so the in-app update checker can detect them as semver-ahead of the current stable release. They are not production-stable — use latest / semver tags for stable releases.
In Settings > Updates, switch the update channel to Prerelease to receive preview builds. The default channel is Stable (semver releases only). Switching channels takes effect immediately on the next update check.
This fork adds several features and UX improvements on top of the upstream Companion:
Output HTML in a ```html code block and it auto-renders as an interactive iframe in the chat. Fragments can push state back to the agent via window.vibeReportState() and the agent can query it via REST. Console output (log/warn/error/info) is captured automatically.
All raw WebSocket messages (NDJSON from Claude Code, JSON-RPC from Codex) are recorded to ~/.companion/recordings/ as JSONL files. Useful for debugging and building replay-based tests. Controllable via COMPANION_RECORD=0 env var or per-session REST endpoints.
One-click toggle in Settings to expose the server over an SSH tunnel (companion.pannous.com). Auto-injects auth tokens so remote access is seamless.
Ghost text suggestions in the Composer, powered by OpenAI gpt-4o-mini (or OpenRouter). Accept with Tab, dismiss with Escape or Ctrl+Z. Double-tap to accept on iOS.
Fork button in the Composer creates a new independent session seeded with the current conversation history. The forked session auto-loads prior transcript and shows a "(fork)" suffix.
After the first assistant turn, sessions are automatically renamed using an AI summary (via OpenRouter or OpenAI fallback). The AI-generated title takes priority in the sidebar.
Root-level scripts (.claude/commands/*.md, project scripts) are auto-discovered and surfaced as slash commands in the HomePage and Composer menu. Global prompts from ~/.claude/prompts/*.md are also available.
Prompts are stored as individual Markdown files (~/.claude/prompts/*.md for global, {cwd}/.claude/commands/*.md for project), diverging from upstream's single JSON file approach.
SessionEditorPane — an in-browser file editor tab with syntax highlighting, file size gates for large files, and the ability to open files directly from chat by clicking paths.
Background sessions send native desktop notifications on permission requests and when they finish.
Edit the project's CLAUDE.md directly from the TopBar without leaving the UI.
Sidebar groups sessions by project folder. Clicking a folder label opens a new session in that folder. Per-project resume dropdown lets you quickly resume recent sessions.
Sidebar filters out "ghost" sessions — those with no title, no history, and no meaningful state. Upstream shows everything.
Archive, restore, delete, and rename actions are exposed as direct buttons on session rows instead of upstream's three-dot context menu. Rename is triggered by double-click.
Sidebar footer uses a compact 3-column grid layout for navigation instead of upstream's vertical Workbench/Workspace grouped sections. No external links section.
Permission mode defaults to bypassPermissions (agent mode). Upstream defaults to "default" which requires explicit approvals.
Sessions are only pruned if genuinely abandoned (no cwd, no history, no title). Upstream is more aggressive. Exited sessions are only restored if resumable, named, or archived.
User messages are deduplicated on reconnect by preserving client-generated IDs. Extended-thinking assistant messages sharing the same ID are also deduplicated.
XML system-injected tags (<system-reminder>, etc.) are stripped from session titles and message displays at all layers.
Inline-code URLs in chat are clickable. Bare filenames are searched in the project directory and linked if found.
Send-now button to bypass the pending-input queue. Cancel queued messages before they reach the CLI. Clear-input event support.
Auto-scroll disables when the scroll-to-bottom button is visible. Clicking the TopBar session tab scrolls to top.
Text selection enabled in shell output. Double-tap to accept ghost completion. Auth cookie uses SameSite=Lax to fix cross-site navigation issues. WKWebView reload loop eliminated.
/api/ask endpoint for sending prompts via Siri Shortcuts or Apple Watch.
Visual indicator in the message feed when Claude's context is being compacted.
Composer input drafts persist across HMR and server restarts.
Auth is disabled by default (COMPANION_AUTH=1 to enable). When disabled, all API calls skip authentication. Auto-auth on startup prevents login-page flash. Unified login page replaces separate token page.
Settings toggle between OpenRouter and direct Claude API for features like auto-naming and completion.
/api/images/* route with tilde expansion for serving local images (used for iMessage integration).
- Full documentation:
docs/(Mintlify — runcd docs && mint devto preview locally) - Protocol reverse engineering:
WEBSOCKET_PROTOCOL_REVERSED.md - Contributor and architecture guide:
CLAUDE.md
MIT

