ade code is a terminal-native client for the same Work agent chat surface the Electron app exposes in AgentChatPane. It targets agents and operators who prefer a shell-first workflow: Ink + React render the TUI, while chat transcripts, slash commands, lane navigation, model picks, and ADE actions all flow through the same JSON-RPC contracts the desktop uses.
It is a client. The runtime, lanes, chats, transcripts, PRs, processes, and proof artifacts live in the per-machine ade serve daemon. ade code attaches to that daemon, drives a single project scope, and renders incoming events.
| Path | Role |
|---|---|
apps/ade-cli/src/cli.ts |
Resolves the built or source TUI entry and forwards the parsed launch context to runAdeCodeCli. |
apps/ade-cli/src/tuiClient/cli.tsx |
TUI entry: argv parsing, project discovery, connection bootstrap, Ink mount. Built to apps/ade-cli/dist/tuiClient/cli.mjs. |
apps/ade-cli/src/tuiClient/app.tsx |
Primary Ink/React surface: navigation, composer, drawers, right pane, session lifecycle, slash command dispatch. |
apps/ade-cli/src/tuiClient/connection.ts |
Resolves attached vs embedded mode, runs the ade/initialize handshake, registers the project with projects.add, wraps subsequent requests with projectId. |
apps/ade-cli/src/tuiClient/jsonRpcClient.ts |
Socket client: connect, request/response, chat/event notifications. |
apps/ade-cli/src/tuiClient/adeApi.ts |
Typed wrappers over AdeCodeConnection.action / actionList for lanes, chat, models, navigation, provider readiness, API-key status, OpenCode diagnostics, project slash-command discovery, lane diff stats (listLaneDiffStats), per-lane PR summaries (listPrsByLane), and the Claude steer family (steerChatMessage, cancelSteerMessage, editSteerMessage, dispatchSteerMessage). |
apps/ade-cli/src/tuiClient/commands.ts / linearCommands.ts |
Slash command catalog and routing. |
apps/ade-cli/src/tuiClient/format.ts |
Transcript rendering helpers for the TUI. |
apps/ade-cli/src/tuiClient/aggregate.ts |
Pure derivations on top of the chat event stream (e.g. derivePendingSteers) consumed by the composer and right-pane steer view. |
apps/ade-cli/src/tuiClient/drawerSelection.ts |
Pure selectors for the lane / chat drawer (active row, expanded groups, keyboard navigation). |
apps/ade-cli/src/tuiClient/eventDedup.ts |
Reserves and syncs chat-event dedupe keys so replayed runtime events do not render twice. |
apps/ade-cli/src/tuiClient/feedback.ts |
Builds the multi-field /feedback form. Validates required fields, packs the FeedbackDraftInput envelope, and adds project / lane / runtime context before submission. |
apps/ade-cli/src/tuiClient/heartbeat.ts |
Maintains the startTuiHeartbeat loop that tells the runtime the terminal client is still attached. |
apps/ade-cli/src/tuiClient/highlightCache.ts |
Pre-registers highlight.js languages (TypeScript, JavaScript, Python, Rust, Go, Swift, Bash, JSON, YAML, Markdown, XML, CSS, SQL) and caches token streams so chat code fences render once instead of being re-highlighted on every redraw. |
apps/ade-cli/src/tuiClient/imageTargets.ts |
Finds the latest openable Codex image result / viewed image target for terminal open actions. |
apps/ade-cli/src/tuiClient/laneTree.ts |
Stack-graph ordering for the lane drawer (sortLanesForStackGraph). |
apps/ade-cli/src/tuiClient/pendingInput.ts |
Derives pending tool approvals and answer prompts from the chat event stream. |
apps/ade-cli/src/tuiClient/planMode.ts |
Provider-agnostic plan-mode detector (isPlanMode(modelState)) plus hasFirstUserMessage event scan. Decides whether the composer should display the plan-mode badge and gate destructive tools. |
apps/ade-cli/src/tuiClient/spinTick.tsx |
Shared monotonic spinner tick provider (SpinTickProvider) so every animated glyph in the TUI ticks in lockstep. |
apps/ade-cli/src/tuiClient/subagentPane.ts |
Builds the right-pane Subagents/Teammates snapshot view from subagent_* and teammate envelopes (subagentSnapshotsFromEvents). |
apps/ade-cli/src/tuiClient/workEventIds.ts |
Stable Work-tab identity helpers used by the TUI to thread ade.work-* event ids through the renderer without re-deriving them per frame. |
apps/ade-cli/src/tuiClient/state.ts |
Persists terminal-client state under ~/.ade/: the last selected chat per lane (lastChatByLane) plus the most recently active lane (lastLaneId), used to restore lane focus across launches. |
apps/ade-cli/src/tuiClient/theme.ts |
Shared Ink color and status tokens. Mirrors the Claude Design wireframe terminal palette 1:1: surfaces, text levels, brand violets, status (running/attention/idle/failed/primary), executor brand colors (Claude/Codex/Cursor/OpenCode/Droid + Shell + Copilot), plus helper exports laneStatusColor, agentStatusColor, agentStatusGlyph, and per-provider glyph + wordmark. |
apps/ade-cli/src/tuiClient/types.ts |
AdeCodeConnection, ProjectLaunchContext, navigation DTOs aligned with apps/desktop/src/shared/types. |
apps/ade-cli/src/tuiClient/components/ |
AdeWordmark, Drawer (with DrawerPrSummary rows), ChatView (exports computeChatScrollMaxOffset and renderChatTranscriptPlainText for /copy), Header, RightPane, SlashPalette, MentionPalette, ApprovalPrompt, ModelStatus, FooterControls, and TerminalPane (xterm-headless preview pane that consumes ChatTerminalPreviewResult from ade.terminal.preview plus live ade.pty.data chunks to render a real terminal grid inside Ink; running Claude terminals can be put into direct control mode from the TUI). |
apps/ade-cli/src/tuiClient/keybindings/index.ts |
Verbatim ~/.claude/keybindings.json reader and TUI action dispatcher (chord support, vim namespace, clipboard-image paste hooks). Resolves defaultKeybindingsPath(), parses the Claude keybindings schema, and maps key sequences onto TUI actions. |
apps/ade-cli/src/tuiClient/statusline/index.ts |
Claude-compatible status line config reader and runner. Reads the ~/.claude/statusline.json contract, executes the configured status command, and exposes the rendered lines to ModelStatus. |
apps/desktop/src/shared/types/chat.ts |
Canonical chat DTOs (AgentChatEventEnvelope, sessions, pending input, AgentChatContextUsage, AgentChatClaudeOutputStyle, AgentChatClaudePlugin, subagent kinds). Imported per-module so ade-cli typecheck stays scoped. |
apps/desktop/src/shared/modelRegistry.ts |
Default model selection for new sessions (getDefaultModelDescriptor). |
apps/desktop/src/shared/adeLayout.ts |
Resolves project-scoped .ade paths. |
ade code opens a Unix-domain or named-pipe socket connection to the runtime daemon. Resolution order in connectToAde:
--socket /path/to/sockon the parentadeprocess (also readsADE_RPC_SOCKET_PATH).- The machine socket from
resolveMachineAdeLayout()(~/.ade/sock/ade.sockor\\.\pipe\ade-runtime). - If the machine socket is not listening,
connection.tscallsspawnDaemon(socketPath)— a detachedade serve --socket <socketPath>— and retries up to 25 times with a 200 ms delay. - As a final fallback, the legacy project-scoped socket from
resolveAdeLayout(projectRoot)if the user passed--require-socketand the machine socket is unavailable.
ade code --print-state exercises that whole path, prints the chosen mode and socket path, and exits.
ade code --embedded (or ade --headless code) skips the daemon and builds an AdeRuntime in-process via loadEmbeddedAdeCli(), which dynamic-imports bootstrap and adeRpcServer from the ade-cli package itself. Used for headless or development environments where Electron / ade serve is not present. This mode is single-project, single-process: closing the TUI tears the runtime down.
forceEmbedded and requireSocket are mutually exclusive — connectToAde rejects the combination.
Both modes run the same handshake before the TUI mounts:
-> ade/initialize {
protocolVersion: "2025-06-18",
clientName: "ade-code",
identity: { role: "cto", callerId: "ade-code:<pid>" }
}
<- { runtimeInfo: { multiProject: true, version, ... }, capabilities: { projects: true, ... } }
-> ade/initialized
If the response advertises runtimeInfo.multiProject === true or capabilities.projects === true, connection.ts calls projects.add { rootPath: <project-root> }, captures the returned projectId, and from then on every project-scoped request is rewritten to include projectId. The runtime-scoped methods (the set in MULTI_PROJECT_RUNTIME_METHODS: ade/initialize, projects.*, ping, runtime/info, etc.) pass through unchanged.
For the embedded runtime there is no projects.add step — the in-process runtime is already bound to one project root.
apps/ade-cli/src/tuiClient/app.tsx is the Ink root. Layout:
- Header — project name, active lane, branch, and the terminal client frame.
- Drawer (toggled with the configured shortcut) — two sections: Lanes and Chats. Selecting a lane in the Lanes pane switches the active lane and filters the Chats pane to that lane's sessions. Lane and chat selection drive the right pane's context.
- ChatView — the main transcript. Renders user, assistant, tool, and system events from
chat/eventnotifications. Tool calls collapse into expandable blocks; the most recent expandable failure id is tracked soEntercan drill into it. - Composer — multi-line input with mention completion (
@…) sourced fromMentionPaletteand slash command completion fromSlashPalette. Pending tool approvals surface asApprovalPrompt. - RightPane — context-sensitive drawer for slash command output. The "right" placement commands (see below) render their results here as forms, lists, diffs, help text, or rendered objects. For an active lane, the default
lane-detailsview shows live git stats (DiffLineStatsviadiff.listLaneDiffStats), the linked PR if any (pr.listPrsByLane), the most recent tool/command summary, elapsed time for the active turn, and aworktreeAvailableguard that surfaces a recoverable warning when a lane's worktree path is missing from disk. - FooterControls — two-row footer. The top row (mode bar, only present when there's content) shows provider glyph + label, model display, fast-mode badge, reasoning effort, permission summary, pending steer count, a 10-cell token usage bar (
TokenBar) that recolors at 50 / 80 / 95 %, and the cached context-percent / token summary. The bottom row shows pane toggles (lanes,setup, plus an optionalagentstoggle when subagents/teammates exist) and pane-specific hints (drawer mode lanes/chats, details navigation, chat scroll position,/steerreminder when steers are queued).footerControlsForAvailability(agentsAvailable)decides which toggles are wired. - Claude terminal control — when the active session is a running
Claude terminal,
Ctrl+Tmoves keyboard input from ADE into that terminal.TerminalPaneswitches from preview mode to a bordered control frame, stops hiding Claude's bottom input rows, and the footer showsCLAUDE CONTROLwithCtrl+Tto return to ADE andCtrl+]as the escape chord. Raw terminal input strips only those control bytes before forwarding the rest to the PTY.
Heartbeats are kept alive with startTuiHeartbeat so the runtime knows the chat client is still attached.
commands.ts exports the built-in slash command catalog. placement decides whether the command runs inline in the chat or opens the right pane. The TUI also discovers project command files and Codex prompts before a chat exists, then refreshes against server-provided AgentChatSlashCommands from the active runtime via getSlashCommands. Provider/runtime commands win over same-named built-ins except for local terminal controls such as /login, /quit, and /clear.
Inline (acts on chat or shell):
| Command | Effect |
|---|---|
/commit [message] |
Commit lane changes through git.commit. |
/push |
Push the active lane branch. |
/clear |
Clear the local TUI transcript view. |
/copy |
Copy the visible chat transcript (rendered through renderChatTranscriptPlainText) to the system clipboard. |
/open |
Hand the current ADE context off to desktop via app/navigate. |
/quit |
Exit ade code. |
/remember <fact> |
Write a durable ADE memory entry. |
/steer cancel |
Remove the latest staged steer message from the local queue. |
/steer edit <text> |
Edit the latest staged steer message. |
/steer send |
Claude only: deliver the latest staged steer inline into the active turn (SDK dispatchSteer mode: "inline"). |
/steer interrupt |
Claude only: interrupt the active turn and run the latest staged steer next (dispatchSteer mode: "interrupt"). |
Right pane (open the contextual drawer):
| Command | Pane |
|---|---|
/steer |
Show staged steer messages and their delivery state. |
/new lane |
Lane creation form. |
/new chat [title] |
New chat in the active lane. |
/rename [title] |
Rename the active chat. |
/tag <tag|clear> |
Tag the active Claude chat (Claude only). |
/output-style [style] |
List or select the active Claude output style (Claude only). |
/plugin [reload|native args] |
List, reload, or manage Claude plugins (Claude only). |
/agents |
List Claude agents from user/project config (Claude only). |
/skills |
List Claude skills from user/project config (Claude only). |
/context |
Show Claude context usage breakdown (Claude only). |
/init |
Generate AGENTS.md and Claude pointer files (Claude only). |
/status |
Project, lane, runtime state summary. |
/diff |
Active lane diff (file list with summarized hunks). |
/log |
Recent commits. |
/pr, /pr open, /pr review, /pr checks |
PR state, create/open PR, reviews, checks. |
/linear … (list, workflows, run, route, sync, ingress, pull, comment, status, assign) |
Linear sub-router; backed by linearCommands.ts. |
/memory [query], /forget |
Search and manage ADE memory. |
/chats |
Sessions in the active lane. |
/switch [lane|chat] |
Switcher palette. |
/help |
Keymap and command help. |
/keybindings |
Show Claude-compatible keybinding config diagnostics. |
/statusline |
Show Claude-compatible status line config. |
/doctor |
Show ADE Code and Claude-compat diagnostics. |
/feedback |
Multi-field feedback form (category / summary / details / expected / actual / environment / additional context) wired to feedback.submit via the feedback.ts form builder. |
/model, /effort |
Model and reasoning-effort pickers. |
/system |
System and runtime details. |
/ade <domain.action> [json] |
Run an allowlisted ADE action; shows result in RightPane. |
Inline chat commands (run through the active Claude SDK session, Claude only):
| Command | Effect |
|---|---|
/compact [instructions] |
Compact the Claude context window through the active SDK session. |
/usage |
Show Claude usage / rate-limit window through the active SDK session. |
/insights |
Generate Claude session insights through the active SDK session. |
/fast [on|off] |
Toggle Claude fast mode through the active SDK session. |
/goal [condition|clear] |
Set or clear the Claude completion goal. |
Claude-only commands only appear in the slash palette when the active chat's provider is claude. The palette filters built-in entries by their providers whitelist so a Codex / OpenCode / Cursor chat does not show parity affordances that have no backing call.
Several slash commands forward to a desktop route when issued from ade code:
/app-control -> /app-control
/browser -> /browser
/computer -> /proof
/computer-use -> /proof
/ios, /ios-sim -> /ios-sim
/macos-vm -> /macos-vm
/mission, /missions -> /missions
/pencil -> /pencil
/proof -> /proof
navigateDesktop posts an app/navigate request to the same runtime, which the multi-window desktop shell uses to open or focus the appropriate window. The TUI does not host these surfaces itself; it points the desktop at them.
Lane resolution at launch goes through two helpers in tuiClient/project.ts:
chooseInitialLane(lanes, context)— context-only pick:--lanehint, then the lane whose worktree contains the currentworkspaceRoot, then the primary/first lane, falling back to "no lane".chooseTuiLaunchLane(lanes, context, lastLaneId)— the actual TUI entry point. If the context lane is explicit (a--lanehint, or the user invokedade codefrom inside a non-primary lane's worktree / attached root), that wins. Otherwise the persistedAdeCodeState.lastLaneIdfrom~/.ade/wins so reopening the TUI returns to the previously focused lane. Falls back to the context choice when there is no persisted lane.
Lane selection persists lastLaneId and updates the daemon's session state so the same lane is reflected in desktop and iOS clients attached to the same runtime.
ade code # attached to the machine daemon for the current project
ade code --print-state # smoke-test: print mode + socket and exit
ade code --embedded # in-process runtime fallback
ade --project-root /repo code # bind to a different project
ade --socket /tmp/ade-runtime-dev.sock code
# attach to a specific socket (dev runtime, peer machine, etc.)After local changes, run npm run build inside apps/ade-cli so both dist/cli.cjs and dist/tuiClient/cli.mjs exist for packaged and linked use. The CLI build verifier imports dist/tuiClient/cli.mjs from an isolated temp directory, checks that bundled __dirname / __filename references have ESM shims, and confirms runAdeCodeCli(["--help"]) prints the ADE Code help banner without relying on repo-local node_modules. During repo development, npm run dev:code runs the source TUI against the shared dev runtime at /tmp/ade-runtime-dev.sock.
ade code ships verbatim compatibility with the Claude Code 2.1.x terminal contracts so users coming from Claude Code keep their existing config and muscle memory:
- Keybindings.
tuiClient/keybindings/index.tsreads~/.claude/keybindings.json(resolved throughdefaultKeybindingsPath(), withCLAUDE_HOMEandXDG_CONFIG_HOMEoverrides). The full Claude schema is honored — chord sequences, modifier syntax, and thevimnamespace — and dispatched onto TUI actions throughdispatchKeybinding()./keybindingssurfaces a diagnostics view;openKeybindingsFile()opens the config in the user's editor. - Status line.
tuiClient/statusline/index.tsreads~/.claude/statusline.json, executes the configured command, and feeds the rendered lines intoModelStatus./statuslineshows the contract and the most recent stdout/stderr. When a status command produces output, the status panel hides the default token/context meter for the same row. - Vim namespace. When vim mode is active, the model-status row exposes the current
insert/normalmode tag and the keybindings dispatcher routesvim.*actions. - Clipboard image paste. Cross-platform clipboard-image paste is wired into the composer (Linux via
xclip/wl-paste, macOS viapngpaste/AppleScript, Windows via PowerShell), so pasting a screenshot uploads it as a Claude attachment alongside text. autopermission mode. The Claude permission picker acceptsauto(mapped onto the SDKpermissionMode: "auto") in addition todefault,plan,acceptEdits, andbypassPermissions.- Subagent panel. The right pane's subagent surface is re-keyed on
agentId+parentToolUseIdand split across two tabs — Subagents and Teammates. Background runs render inline within the Subagents tab via a per-rowbackgroundflag rather than a separate tab. Snapshots are reconstructed live fromsubagent_*envelopes (andteammate.idle/task.completedfor teammates) viasubagentSnapshotsFromEvents(). Each snapshot carriesparentToolUseId,turnId,startedAt,endedAt, and a deriveddurationMsso rows can show elapsed time even when the runtime did not reportusage.durationMs. The footer exposes an explicit Agents pane toggle (viapane:agentskeybinding) when at least one teammate/subagent row exists; absent that, the count surfaces as an inline chip. - Context, output styles, plugins.
/context,/output-style, and/plugincallchat.getContextUsage,chat.listClaudeOutputStyles/chat.setClaudeOutputStyle, andchat.listClaudePlugins/chat.reloadClaudePluginsagainst the same Claude SDK runtime the desktop chat uses.
+ new chatopens a draft setup view in the details pane; it does not create a backend chat until the first prompt is sent from the middle composer./modelopens the model setup view. It can switch provider, model, reasoning, and permission settings, refresh provider readiness throughai.getStatus, and open desktop Settings > AI Providers for full configuration./logindelegates only to provider CLIs that can authenticate in the current terminal: Claude (claude auth login), Codex (codex login), and OpenCode (opencode auth login). Cursor chat is@cursor/sdkand needsCURSOR_API_KEYor desktop Settings > AI Providers. Droid chat runs Factory Droid over ACP and needsFACTORY_API_KEYor Factory's interactivedroidlogin.- The middle composer shows the selected provider, model, reasoning, and permission mode under the prompt so draft changes on the right are visible before the chat starts.
- ADE CLI — runtime daemon, install paths, service manager, full CLI surface.
- Chat feature — in-app Work chat architecture (service + renderer); same agent chat backend.
- Remote runtime — how the same runtime daemon is reached over SSH.
- System overview — CLI / terminal client placement in the system diagram.