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

76 lines
5.7 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 ~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.