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
34 changes: 18 additions & 16 deletions .agents/docs/agent-adapters.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,19 +49,21 @@ Every provider is a folder under `src/supervisor/agents/<kind>/` with the same i

Opening two provider folders side-by-side answers "what does this provider do differently" by file-name alignment alone.

Model/effort lists below are the **statically declared defaults**. Several providers ship `models: []` / `efforts: []` and fill them at runtime from a capabilities probe (Codex/Gemini/Copilot/Grok/OpenCode) — the listed values are illustrative, not authoritative. Read the provider's `detection.ts` for the live source of truth.

| Provider | Models | Efforts | Live Input | Structured Session |
| ------------ | ------------------------------------------------------ | ---------------------------------------- | --------------------- | ---------------------- |
| Claude | opus-4-8, fable-5, opus-4-7, opus-4-6, sonnet, haiku | low, medium, high, xHigh, max, ultracode | terminal | No |
| Codex | (probed dynamically via app-server) | (probed dynamically) | terminal / GUI server | Yes (stdio app-server) |
| Gemini | (probed dynamically via ACP) | (probed dynamically) | terminal | No |
| Copilot | (probed via ACP) | (probed via ACP) | terminal | Yes (ACP) |
| Cursor | auto, composer-\*, GPT/Opus/Sonnet variants | (embedded in model name) | terminal | No |
| Grok | grok-build (probed via ACP) | (none) | terminal | Yes (ACP) |
| OpenCode | (probed dynamically via SDK) | (probed dynamically) | terminal / GUI server | Yes (SDK server) |
| Antigravity | auto (managed by `agy`) | (none) | terminal | No |
| Command Code | Kimi/Claude/GPT/Gemini/GLM/… (static, `--list-models`) | (none) | terminal | No |
Model/effort lists below are the **statically declared defaults**. Several providers ship `models: []` / `efforts: []` and fill them at runtime from a capabilities probe (Codex/Gemini/Copilot/Grok/OpenCode, and Cursor via its CLI `--list-models`) — the listed values are illustrative, not authoritative. Read the provider's `detection.ts` for the live source of truth.

The **Structured Session** column reflects whether the adapter implements `createStructuredSession` (i.e. supports a `"gui"` presentation mode); it is not a model-list default and is authoritative.

| Provider | Models | Efforts | Live Input | Structured Session |
| ------------ | ------------------------------------------------------------------------ | ---------------------------------------- | --------------------- | ---------------------- |
| Claude | opus-4-8, fable-5, opus-4-7, opus-4-6, sonnet, haiku | low, medium, high, xHigh, max, ultracode | terminal | Yes (SDK) |
| Codex | (probed dynamically via app-server) | (probed dynamically) | terminal / GUI server | Yes (stdio app-server) |
| Gemini | (probed dynamically via ACP) | (probed dynamically) | terminal | Yes (ACP) |
| Copilot | (probed via ACP) | (probed via ACP) | terminal | Yes (ACP) |
| Cursor | auto, composer-\*, GPT/Opus/Sonnet variants (probed via `--list-models`) | (embedded in model name) | terminal | Yes (ACP) |
| Grok | grok-build (probed via ACP) | (none) | terminal | Yes (ACP) |
| OpenCode | (probed dynamically via SDK) | (probed dynamically) | terminal / GUI server | Yes (SDK server) |
| Antigravity | auto (managed by `agy`) | (none) | terminal | No |
| Command Code | Kimi/Claude/GPT/Gemini/GLM/… (static, `--list-models`) | (none) | terminal | No |

## Adding a New Provider — Full Checklist

Expand Down Expand Up @@ -116,7 +118,7 @@ forgotten.

### 3. Shared contracts

- [ ] `src/shared/contracts/agentInstance.ts` — add the kind to `KNOWN_AGENT_DRIVERS`.
- [ ] `src/shared/contracts/agentInstance.ts` — add the kind to `KNOWN_AGENT_DRIVERS`. (Note: this map currently has **no runtime readers** — the shipped Grok provider omits its entry with no observable effect — so today this is for completeness, not feature-gating.)

### 4. Renderer provider — `src/renderer/components/providers/<kind>/`

Expand Down Expand Up @@ -173,7 +175,7 @@ The codebase is provider-agnostic by design (targeting 5-10 providers). Each pro
- WSL projects are detected via `ProjectLocation.kind === "wsl"`.
- Commands are wrapped: `wsl.exe -d <distro> --cd <linuxPath> -- <command>`.
- `batchWslCommandsAsync()` combines multiple commands into one `wsl.exe` invocation to avoid ~800-1000ms per-spawn overhead.
- Shell detection (`resolveWslShellPath`) is cached per distro with `/bin/sh` fallback.
- Shell detection (`resolveWslShellPath`) is cached per distro with a `/bin/bash` fallback (chosen over `/bin/sh` so rc files — nvm/fnm/asdf — still get sourced).
- Agent install detection runs per-environment (Windows and each active WSL distro independently).

