Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Changelog

## 2026-09-30

- [docs] Add the AI Working Agreement and context files (files: CLAUDE.md, CHANGELOG.md, docs/CONTEXT.md, docs/ARCHITECTURE.md, docs/DECISIONS.md, docs/TODO.md)

## 2026-09-29

- [docs] Add README screenshots and restructure the README (files: README.md, docs/screenshots/)
- [release] v1.1.0 (files: package.json)
- [ci] Fix the Windows lint and Linux e2e failures; publish a GitHub release on version tags (files: .github/workflows/)
- [settings] Add appearance options (theme, accent, palette, font) and the update check (files: src/main/update-check.ts, src/renderer/components/SettingsView.tsx)
- [platform] Add Windows, Linux distro packages, tiling compositors, and shared CLAUDE.md/skills (files: src/main/platform.ts, src/main/shared-config.ts, electron-builder.yml)
- [m5] Add packaging, the README, and shutdown hardening
- [m1-m4] Add the PTY grid with accounts, layouts, and persistence
53 changes: 53 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# AI Working Agreement

## Session start (always, in order)

1. Read `docs/CONTEXT.md` (project map + current state). Do NOT scan the repo.
2. Read the top 30 lines of `CHANGELOG.md` (recent changes).
3. Read `docs/TODO.md` only if the task is about planning or next steps.
4. Read other files ONLY when the task needs them. Open by path from the file map; do not glob/grep the whole repo unless the map has no answer.

## Do not re-read

- Skip any file listed under "Stable / don't reopen" in `docs/CONTEXT.md` unless the task touches it.
- Trust `docs/ARCHITECTURE.md` and `docs/DECISIONS.md`. Don't re-derive decisions already logged there. If one looks wrong, say so and propose a change; don't silently override.

## Context files (create if missing)

- `docs/CONTEXT.md`: one-page brief. Sections: Purpose, Stack, Run/build/test commands, Directory map (path: one-line role), Conventions, Stable/don't reopen, Known gotchas, Current focus.
- `docs/ARCHITECTURE.md`: modules, data flow, external services, env vars (names only, no secrets).
- `docs/DECISIONS.md`: append-only ADR log. Format: `YYYY-MM-DD | Decision | Why | Alternatives rejected`.
- `docs/TODO.md`: Now / Next / Later / Blocked.
- `CHANGELOG.md`: newest first. Format: `## YYYY-MM-DD` then bullets `- [area] what changed (files: a.ts, b.ts)`. Keep entries to 1 line each.

## After every task (mandatory)

1. Add a CHANGELOG entry: what changed, files touched.
2. Update `docs/CONTEXT.md` if: files/dirs added/removed, commands changed, a new gotcha was found, or "Current focus" changed.
3. Append to `docs/DECISIONS.md` if a non-obvious choice was made.
4. Update `docs/TODO.md` (move/close/add items).
5. Keep these files short. Compress old entries; never let CONTEXT.md exceed ~150 lines.

## Working rules

- Terse output. Commands/code first, minimal prose.
- Before editing: state the plan in ≤5 bullets. Then do it.
- Smallest diff that solves the problem. No unrelated refactors.
- Match existing conventions in `docs/CONTEXT.md`.
- Never invent files, APIs, or env vars. If unsure, ask or check the map.
- Run tests/lint/build commands from CONTEXT.md before declaring done; report result.
- Never commit secrets. Reference env var names only.
- If context files are stale or contradict the code, fix them and note it in CHANGELOG.

## When context is missing

If `docs/CONTEXT.md` doesn't exist: explore once, generate all context files above, then proceed. Never repeat this exploration in later sessions.

## End-of-session handoff

Finish with:

- Done: (1–3 bullets)
- Changed files: (list)
- Next: (1–3 bullets)
- Open questions/blockers: (if any)
61 changes: 61 additions & 0 deletions docs/ARCHITECTURE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# Wraithgrid: Architecture

