diff --git a/CLAUDE.md b/CLAUDE.md index 4f3edb1..63f5b95 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -4,13 +4,15 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co ## Project Status -**Planning stage — no code yet.** The `docs/` folder holds the authoritative spec; there is no `src/`, `public/`, or `package.json` yet. Read these before implementing: +**Built and working — v0.1 (core) + v0.2 (multi-tab) + v0.3 (Claude Code cockpit) shipped.** `src/` (TypeScript backend), `public/` (esbuild frontend), and tests all exist; 212 tests pass. v0.3 work happens on the `v0.3-cockpit` branch. Key docs: -- `docs/TECH_DOC.md` — design & rationale (the **why**). Authoritative for protocol (§4), session model (§5.2), security (§7), acceptance criteria (§8). -- `docs/ARCHITECTURE.md` — development guide (the **how**). Module contracts at **function-signature level**, dependency rules, build order (§6 S1–S8), and the invariant checklist (§8). -- `docs/PLAN.md` — the task plan. Dependency-ordered **waves W0–W5**, fine-grained **tasks T1–T21** for multi-agent parallelism (each task has `Owns:` / `Depends:`; per-task model & isolation in §4). -- `docs/PROGRESS_LOG.md` — the **memory file** (cross-session). Orchestrator-owned; subagents return their entry rather than writing it. See workflow below. -- `.claude/agents/` — project subagents: `module-builder` (TDD-implements one task), `module-reviewer` (read-only review). +- `docs/TECH_DOC.md` — design & rationale (the **why**). Protocol (§4), session model (§5.2), security (§7), acceptance (§8). +- `docs/ARCHITECTURE.md` — function-signature-level contracts, dependency rules, the **invariant checklist (§8)**. On conflict, ARCHITECTURE wins on *how*; the M*/L* anchors are cross-validated fixes. +- `docs/PROGRESS_LOG.md` — the **memory file** (cross-session). Read it first; it's the running record of what shipped (v0.1 T1–T21, then v0.2/v0.3 feature entries). Orchestrator-owned. +- `docs/PLAN.md` (v0.1 task plan) and `~/.claude/plans/shimmering-wondering-island.md` (v0.3 cockpit plan). +- `.claude/agents/` — project subagents: `module-builder`, `module-reviewer` (for parallel dev). + +**v0.3 additions** (cockpit): Claude Code **hooks → live status** per tab (`POST /hook`, `npm run setup-hooks`); **remote approve/reject** (held `POST /hook/permission`); **tmux keepalive** (`USE_TMUX`); plus FE: themes, search, dashboard, QR, PWA, new-tab-in-cwd. The server gained a hook side-channel but the terminal stream is still a byte-shuttle. **Language decision: TypeScript (`.ts`), not `.js`** — ARCHITECTURE §0 records this divergence from TECH_DOC's original `.js` filenames. Wherever the two docs conflict, ARCHITECTURE wins on *how* (it was cross-validated and corrected); TECH_DOC wins on *why/scope*. @@ -61,7 +63,7 @@ npm start # listens on 0.0.0.0:3000 npm test # unit tests (vitest, all modules) ``` -Config is via env vars only (no hardcoding): `PORT`, `SHELL_PATH`, `BIND_HOST`, `IDLE_TTL`, `SCROLLBACK_BYTES`, `MAX_PAYLOAD_BYTES`, `ALLOWED_ORIGINS`. Note `allowedOrigins` is derived from the host's network-interface IPs (not from `BIND_HOST` — `0.0.0.0` is never a valid Origin); see ARCHITECTURE §3.1. +Config is via env vars only (no hardcoding): `PORT`, `SHELL_PATH`, `BIND_HOST`, `IDLE_TTL`, `SCROLLBACK_BYTES`, `MAX_PAYLOAD_BYTES`, `USE_TMUX` (1/0/auto), `ALLOWED_ORIGINS`. Note `allowedOrigins` is derived from the host's network-interface IPs (not from `BIND_HOST` — `0.0.0.0` is never a valid Origin); see ARCHITECTURE §3.1. ## Architecture (the parts that span files) diff --git a/README.md b/README.md index 58beb41..47e365a 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,15 @@ Sessions survive disconnects: the shell (and whatever's running in it) keeps goi > ⚠️ **This hands a full shell to anyone who can reach the port.** LAN-only, no authentication. **Never** port-forward or tunnel it to the public internet. See [Security](#security). +## Features + +- **Multi-tab** — each tab is an independent shell session. Tabs auto-name to the current folder (double-click to rename), show a connection dot (🟢/🟡/🔴) and an unread-output dot, and can be drag-reordered. `+` opens a new tab in the active tab's directory. +- **Claude Code cockpit** (with hooks, see below) — each tab shows Claude's status (⚙ working / ⏳ needs approval / ✓ idle), sends a browser notification when it needs you, and lets you **tap Approve / Reject** on a tool request with no typing. +- **tmux keepalive** (optional) — run the shell inside tmux so sessions survive a server/host restart, not just a disconnect. +- **Mobile + desktop shortcut bar** — one-tap Esc / Esc·Esc / ⇧Tab / arrows / Enter / ^C / ^O / ^T / ^B / Tab / `/` (the Claude Code keys a phone keyboard can't produce). +- **Session keepalive + replay** — the PTY keeps running across disconnects; reconnect replays a ~2 MB scrollback ring buffer. +- **Toolbar** — 🔍 scrollback search · ⚙ themes & font size · ▦ all-sessions dashboard · 📱 QR connect (scan to open on another device). Clickable links. Installable as a PWA. + ## Requirements - Node.js ≥ 18 (developed on v24) @@ -34,6 +43,23 @@ ipconfig getifaddr en0 For frontend development, run `npm run dev:web` (esbuild --watch) alongside `npm start`. +## Claude Code cockpit (optional) + +To see Claude's status per tab and approve/reject tool calls from your phone, install the hooks once: + +```bash +npm run setup-hooks # adds http hooks to ~/.claude/settings.json (backs it up) +# npm run setup-hooks -- --remove # to uninstall +``` + +Then run `claude` inside a tab. The hooks POST to the server (loopback only) when Claude starts/stops/needs permission; the tab badge updates live, and a `PermissionRequest` shows an **Approve / Reject** bar. The hooks are a no-op outside web-terminal (they curl `$WEBTERM_HOOK_URL`, which is only set in spawned shells), so they're safe to leave installed. + +**tmux keepalive** — run with `USE_TMUX=1` (or just have `tmux` on PATH; it auto-detects) to keep sessions alive across a server restart: + +```bash +USE_TMUX=1 npm start +``` + ## Configuration All via environment variables (no hardcoding): @@ -46,6 +72,7 @@ All via environment variables (no hardcoding): | `IDLE_TTL` | `86400` (s) | reclaim a detached session after this idle time | | `SCROLLBACK_BYTES` | `2097152` | per-session replay ring buffer (bytes) | | `MAX_PAYLOAD_BYTES` | `1048576` | max single WS frame | +| `USE_TMUX` | `auto` | `1`/`0`/`auto` — run the shell inside tmux (keepalive across restart); `auto` = on if `tmux` is on PATH | | `ALLOWED_ORIGINS` | (derived) | extra allowed WS origins, comma-separated | `allowedOrigins` is **derived from the host's network-interface IPs** (plus `localhost` and anything in `ALLOWED_ORIGINS`) — never from `BIND_HOST`, since `0.0.0.0` is never a real browser Origin.