## Hook Runtime Resolution
Expand All @@ -190,7 +192,7 @@ Three layers, in order of cost:
2. **Login-shell probe.** macOS GUI apps don't inherit the user's interactive PATH (no Homebrew, no nvm) — so on POSIX we spawn `$SHELL -lic` with sentinel markers (`__LC_NODE_PATH__:`, `__LC_NODE_VERSION__:`) to extract the user's `node` past any rc-file noise. On Windows, Electron inherits PATH from the registry already, so `where.exe node` is enough. If the binary version is ≥ `MIN_ACCEPTED_NODE_MAJOR`, that's our pick.
3. **Background install.** When 1 + 2 both miss, the resolver fires `installNativeRuntime` (download → SHA256-verify → `tar -xJf` for `.tar.xz` / `tar.exe -xf` for `.zip`) and immediately returns null. The current install pass falls back to `ELECTRON_RUN_AS_NODE=1`; next supervisor boot picks up the managed runtime via the fast path.

Result is memoized for the supervisor lifetime (one promise shared across all 5 providers) and cleared on restart. `resolveNativeNode` is the public entry point; `managedNodePath` is exported for tests.
Result is memoized for the supervisor lifetime (one promise per base dir, shared across whatever providers resolve against that dir) and cleared on restart. `resolveNativeNode` is the public entry point; `managedNodePath` is exported for tests.

### WSL — `src/supervisor/wsl/runtime/index.ts`

Expand Down
10 changes: 5 additions & 5 deletions .agents/docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@
## Layers

- `src/main/`: Electron shell (`main.ts`), context-isolated preload bridge (`preload.ts`), SQLite database (`db.ts`, `db.schema.ts` via Drizzle ORM + better-sqlite3).
- `src/supervisor/`: Forked Node process owning all agent PTY sessions, git operations, terminal log persistence, agent status caching, and commit message generation. Entry point: `index.ts` (IPC dispatcher with Zod-validated payloads).
- `src/renderer/`: React 19 + HeroUI v3 + xterm.js. Zustand stores for state, persisted to SQLite via the preload bridge.
- `src/supervisor/`: Forked Node process owning all agent PTY sessions, git operations, terminal log persistence, agent status caching, and commit message generation. Entry point: `index.ts` (IPC dispatcher that trusts pre-validated payloads — Zod validation runs caller-side in the main process before dispatch, not in the supervisor).
- `src/renderer/`: React 19 + HeroUI v3 + xterm.js. Zustand stores for state; a couple persist to SQLite via the preload bridge (`appStore`, `threadTodoDockStore`), several persist to `localStorage`, and many are ephemeral — see the State Management table for the per-store breakdown.
- `src/shared/`: Zod schemas, TypeScript types, IPC contracts (`ipc.ts`), and pure helpers (ANSI stripping, WSL path utilities, worktree path computation, theme resolution, agent status filtering).

## IPC Architecture

The main process forks the supervisor as a child process. Communication is UUID-keyed request/reply over `process.send()` + `process.on("message")`. The supervisor also emits fire-and-forget events (thread output, state changes, agent statuses) that the main process forwards to the renderer via `ipcRenderer`.
The main process forks the supervisor as a child process. Communication is UUID-keyed request/reply over `process.send()` + `process.on("message")`. The supervisor also emits fire-and-forget events (thread output, state changes, agent statuses) that the main process forwards to the renderer via `webContents.send` (received in the renderer through `ipcRenderer.on`).

The preload bridge (`window.lightcode`) wraps all IPC into typed async methods defined by the `LightcodeBridge` interface in `src/shared/ipc.ts`.
The preload bridge (`window.lightcode`) wraps all IPC into typed async methods defined by the `LightcodeBridge` type, declared in `src/shared/ipc/bridge.ts` and re-exported via the `src/shared/ipc.ts` barrel.

## State Management

Expand Down Expand Up @@ -59,7 +59,7 @@ Native modules (`node-pty`, `better-sqlite3`, `electron`) are excluded from bund

## Database

SQLite via Drizzle ORM (`src/main/db.ts`). Tables: `projects`, `threads`, `appState`, `threadRuntimeItems`, `threadContextUsage`, `threadCompletedTurns`. The renderer reads/writes through preload bridge methods (`dbGetProjects`, `dbUpsertThread`, `dbSyncAll`, etc.). Zustand persistence uses a custom `dbStorage` backend.
SQLite via Drizzle ORM (`src/main/db.ts`, `db.schema.ts`). Drizzle tables: `projects`, `threads`, `appState`, `projectNotes`, `threadRuntimeItems`, `threadContextUsage`, `threadCompletedTurns`. Plus `usage_events`, created via raw `CREATE TABLE` DDL in `db.ts` rather than the Drizzle schema. The renderer reads/writes through preload bridge methods (`dbGetProjects`, `dbUpsertThread`, `dbSyncAll`, etc.). Zustand persistence uses a custom `dbStorage` backend.

## Git Integration

Expand Down
10 changes: 6 additions & 4 deletions .agents/docs/editing-rules.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,10 @@ const getProjectThreads = createArrayKeyedMap((threads: Thread[]) =>
buildProjectThreadsMap(threads, (t) => !t.archived),
);

