Files
2026-06-09 19:50:15 +07:00

5.7 KiB
Raw Permalink Blame History

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 specDOCS/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:

cargo build                      # build all crates
cargo test                       # run all Rust tests
cargo test -p spacesh-core       # test one crate
cargo test -p spacesh-core <name># 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 ~48 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.