# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## Status Pre-implementation. The repo currently contains **only the spec** — `DOCS/MAIN.md` (spacesh tech spec v0.1, in Russian). No source code, no `Cargo.toml`, no git history yet. Read `DOCS/MAIN.md` first; it is the single source of truth for what to build and why. When writing code, follow the crate layout, protocol, and milestones it fixes. ## What spacesh is A terminal-workspace for running multiple AI agents (Claude Code, Codex, Gemini, opencode, shell) in parallel on macOS. Architecture: a **backend daemon (`spaceshd`) owns the live PTY sessions**; the Tauri GUI and the `spacesh` CLI are thin clients that talk to it over one Unix-domain socket (`~/.spacesh/sock`). The daemon outliving the GUI is the core design bet — killing/updating the GUI must not kill a running agent. ## Architecture invariants These are load-bearing decisions from the spec — do not violate them when implementing: - **Daemon is the single source of truth.** GUI and CLI hold no state; they only send commands and subscribe to events. A click in the GUI and `spacesh focus s_8f3` from a script are the *same* `focus` command — never duplicate operational logic between GUI and CLI. - **One socket, one protocol.** Length-prefixed (`u32` BE + payload) JSON frames over async UDS (tokio). Envelope has three kinds: `req` (client→daemon, correlated by `id`), `res` (daemon→client, by `id`), `evt` (daemon→subscribers, push, no id). Serialization is isolated in `spacesh-proto` so JSON→MessagePack is a localized swap. - **Hybrid terminal parsing.** One PTY byte stream fans out to: (1) GUI `output` event → xterm.js renders; (2) `alacritty_terminal` grid in the daemon = authoritative screen model, used for status detection, scrollback search, OSC 133 last-command extraction, and reattach snapshots. The grid IS the snapshot source — no separate ring buffer. Display (xterm.js) and analysis (alacritty grid) are deliberately split. - **Reattach via snapshot.** On `attach`, daemon serializes the current grid → ANSI dump → GUI writes it into a fresh xterm.js instance for instant repaint, then live `output` follows. This is how the daemon architecture solves screen restoration. - **Status is pushed, not guessed.** State arrives as a `set_state` command (from agent hooks calling `spacesh notify`, OSC 133 for shells, pattern-matching fallback last). States: `work | wait | done | error | idle`. `$SPACESH_SURFACE_ID` is injected into each panel's env at spawn so hooks know which surface they report on. - **Extend through the bus.** New features = new commands/events/subscribers (e.g. Telegram/MAX notifications are a subscriber *inside* `spaceshd` listening to `state`/`exit`). The spine does not move. ## Planned code structure Cargo workspace + Tauri app (per spec §10): ``` crates/ spacesh-proto/ # protocol types, serde, length-prefix framing — shared by all spacesh-core/ # workspace/split model, alacritty_terminal grid, status engine — NO I/O, unit-testable spacesh-pty/ # PTY spawn/io (portable-pty), reader-loop, batching, resize, backpressure spaceshd/ # daemon: owns sessions, hosts socket, event fan-out, layout persist, launchd, notification subscribers spacesh-cli/ # thin socket client (clap) app/ src-tauri/ # Rust: socket client + bridge into webview (tauri 2) src/ # React/TS front (TerminalView, LayoutEngine, Sidebar, EventCenter, Wizard, Settings, socketBridge) ``` `spacesh-core` is deliberately I/O-free so the domain model and status engine are unit-testable in isolation. ## Build / run (once the workspace exists) No build files exist yet. After scaffolding, the conventional commands will be: ```bash cargo build # build all crates cargo test # run all Rust tests cargo test -p spacesh-core # test one crate cargo test -p spacesh-core # run a single test by name filter cargo run -p spaceshd # start the daemon cargo run -p spacesh-cli -- status --json cd app && npm install && npm run tauri dev # GUI dev (Tauri 2) ``` Verify the actual scripts against `Cargo.toml` / `app/package.json` once they are created — do not assume. ## Performance budgets (enforce in PTY/render paths) - Keypress → echo: < 16 ms under normal load. - PTY output batching: coalesce ~4–8 ms **or** ~16 KB, whichever first — never emit `output` per byte (a 50k-line build log must not yield 50k events). - Backpressure: bounded per-subscriber channel; on overflow drop/coalesce frames for that client, but always keep the authoritative daemon grid up to date. - Reattach repaint: < 100 ms on a typical grid. ## Implementation order (spec §12) M0 live terminal through socket (vertical slice) → M1 persistence + reattach + launchd → M2 layouts/workspaces/presets → M3 statuses (hooks, OSC 133, fallback, notifications UI) → M4 CLI → M5 features (zoom, Telegram/MAX notifications, scrollback search, CodeMirror 6 diff view) → M6 (post-v1) remote via SSH-tunneled socket. ## Explicitly out of v1 scope Token/limit accounting, legal/auth layer, embedded browser, full Monaco-class editor, agent-command guardrails. "Modifications" = external notifications (Telegram + MAX) only. Don't build these unless the scope decision is revisited. ## Conventions Per the user's global rules: English for all code, comments, and docs; camelCase vars/functions, PascalCase types, snake_case files/dirs, UPPER_CASE env vars. No hard-coded values — use env/config (`~/.spacesh/config.toml`); tokens via `env:VAR` references, never plaintext. Spec prose and the docs in `DOCS/` are Russian; keep that, but code stays English.