// selector — O(1) after the first caller for a given array identity
const threads = getProjectThreads(allThreads).get(projectId) ?? EMPTY_THREADS;
// selector — O(1) after the first caller for a given array identity.
// The returned getter takes (array, key) and returns the value directly;
// the internal Map's .get is not exposed to callers.
const threads = getProjectThreads(allThreads, projectId) ?? EMPTY_THREADS;
```

Precedent: `state/derivations.ts` (`createArrayKeyedMap`), consumed by `hooks/uiSelectors.ts` and `state/gitSelectors.ts`.
Expand Down Expand Up @@ -117,7 +119,7 @@ A component that renders a tab strip, a file tree, or a PR card often ends up su
Fix it by splitting the component so each subcomponent subscribes only to what **it** renders:

- `FileEditorPane` → `TabStripHeader` (tab list) + `EditorBody` (active buffer content).
- `ProjectTreeView` → `TreeChildren` (per-directory entries) + `TreeEntryRow` (per-path flags) + `TreeSearchBar` (debounced query).
- `ProjectTreeView` → `TreeChildren` (per-directory entries) + `TreeEntryRow` (per-path flags); the debounced search query is owned by the `useProjectTree` hook.
- `GitReviewSidebar` → `PrSection(prKey)` + per-file `FileRow(path)`.

The row component's props are the **identifier** (`path`, `key`, `id`) and maybe stable callbacks — never the entity payload.
Expand Down Expand Up @@ -165,7 +167,7 @@ If a child needs a store value, the child reads it via a hook. Do not pass `sele
## Vite Configuration

- Use Vite 8 Rolldown-native config (e.g. `rolldownOptions`) over older Rollup-first patterns.
- Manual chunks are defined for xterm, git-diff, ui (HeroUI + React Aria), framework (React + Zustand + Zod), and vendor.
- Manual chunks are defined for xterm, git-diff, monaco, shiki, ui (HeroUI + React Aria), framework (React + Zustand + Zod), and vendor.

## Cleanup

Expand Down
6 changes: 3 additions & 3 deletions .agents/docs/ui-patterns.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ Match the canonical dialog look — do not restyle. Reference: `CreatePrModal`,

## ACP Composer Behavior

- **Inline file mentions stay text-first, then serialize to structured segments.** `MentionInput` + `serializeMentions` accept raw `@path` tokens, so repo-relative references like `@.agents/docs/ui-patterns.md` become `{ kind: "file" }` prompt segments on submit without requiring a picker chip.
- **Inline file mentions stay text-first, then serialize to structured segments.** `MentionInput` + the `serializeMentions.ts` helpers (`serializeToSegments` / `flattenSegments`) accept raw `@path` tokens, so repo-relative references like `@.agents/docs/ui-patterns.md` become `{ kind: "file" }` prompt segments on submit without requiring a picker chip.
- **ACP resource paths resolve from the active project root.** Relative file mentions and attachments are normalized before ACP conversion so Windows, WSL, spaces, and `file://` URIs stay valid.
- **GUI ACP threads support steering via follow-up submits while working.** A new submit during `working` interrupts the active turn, queues the follow-up as structured `{ prompt, segments, config }`, and drains queued turns FIFO once the session returns to `idle` / `needs_reply`.
- **Terminal presentation stays PTY-owned.** Do not add queueing or fake stop/steering UI to terminal-native threads; the terminal surface remains the source of truth there.
Expand Down Expand Up @@ -94,8 +94,8 @@ Three modes: light, dark, system. Resolved via `useResolvedAppearance()` hook in

## Layout

- **AppShell**: Collapsible sidebar (220-500px, collapsed to 48px icon rail) + main content + optional right panel (320-700px). Drag-to-resize with localStorage persistence.
- **SplitPaneContainer**: Horizontal multi-pane for side-by-side threads (1-3 panes, equal initial distribution, 15% minimum each).
- **AppShell**: Collapsible sidebar (240-500px, collapsed to 48px icon rail) + main content + optional right panel (320-1100px). Drag-to-resize with localStorage persistence.
- **SplitPaneContainer**: Recursive horizontal/vertical multi-pane tree for side-by-side threads (unbounded pane count, equal initial distribution, 15% minimum each).
- Resizable panels use **local state only** (not Zustand) to avoid resize lag.

## Module Loading
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

<p align="center">
<strong>One window for all your AI coding agents.</strong><br />
Run Claude, Codex, OpenCode, Gemini, Antigravity, Cursor, and Copilot side-by-side. Terminal and chat, any layout.
Run Claude, Codex, OpenCode, Gemini, Grok, Antigravity, Cursor, Command Code, and Copilot side-by-side. Terminal and chat, any layout.
</p>

<p align="center">
Expand All @@ -25,7 +25,7 @@

## Supported Agents

**Claude** · **Codex** · **OpenCode** · **Gemini** · **Antigravity** · **Cursor** · **Copilot** and any agent from the [ACP registry](https://agentclientprotocol.com).
**Claude** · **Codex** · **OpenCode** · **Gemini** · **Grok** · **Antigravity** · **Cursor** · **Command Code** · **Copilot** and any agent from the [ACP registry](https://agentclientprotocol.com).

## Why Lightcode?

Expand Down
Loading