Skip to content
Open
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
13 changes: 10 additions & 3 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

## Project Snapshot

T3 Code is a minimal web GUI for using coding agents like Codex and Claude.
T3 Code is a minimal web GUI for using coding agents like Codex, Claude, Cursor, Grok, and OpenCode.

This repository is a VERY EARLY WIP. Proposing sweeping changes that improve long-term maintainability is encouraged.

Expand All @@ -26,11 +26,18 @@ Long term maintainability is a core priority. If you add new functionality, firs

## Package Roles

- `apps/server`: Node.js WebSocket server. Wraps Codex app-server (JSON-RPC over stdio), serves the React web app, and manages provider sessions.
- `apps/server`: Node.js WebSocket server (`t3`). Wraps provider CLIs (Codex app-server over JSON-RPC, plus Claude, Cursor, Grok, and OpenCode), serves the React web app, and manages provider sessions.
- `apps/web`: React/Vite UI. Owns session UX, conversation/event rendering, and client-side state. Connects to the server via WebSocket.
- `apps/desktop`: Electron shell. Spawns a desktop-scoped `t3` backend process and loads the shared web app.
- `apps/mobile`: Expo/React Native app. Connects to a paired T3 Code server over the network.
- `apps/marketing`: Astro marketing site (landing, download, and legal pages).
- `packages/contracts`: Shared effect/Schema schemas and TypeScript contracts for provider events, WebSocket protocol, and model/session types. Keep this package schema-only — no runtime logic.
- `packages/shared`: Shared runtime utilities consumed by both server and client applications. Uses explicit subpath exports (e.g. `@t3tools/shared/git`) — no barrel index.
- `packages/client-runtime`: Shared runtime package for sharing client code across web and mobile.
- `packages/ssh`: SSH launch/tunneling utilities for desktop-managed remote environments.
- `packages/tailscale`: Tailscale detection and Serve integration for remote access endpoints.
- `packages/effect-acp`: Effect-based Agent Client Protocol (ACP) client used by ACP providers.
- `packages/effect-codex-app-server`: Effect-based client for the Codex app-server JSON-RPC protocol.

## Reference Repos

Expand All @@ -47,7 +54,7 @@ agents.
- Prefer examples and patterns from the vendored source code over generated guesses or web search results.
- Do not edit files under `.repos/` unless explicitly asked.
- Do not import from `.repos/`; application code must continue importing from normal package dependencies.
- Manage vendored subtrees with `bun run sync:repos`; use `bun run sync:repos --repo <id>` to sync one
- Manage vendored subtrees with `vp run sync:repos`; use `vp run sync:repos --repo <id>` to sync one
configured repository.
- When updating a dependency with a configured vendored subtree, sync that subtree in the same change so
`.repos/` matches the installed dependency version.
Expand Down
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
# T3 Code

T3 Code is a minimal web GUI for coding agents (currently Codex, Claude, Cursor, and OpenCode, more coming soon).
T3 Code is a minimal web GUI for coding agents (currently Codex, Claude, Cursor, Grok, and OpenCode, more coming soon).

## Installation