## Processes
- **Main** (`src/main`): owns the filesystem, child processes, and network. Sole writer of `config.json`.
- **Preload** (`src/preload`): exposes a fixed typed API over `contextBridge`; bundled into one file.
- **Renderer** (`src/renderer`): React UI, sandboxed, `contextIsolation` on, `nodeIntegration` off.
- **Shared** (`src/shared`): types, zod schemas, IPC channel names, pure helpers used by all three.

## Main modules
| Module | Role |
|---|---|
| `index.ts` | App lifecycle, BrowserWindow, single-instance lock, PATH merge, composition root |
| `ipc.ts` | `registerIpc(deps)`: one handler per channel, zod-validated input |
| `pty-manager.ts` | `PtyManager`: spawn/write/resize/kill; `KillPolicy` strategy (POSIX group signal vs Windows ConPTY list) |
| `pane-env.ts` | Pane environment; `CLAUDE_CONFIG_DIR` only for claude panes |
| `config-store.ts` | `ConfigStore`: parse/migrate/sanitize, atomic write, quarantine to `config.bad-<ts>.json` |
| `claude-detect.ts` | Locate `claude` on PATH or via override; read version |
| `platform.ts` | Desktop/window-chrome detection, default shell, extra bin dirs, spawn command, login-shell PATH |
| `shared-config.ts` | Symlink/junction/hardlink shared CLAUDE.md + skills; backups, never deletes |
| `update-check.ts` | `UpdateChecker`: GitHub Releases API, cached, report only |
| `paths.ts` | Config file path, accounts root, `~` resolution, `isStrictlyInside` guard |

## Renderer
- `main.tsx` builds `Services` (API bridge, PtyBus, store) and provides them via context.
- `app/store.ts` (zustand): config + runtime pane state, all actions; persists via `config:set`.
- `lib/pty-bus.ts`: fans out `pty:data`/`pty:exit` events to panes.
- `lib/status.ts`: derives running/idle/awaiting-approval/login from output (ANSI-stripped).
- `layout/tree.ts`: pure split-tree ops; `presets.ts`, `focus.ts` on top.
- `lib/scheduling.ts`: `StaggeredQueue` starts restored panes 150 ms apart (NFR-2).

## Data flow
1. Launch → main loads `config.json` → renderer `config:get` → store hydrates.
2. Pane start → `pty:create {paneId, accountId, cwd, args}` → main resolves account from its own
config → `buildPaneEnv` → `PtyManager.spawn` → `pty:data`/`pty:exit` events back.
3. Keystrokes → `pty:write`; resize → `pty:resize`; close → `pty:kill` (process tree).
4. Any config change → store → `config:set` (validated) → atomic write.
5. Quit → every pane's process tree killed.

## IPC surface
Full list in `src/shared/ipc-channels.ts`; schemas in `src/shared/ipc-contract.ts`. Groups: pty,
config, dialog, window, claude detect, account dir create/delete, openExternal (http/https only),
shared apply, app info, update check.

## External services
- GitHub Releases API for `cachewraith/wraithgrid` (optional update check; fixed URL).
- Nothing else. No telemetry.

