chore: seed repo with spec, plan, and design
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
# 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 <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 ~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.
|
||||
Reference in New Issue
Block a user