> [!WARNING]
> T3 Code currently supports Codex, Claude, Cursor, and OpenCode.
> T3 Code currently supports Codex, Claude, Cursor, Grok, and OpenCode.
> Install and authenticate at least one provider before use:
>
> - Codex: install [Codex CLI](https://developers.openai.com/codex/cli) and run `codex login`
> - Claude: install [Claude Code](https://claude.com/product/claude-code) and run `claude auth login`
> - Cursor: install [Cursor CLI](https://cursor.com/cli) and run `cursor-agent login`
> - Grok: install [Grok CLI](https://docs.x.ai/build/cli/reference) and run `grok login`
> - OpenCode: install [OpenCode](https://opencode.ai) and run `opencode auth login`

### Run without installing
Expand Down
1 change: 0 additions & 1 deletion docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
- [Architecture](./architecture/overview.md)
- [T3 Connect](./cloud/t3-connect-clerk.md)
- [Integrations](./integrations/source-control-providers.md)
- [Mobile](./mobile/app.md)
- [Operations](./operations/ci.md)
- [Providers](./providers/codex.md)
- [Reference](./reference/encyclopedia.md)
Expand Down
12 changes: 7 additions & 5 deletions docs/architecture/overview.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Architecture

T3 Code runs as a **Node.js WebSocket server** that wraps `codex app-server` (JSON-RPC over stdio) and serves a React web app.
T3 Code runs as a **Node.js WebSocket server** that wraps coding-agent CLIs (Codex, Claude, Cursor, Grok, OpenCode) and serves a React web app. The diagrams below use Codex (`codex app-server`, JSON-RPC over stdio) as the example provider.

```
┌─────────────────────────────────┐
Expand All @@ -19,9 +19,11 @@ T3 Code runs as a **Node.js WebSocket server** that wraps `codex app-server` (JS
│ CheckpointReactor │
│ RuntimeReceiptBus │
└──────────┬──────────────────────┘
│ JSON-RPC over stdio
│ stdio (JSON-RPC / ACP)
┌──────────▼──────────────────────┐
│ codex app-server │
│ provider CLIs │
│ codex · claude · cursor-agent │
│ grok · opencode │
└─────────────────────────────────┘
```

Expand All @@ -31,7 +33,7 @@ T3 Code runs as a **Node.js WebSocket server** that wraps `codex app-server` (JS

- **Server**: `apps/server` is the main coordinator. It serves the web app, accepts WebSocket requests, waits for startup readiness before welcoming clients, and sends all outbound pushes through a single ordered push path.

- **Provider runtime**: `codex app-server` does the actual provider/session work. The server talks to it over JSON-RPC on stdio and translates those runtime events into the app's orchestration model.
- **Provider runtime**: the provider CLIs (for example `codex app-server`) do the actual provider/session work. The server talks to them over stdio (JSON-RPC or ACP) and translates those runtime events into the app's orchestration model. See [providers.md](./providers.md) for the driver list.

- **Background workers**: Long-running async flows such as runtime ingestion, command reaction, and checkpoint processing run as queue-backed workers. This keeps work ordered, reduces timing races, and gives tests a deterministic way to wait for the system to go idle.

Expand Down Expand Up @@ -92,7 +94,7 @@ sequenceDiagram

1. A user action in the browser becomes a typed request through `WsTransport` and the browser API layer in `nativeApi`.
2. `wsServer` decodes that request using the shared WebSocket contracts in `ws.ts` and routes it to the right service.
3. [`ProviderService`][8] starts or resumes a session and talks to `codex app-server` over JSON-RPC on stdio.
3. [`ProviderService`][8] starts or resumes a session and talks to the provider CLI (for example `codex app-server` over JSON-RPC on stdio).
4. Provider-native events are pulled back into the server by [`ProviderRuntimeIngestion`][9], which converts them into orchestration events.
5. [`OrchestrationEngine`][10] persists those events, updates the read model, and exposes them as domain events.
6. `wsServer` pushes those updates to the browser through `ServerPushBus` on channels defined in [`orchestration.ts`][11].
Expand Down
30 changes: 19 additions & 11 deletions docs/architecture/providers.md
Original file line number Diff line number Diff line change
@@ -1,23 +1,31 @@
# Provider architecture

The web app communicates with the server via WebSocket using a simple JSON-RPC-style protocol:
The clients (web, desktop, mobile) communicate with the server over WebSocket using Effect RPC.
The RPC surface is defined schema-first in `packages/contracts/src/rpc.ts` (`WS_METHODS` plus the
orchestration methods from `ORCHESTRATION_WS_METHODS`), so requests, results, errors, and streamed
events are all schema-validated at the transport boundary.

- **Request/Response**: `{ id, method, params }` → `{ id, result }` or `{ id, error }`
- **Push events**: typed envelopes with `channel`, `sequence` (monotonic per connection), and channel-specific `data`
## Built-in providers

Push channels: `server.welcome`, `server.configUpdated`, `terminal.event`, `orchestration.domainEvent`. Payloads are schema-validated at the transport boundary (`wsTransport.ts`). Decode failures produce structured `WsDecodeDiagnostic` with `code`, `reason`, and path info.
Providers are implemented as `ProviderDriver`s in `apps/server/src/provider/Drivers/` and
registered in `builtInDrivers.ts`. The built-in drivers are:

Methods mirror the `NativeApi` interface defined in `@t3tools/contracts`:
- **Codex** — wraps `codex app-server` (JSON-RPC over stdio)
- **Claude** — wraps the Claude Code CLI
- **Cursor** — wraps `cursor-agent` via ACP
- **Grok** — wraps the Grok CLI via ACP (`grok agent stdio`)
- **OpenCode** — wraps the OpenCode CLI

- `providers.startSession`, `providers.sendTurn`, `providers.interruptTurn`
- `providers.respondToRequest`, `providers.stopSession`
- `shell.openInEditor`, `server.getConfig`

Codex is the only implemented provider. `claudeCode` is reserved in contracts/UI.
Each driver probes its CLI on `PATH`, reports an availability/auth snapshot, and adapts the
provider's runtime events into the shared orchestration model.

## Client transport

`wsTransport.ts` manages connection state: `connecting` → `open` → `reconnecting` → `closed` → `disposed`. Outbound requests are queued while disconnected and flushed on reconnect. Inbound pushes are decoded and validated at the boundary, then cached per channel. Subscribers can opt into `replayLatest` to receive the last push on subscribe.
`packages/client-runtime` owns the client connection stack: `connection/supervisor.ts` manages
per-environment connection lifecycle (connect, retry with backoff, reconnect on wakeups), and
`rpc/client.ts` exposes typed RPC access on top of it. Outbound requests fail fast or wait for a
live session depending on the call site; inbound payloads are decoded against the contracts
schemas at the boundary.

## Server-side orchestration layers

Expand Down
12 changes: 6 additions & 6 deletions docs/getting-started/quick-start.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,20 @@

```bash
# Development (with hot reload)
bun run dev
vp run dev

# Desktop development
bun run dev:desktop
vp run dev:desktop

# Desktop development on an isolated port set
T3CODE_DEV_INSTANCE=feature-xyz bun run dev:desktop
T3CODE_DEV_INSTANCE=feature-xyz vp run dev:desktop

# Production
bun run build
bun run start
vp run build
vp run start

# Build a shareable macOS .dmg (arm64 by default)
bun run dist:desktop:dmg
vp run dist:desktop:dmg

# Or from any project directory after publishing:
npx t3
Expand Down
2 changes: 1 addition & 1 deletion docs/reference/scripts.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Scripts

- `vp run dev` — Starts contracts, server, and web in watch mode.
- `vp run dev:server` — Starts just the WebSocket server. The server process runs on Bun (`@effect/platform-bun` + `BunPtyAdapter`), but task running uses `vp run`.
- `vp run dev:server` — Starts just the WebSocket server (Node.js via `node --watch`).
- `vp run dev:web` — Starts just the Vite dev server for the web app.
- Dev commands default `T3CODE_HOME` to `~/.t3` — the same shared home the desktop/production app uses. Override with `--home-dir` (see below) to keep dev state separate.
- Override server CLI-equivalent flags from root dev commands with `--`, for example:
Expand Down
16 changes: 14 additions & 2 deletions docs/reference/workspace-layout.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,19 @@
# Workspace layout

- `/apps/server`: Node.js WebSocket server. Wraps Codex app-server, serves the built web app, and opens the browser on start.
## Apps

- `/apps/server`: Node.js WebSocket server (`t3`). Wraps the provider CLIs, serves the built web app, and opens the browser on start.
- `/apps/web`: React + Vite UI. Session control, conversation, and provider event rendering. Connects to the server via WebSocket.
- `/apps/desktop`: Electron shell. Spawns a desktop-scoped `t3` backend process and loads the shared web app.
- `/apps/mobile`: Expo/React Native app. Connects to a paired T3 Code server over the network.
- `/apps/marketing`: Astro marketing site (landing, download, and legal pages).

## Packages

- `/packages/contracts`: Shared effect/Schema schemas and TypeScript contracts for provider events, WebSocket protocol, and model/session types.
- `/packages/shared`: Shared runtime utilities consumed by both server and web. Uses explicit subpath exports (e.g. `@t3tools/shared/git`, `@t3tools/shared/DrainableWorker`) — no barrel index.
- `/packages/shared`: Shared runtime utilities consumed by both server and clients. Uses explicit subpath exports (e.g. `@t3tools/shared/git`, `@t3tools/shared/DrainableWorker`) — no barrel index.
- `/packages/client-runtime`: Shared client runtime (connection supervision, RPC client, state) used by web and mobile.
- `/packages/ssh`: SSH launch/tunneling utilities for desktop-managed remote environments.
- `/packages/tailscale`: Tailscale detection and Serve integration for remote access endpoints.
- `/packages/effect-acp`: Effect-based Agent Client Protocol (ACP) client used by ACP providers.
- `/packages/effect-codex-app-server`: Effect-based client for the Codex app-server JSON-RPC protocol.
22 changes: 20 additions & 2 deletions docs/user/keybindings.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,25 +19,35 @@ See the full schema for more details: [`packages/contracts/src/keybindings.ts`](

```json
[
{ "key": "mod+b", "command": "sidebar.toggle" },
{ "key": "mod+j", "command": "terminal.toggle" },
{ "key": "mod+alt+b", "command": "rightPanel.toggle" },
{ "key": "mod+d", "command": "terminal.split", "when": "terminalFocus" },
{ "key": "mod+shift+d", "command": "terminal.splitVertical", "when": "terminalFocus" },
{ "key": "mod+n", "command": "terminal.new", "when": "terminalFocus" },
{ "key": "mod+w", "command": "terminal.close", "when": "terminalFocus" },
{ "key": "mod+d", "command": "diff.toggle", "when": "!terminalFocus" },
{ "key": "mod+shift+j", "command": "preview.toggle" },
{ "key": "mod+r", "command": "preview.refresh", "when": "previewFocus" },
{ "key": "mod+l", "command": "preview.focusUrl", "when": "previewFocus" },
{ "key": "mod+=", "command": "preview.zoomIn", "when": "previewFocus" },
{ "key": "mod++", "command": "preview.zoomIn", "when": "previewFocus" },
{ "key": "mod+-", "command": "preview.zoomOut", "when": "previewFocus" },
{ "key": "mod+0", "command": "preview.resetZoom", "when": "previewFocus" },
{ "key": "mod+k", "command": "commandPalette.toggle", "when": "!terminalFocus" },
{ "key": "mod+n", "command": "chat.new", "when": "!terminalFocus" },
{ "key": "mod+shift+o", "command": "chat.new", "when": "!terminalFocus" },
{ "key": "mod+shift+n", "command": "chat.newLocal", "when": "!terminalFocus" },
{ "key": "mod+o", "command": "editor.openFavorite" }
{ "key": "mod+shift+m", "command": "modelPicker.toggle", "when": "!terminalFocus" },
{ "key": "mod+o", "command": "editor.openFavorite" },
{ "key": "mod+shift+[", "command": "thread.previous" },
{ "key": "mod+shift+]", "command": "thread.next" }
]
```

For most up to date defaults, see [`DEFAULT_KEYBINDINGS` in `apps/server/src/keybindings.ts`](../../apps/server/src/keybindings.ts)
Plus `mod+1` … `mod+9` bound to `thread.jump.{n}` (and `modelPicker.jump.{n}` while the model picker is open).

For most up to date defaults, see [`DEFAULT_KEYBINDINGS` in `packages/shared/src/keybindings.ts`](../../packages/shared/src/keybindings.ts)

## Configuration

Expand All @@ -53,10 +63,14 @@ Invalid rules are ignored. Invalid config files are ignored. Warnings are logged

### Available Commands

- `sidebar.toggle`: open/close the sidebar
- `rightPanel.toggle`: open/close the right panel
- `terminal.toggle`: open/close terminal drawer
- `terminal.split`: split terminal (in focused terminal context by default)
- `terminal.splitVertical`: split terminal vertically (in focused terminal context by default)
- `terminal.new`: create new terminal (in focused terminal context by default)
- `terminal.close`: close/kill the focused terminal (in focused terminal context by default)
- `diff.toggle`: open/close the diff view
- `preview.toggle`: open/close the in-app browser preview panel (desktop app only)
- `preview.refresh`: reload the active preview tab (in focused preview context by default)
- `preview.focusUrl`: focus the URL input of the preview panel (in focused preview context by default)
Expand All @@ -67,6 +81,10 @@ Invalid rules are ignored. Invalid config files are ignored. Warnings are logged
- `chat.new`: create a new chat thread preserving the active thread's branch/worktree state
- `chat.newLocal`: create a new chat thread for the active project in a new environment (local/worktree determined by app settings (default `local`))
- `editor.openFavorite`: open current project/worktree in the last-used editor
- `modelPicker.toggle`: open or close the model picker
- `modelPicker.jump.{n}`: select the nth model picker entry (`n` from 1 to 9)
- `thread.previous` / `thread.next`: switch to the previous/next thread
- `thread.jump.{n}`: jump to the nth thread (`n` from 1 to 9)
- `script.{id}.run`: run a project script by id (for example `script.test.run`)

### Key Syntax
Expand Down
Loading