diff --git a/DOCS/RUNNING.md b/DOCS/RUNNING.md new file mode 100644 index 0000000..b6a7aaa --- /dev/null +++ b/DOCS/RUNNING.md @@ -0,0 +1,211 @@ +# spacesh — запуск и ручное тестирование + +Практический ран-бук: как собрать, запустить и руками проверить всё, что реализовано (M0+M1, M2, M3, M4). + +> Архитектура в двух словах: **демон `spaceshd`** владеет живыми PTY-сессиями и слушает Unix-socket; **GUI** (Tauri) и **CLI `spacesh`** — тонкие клиенты к нему. GUI и CLI поднимают демон лениво, если он не запущен. + +--- + +## 1. Предусловия + +- macOS (целевая платформа). +- Rust (стабильный) — `cargo --version`. +- Node ≥ 20 + npm — `node --version`, `npm --version`. +- Для GUI: системный WebView (есть в macOS из коробки). + +Проверка тулчейна: +```bash +cargo --version && node --version && npm --version +``` + +--- + +## 2. Сборка + +Из корня репозитория: +```bash +# Rust-крейты (демон, CLI, proto/core/pty) +cargo build + +# Фронт-зависимости (один раз) +cd app && npm install && cd .. +``` + +Бинарники после `cargo build`: +- демон → `target/debug/spaceshd` +- CLI → `target/debug/spacesh` + +Полный прогон автотестов (должно быть зелено): +```bash +cargo test --workspace +cd app && npm run build && cd .. # tsc + vite, проверяет фронт +``` + +--- + +## 3. Запуск GUI (основной путь) + +Запускает Tauri-окно; демон поднимется лениво. +```bash +cd app && npm run tauri dev +``` +Первый старт дольше (компиляция `src-tauri`). Откроется окно spacesh. + +Базовый цикл в GUI: +1. **`+ New workspace`** (слева) → визард: укажи папку, выбери раскладку-пресет (1 / 2↔ / 2↕ / 2×2 / …), назначь агента на каждую панель (shell / claude / codex / gemini) → **Create workspace**. +2. Появится грид панелей с терминалами. Печатай — ввод уходит в PTY, вывод рисуется. +3. Слева — список воркспейсов (группы, счётчик панелей, кольцо статуса, точка «не забыть»). +4. Справа — **Event Center** (лента событий статуса). + +--- + +## 4. Демон вручную (для CLI-тестов и отладки) + +GUI поднимает демон сам, но для CLI-сценариев удобно держать **изолированный** демон на отдельном сокете через `SPACESH_SOCK` (чтобы не мешать «боевому»): + +```bash +# терминал A — демон на временном сокете +SOCK=/tmp/spacesh-dev.sock +SPACESH_SOCK=$SOCK ./target/debug/spaceshd +# в логах: "spaceshd listening on /tmp/spacesh-dev.sock" +``` + +```bash +# терминал B — CLI к тому же сокету +SOCK=/tmp/spacesh-dev.sock +SPACESH_SOCK=$SOCK ./target/debug/spacesh status +``` + +Без `SPACESH_SOCK` сокет по умолчанию — `~/.spacesh/sock` (общий с GUI). + +Остановить демон: `spacesh shutdown` (к нужному сокету) или `Ctrl-C` в терминале A. + +### launchd (автоперезапуск, опционально) +```bash +./target/debug/spaceshd install-agent # ставит ~/Library/LaunchAgents/xyz.spacesh.daemon.plist (KeepAlive) +launchctl list | grep spacesh # проверить +# снять: +launchctl unload ~/Library/LaunchAgents/xyz.spacesh.daemon.plist +rm ~/Library/LaunchAgents/xyz.spacesh.daemon.plist +``` + +--- + +## 5. CLI `spacesh` + +Все команды бьют в демон (лениво стартуют, если не запущен; `notify` — best-effort, без старта). Глобальный флаг `--json` — сырой ответ. + +```bash +SOCK=/tmp/spacesh-dev.sock +S() { SPACESH_SOCK=$SOCK ./target/debug/spacesh "$@"; } + +S open /tmp/myproject # → workspace_id (w_…) +S status # таблица: воркспейсы · панели (id, агент, running/stopped, статус) +S status --json # сырой JSON +S new-surface # поднять панель (дефолт $SHELL) → surface_id (s_…) +S new-surface --cmd claude +S split --dir right # разбить панель соседней +S apply-preset --preset 2x2 --agent claude --agent shell --agent shell --agent shell +S notify --surface --state work # ← так же делают хуки агентов +S restart # перезапустить stopped-панель +S close +S group create --name production --color '#F4544E' +S set-meta --group --unread true +S close-workspace +S completions zsh # скрипт автодополнения +S shutdown +``` + +Пресеты: `1 2lr 2tb 2+1 1+2 3 2x2 4 2x3 2x4`. +Состояния (`--state`): `work wait done error idle`. + +--- + +## 6. Ручные сценарии по фичам + +Запусти демон на `SPACESH_SOCK=/tmp/spacesh-dev.sock` (см. §4), GUI — `npm run tauri dev` (для GUI-проверок). + +### M0 — живой терминал +1. GUI → `+ surface` (или `spacesh new-surface`). +2. Печатай `echo hi` → видишь вывод. Байты летают GUI↔демон↔PTY. + +### M1 — переживаемость / reattach +1. В панели запусти долгое: `top` (или агента). +2. **Закрой окно GUI** (демон остаётся жив): проверь `pgrep spaceshd` и `ls ~/.spacesh/sock`. +3. Снова `npm run tauri dev` → панель на месте, экран восстановлен из снапшота, `top` продолжает обновляться. + +### M2 — раскладки, воркспейсы, персист +1. Визард → пресет `2×2`, агенты на слоты → грид из 4 панелей. +2. Тяни **сплиттер** между панелями мышью — пропорции меняются. +3. Тулбар пресетов сверху — переразбивка активного воркспейса. +4. Закрой панель → дерево схлопывается. +5. **Холодный рестарт демона:** `spacesh shutdown` (или `pkill spaceshd`), затем снова открой GUI → воркспейсы и раскладка восстановлены с диска, панели показаны **`stopped`** с кнопкой **Restart** (autostart по умолчанию выкл). Жми Restart → панель поднимается. +6. Сайдбар: создай группу, перекрась, перетащи воркспейс (если drag включён), метка unread. + Состояние лежит в `~/.spacesh/state.json` (или своём, если задан `SPACESH_SOCK`-профиль — сокет отдельный, но state.json общий в `~/.spacesh`). + +### M3 — статусы +1. **Claude-панель:** `new-surface --cmd claude` (нужен установленный `claude` в PATH). Демон кладёт per-surface хуки в `~/.spacesh/hooks//` и прокидывает `CLAUDE_CONFIG_DIR`. Поработай в агенте: кольцо панели меняется **work → wait → done** (Stop→done, Notification→wait, UserPromptSubmit→work). Глобальный `~/.claude/settings.json` не трогается. +2. **shell-панель (zsh) — OSC 133:** `new-surface ` (zsh по умолчанию). Запусти команду: пока идёт — кольцо **work**; завершилась с 0 — **done**; `false` (exit 1) — **error**. (Интеграция инжектится через `ZDOTDIR`; bash/fish — на эвристике fallback.) +3. **Эмуляция без агента:** `spacesh notify --surface --state error` → кольцо краснеет, в `status` виден `error`. +4. **Нативные уведомления:** сверни/расфокусь окно GUI, доведи панель до `done/wait/error` → придёт macOS-уведомление (первый раз спросит разрешение). Клик по записи в **Event Center** фокусит панель. +5. **Авто-unread:** событие статуса в **неактивном** воркспейсе ставит ему синюю точку «не забыть»; выбор воркспейса снимает. + +### M4 — CLI +- `spacesh status --json` против живого демона; `spacesh notify` без демона → молча `exit 0`; `spacesh completions zsh` печатает скрипт. + +--- + +## 7. Где что лежит / сброс + +``` +~/.spacesh/ + sock # Unix-socket демона (или путь из $SPACESH_SOCK) + daemon.lock # single-instance лок + state.json # персист раскладок/воркспейсов/групп (M2) + hooks// # per-surface CLAUDE_CONFIG_DIR с settings.json (M3, чистится при close) + shellint// # per-surface ZDOTDIR с zsh OSC 133 (M3) +``` + +Полный сброс состояния (демон должен быть остановлен): +```bash +spacesh shutdown 2>/dev/null; pkill spaceshd 2>/dev/null +rm -rf ~/.spacesh # сбрасывает сокет, лок, state.json, hooks, shellint +``` + +--- + +## 8. Траблшутинг + +- **`daemon unavailable` / CLI висит:** сокет битый. `pkill spaceshd; rm -f ~/.spacesh/sock` (или свой `$SPACESH_SOCK`), повтори. +- **«another spaceshd is already running»:** уже есть живой демон на этом сокете — это норма; используй его или `spacesh shutdown`. +- **GUI не видит демон / пусто:** проверь, что GUI и демон на одном сокете (если задавал `SPACESH_SOCK` для демона — задай тот же для GUI: `SPACESH_SOCK=… npm run tauri dev`). +- **Статусы у claude не меняются:** проверь, что `claude` в PATH, и что в `~/.spacesh/hooks//settings.json` абсолютный путь к `spacesh` верный. Имена/формат хуков Claude Code дрейфуют по версиям — при несовпадении правится только `crates/spaceshd/src/hooks.rs`. +- **Нет уведомлений:** проверь разрешение macOS (Системные настройки → Уведомления → spacesh) и что окно действительно не в фокусе. +- **`npm run tauri dev` падает на компиляции:** прогони `cargo build -p spaceshd` отдельно, посмотри ошибку; затем `cd app && npm install`. + +--- + +## 9. Известные ограничения (на сейчас) + +- **Playwright/headless-браузер** видит только Vite-фронт (`npm run dev`, :1420) — Tauri-IPC там недоступен, живой daemon-флоу не тестируется. Полный e2e — только `npm run tauri dev` на дисплее. +- **OSC 133 — только zsh** (через `ZDOTDIR`); bash/fish работают на fallback-эвристике. +- **Клик по нативному уведомлению** не фокусит конкретную панель (клик по записи в Event Center — фокусит). +- **Event Center** — плоская лента (вкладки All/Unread/Errors пока без фильтра); живёт в памяти GUI, очищается при перезапуске GUI. +- **Статус эфемерен** (work/wait/done/error/idle) — не персистится; после холодного рестарта демона панель `stopped`, статус `idle`. +- Авторизация / личный кабинет / внешние нотификации (Telegram/MAX) / зум / поиск по скроллбэку / diff-вьюер / remote — **не реализованы** (M5/M6/auth, см. `DOCS/MAIN.md`). + +--- + +## 10. Быстрый smoke одной командой (без GUI) + +```bash +cargo build +SOCK=/tmp/spacesh-smoke.sock +SPACESH_SOCK=$SOCK ./target/debug/spaceshd & DPID=$! +sleep 1 +SPACESH_SOCK=$SOCK ./target/debug/spacesh open /tmp +SPACESH_SOCK=$SOCK ./target/debug/spacesh status +SPACESH_SOCK=$SOCK ./target/debug/spacesh shutdown +kill $DPID 2>/dev/null; rm -f $SOCK +``` +Ожидаемо: `open` печатает `w_…`, `status` показывает воркспейс, `shutdown` гасит демон.