## Persistence
- `config.json` in userData: `~/.config/wraithgrid/` (Linux), `%APPDATA%\wraithgrid\` (Windows).
- Account config dirs default under `~/.wraithgrid/accounts/`. Wraithgrid does not read inside them.

## Env vars (names only)
- Read by app: `WRAITHGRID_USER_DATA_DIR` (override userData, tests), `WRAITHGRID_DEVTOOLS`
(`1` opens devtools), `ELECTRON_RENDERER_URL` (dev server), `TERM`, `PATH`, `ComSpec`.
- Set by app: `CLAUDE_CONFIG_DIR` (per claude pane), `WRAITHGRID_RESOLVING_ENVIRONMENT=1`
(during login-shell PATH probe).
- Build scripts: `WRAITHGRID_BUILD_IMAGE`, `WRAITHGRID_NODE_VERSION` (`scripts/dist-linux-docker.sh`).

## CI/CD
- `build.yml`: push/PR → typecheck, lint, unit, e2e (Linux, xvfb), package (Ubuntu 22.04 + Windows).
- `release.yml`: `v*.*.*` tag → verify tag = package.json → build → SHA256SUMS → `gh release create`.
76 changes: 76 additions & 0 deletions docs/CONTEXT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# Wraithgrid: Context

## Purpose
Electron desktop app that runs many official `claude` CLI sessions side by side, one per pane,
each with its own account (`CLAUDE_CONFIG_DIR`) and project folder. Windows + Linux. v1.1.0.

## Stack
Electron 44, electron-vite 5, React 19, TypeScript 6, zustand, zod 4, @xterm/xterm 6, node-pty,
react-resizable-panels, @dnd-kit. Tests: Vitest (unit), Playwright (Electron e2e). pnpm 11, Node ≥22.12.

## Commands
- `pnpm i` — install; rebuilds node-pty for Electron (allow electron/esbuild/node-pty builds)
- `pnpm dev` — run with hot reload
- `pnpm typecheck` — tsc over node + web projects
- `pnpm lint` — ESLint + Prettier check (`pnpm format` to fix)
- `pnpm test` — Vitest unit tests (`tests/unit`)
- `pnpm test:e2e` — build, then Playwright (`tests/e2e`; needs a display, CI uses xvfb-run)
- `pnpm build` — production build into `out/`
- `pnpm dist:linux:portable` — Linux packages in Ubuntu 22.04 Docker; `pnpm dist:win` on Windows
- Release: `npm version <patch|minor|major> -m "chore: release %s" && git push --follow-tags`

## Directory map
- `src/main/index.ts`: app entry; window, PATH resolution, wires ConfigStore/PtyManager/IPC
- `src/main/ipc.ts`: all IPC handlers; validates every payload with zod contracts
- `src/main/pty-manager.ts`: spawns/kills pane processes; per-OS KillPolicy
- `src/main/pane-env.ts`: builds a pane's env (sets `CLAUDE_CONFIG_DIR` for claude panes)
- `src/main/config-store.ts`: config.json load/save, atomic write, bad-file quarantine
- `src/main/claude-detect.ts`: finds the `claude` binary and version
- `src/main/platform.ts`: OS/desktop differences as pure functions (chrome, shell, PATH, spawn)
- `src/main/shared-config.ts`: link one CLAUDE.md + skills/ into every account
- `src/main/update-check.ts`: GitHub Releases check (fixed URL, report only)
- `src/main/paths.ts`: config/account paths, `isStrictlyInside` guard
- `src/preload/index.ts`: typed bridge; renderer never sees ipcRenderer
- `src/shared/ipc-channels.ts`: the complete IPC channel list
- `src/shared/ipc-contract.ts`: zod schemas for IPC args/events
- `src/shared/schema.ts`: persisted config schema, migrate/sanitize/parse
- `src/shared/types.ts`: domain types, themes, accents, palettes, fonts
- `src/shared/args.ts`, `src/shared/paths.ts`: launch-args parsing, pure path helpers
- `src/renderer/main.tsx`: composition root (builds Services)
- `src/renderer/app/store.ts`: zustand store, all app actions (largest file)
- `src/renderer/app/{App,services,shortcuts}`: root view, DI context, key matching
- `src/renderer/components/`: UI (PaneGrid, Pane, Terminal, dialogs, Settings/Accounts views)
- `src/renderer/layout/`: pure split-tree ops, presets, directional focus
- `src/renderer/lib/`: PtyBus, status detection, terminal themes, ANSI strip, scheduling
- `src/renderer/styles/`: design tokens + base CSS
- `tests/unit/`, `tests/e2e/`, `tests/fixtures/fake-claude.sh`: tests + fake CLI
- `scripts/`: Docker Linux build, multi-distro package smoke test
- `build/`: icons; `.github/workflows/`: build.yml (CI), release.yml (tag → release)
- `docs/REQUIREMENTS.md`: original requirements (FR-*/NFR-* IDs); `docs/screenshots/`

## Conventions
- Conventional commits: `type(scope): summary`.
- Prettier + ESLint; no semicolons, single quotes (see existing files).
- `@shared` alias → `src/shared` (vite, vitest).
- Main-process logic as pure functions of `(platform, env)` / injected deps so it's unit-testable.
- Every IPC channel listed in `ipc-channels.ts` with a zod schema in `ipc-contract.ts`; main
resolves accounts from its own config, never trusts renderer paths.
- Dependencies pinned to exact versions; GitHub Actions pinned by SHA.
- Short file-top comment explaining *why* a module exists.

## Stable / don't reopen
- `docs/REQUIREMENTS.md`, `docs/ui-prototype.dc.html`, `docs/screenshots/`
- `electron-builder.yml`, `scripts/*.sh`, `build/`
- `src/renderer/lib/{ansi,ids,scheduling}.ts`, `src/renderer/layout/presets.ts`

## Known gotchas
- node-pty compiles against the host glibc: ship Linux builds from Ubuntu 22.04 (portable script / CI).
- Launcher-started app lacks shell PATH: main asks the login shell once (skipped when `TERM` set).
- Windows `claude.cmd` runs through cmd.exe: args with `& | < > ^ % "` are refused.
- Sandboxed preload can't require node_modules: preload is bundled (`externalizeDeps: false`).
- Single-instance lock: a second launch exits.
- Tests use `WRAITHGRID_USER_DATA_DIR` for a throwaway config dir.
- AppImage on Ubuntu 24.04+/Kali blocked by AppArmor; prefer .deb.

## Current focus
v1.1.0 released (appearance settings, update check, Windows + distro packages). No active task.
15 changes: 15 additions & 0 deletions docs/DECISIONS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Decisions

`YYYY-MM-DD | Decision | Why | Alternatives rejected`

Entries dated 2026-09-29 are reconstructed from the code and commit history during the 2026-09-30 bootstrap.

- 2026-09-29 | Run the official `claude` CLI in a PTY, one `CLAUDE_CONFIG_DIR` per account | Separate logins without touching credentials | Custom client; proxying or copying tokens
- 2026-09-29 | Electron + React + xterm.js + node-pty | Full terminal fidelity, cross-platform | Native per-OS apps; web-only terminal
- 2026-09-29 | Sandboxed renderer; fixed IPC list with zod-validated payloads; main resolves accounts itself | Renderer can't reach the FS/processes except through checked calls | Exposing ipcRenderer; trusting renderer-supplied paths
- 2026-09-29 | Config as one JSON file, atomic writes, corrupt file quarantined to `config.bad-<ts>.json` | Simple, inspectable, crash-safe | SQLite; electron-store
- 2026-09-29 | Build Linux packages on Ubuntu 22.04 (Docker/CI) | node-pty links host glibc; 2.35 covers target distros | Building on Arch/Fedora
- 2026-09-29 | Shared CLAUDE.md/skills via links; conflicting files renamed to `.wraithgrid-backup` | Opt-in sharing without data loss or reading account files | Copying files; deleting conflicts
- 2026-09-29 | Update check reports only, from a hardcoded GitHub URL | Works for every package type; no SSRF from config | Auto-updater (electron-updater)
- 2026-09-29 | Platform logic as pure functions of `(platform, env)` | Unit-testable on any OS | Branching on `process.platform` inline
- 2026-09-30 | Adopt the AI Working Agreement (`CLAUDE.md`) + docs context files | Cut per-session re-exploration | None
14 changes: 14 additions & 0 deletions docs/TODO.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# TODO

## Now
- (none)

## Next
- (none recorded)

## Later
- macOS support (REQUIREMENTS §4 lists it; only Windows + Linux ship today)
- Configurable shortcuts (REQUIREMENTS §7.5 "configurable later")

## Blocked
- (none)