Getting started with cox

From docs/getting-started.md at 8dbc1da View on GitHub

cox is a modular terminal coding agent in Rust. One core state machine turns submissions into typed events; the same stream powers the TUI, headless runs, editor clients (ACP), and MCP.

Build and first run

Rust is pinned with mise. Prefer mise exec -- cargo … over a global toolchain.

git clone https://github.com/pyrlyn/cox && cd cox
mise exec -- cargo build -p cox
export ANTHROPIC_API_KEY=sk-...   # or OPENAI_API_KEY
./target/debug/cox doctor         # green except prices? you are good
./target/debug/cox -p "create hello.txt containing hi"
./target/debug/cox                # interactive TUI: Enter sends, Esc interrupts

In the TUI: y / s / n answer approval prompts, /model switches tiers, /compact compacts context now. !cmd runs a shell line through the same sandbox and rules as the model's bash, kept out of the conversation; !!cmd also hands its output to the model. Headless scripts use cox run -p; editors use cox acp; other agents can call cox mcp. cox init scaffolds an AGENTS.md for a repository that has none.

cox run -p "what is wrong here?" --image shot.png --image log.jpg attaches images to the first turn; repeat --image for more. Each must be a PNG, JPEG, GIF or WebP of at most 3.75 MB. A missing file, a non-image or an oversized image exits 2 with the reason before any request is made.

Keys

? on an empty composer shows this table over the transcript; /help prints it with the slash commands. The composer's empty line shows the first five rows for where you are (idle, a running turn, a modal, an overlay). Ctrl+C twice quits when idle. A running turn falls back to the idle keys. plugin.leader (Ctrl+K by default) arms the next key for a plugin's own binding; a plugin's slash commands show up in the / palette as /<id>:<name>, after the built-ins.

KeyActionContext
Entersendidle
Shift+Tabmode.cycleidle
@fileidle
/commandidle
?helpidle
Shift+Enternewlineidle
Alt+Enternewlineidle
Ctrl+Enternewlineidle
Ctrl+Rhistoryidle
Ctrl+Tthinkingidle
Ctrl+Otranscriptidle
Ctrl+Eexpandidle
Ctrl+Gdiffidle
Alt+Vvoiceidle
Ctrl+Kplugin.leaderidle
Ctrl+Cquitidle
Ctrl+Dquitidle
ycopyidle
Shift+Ycopy.allidle
Escinterruptrunning
Ctrl+Cinterruptrunning
Ctrl+Bbackgroundrunning
Ctrl+Otranscriptrunning
Alt+Entersend.nowrunning
Ctrl+Entersend.nowrunning
Ctrl+Uunqueuerunning
Enterchoosemodal
Escclosemodal
Uppreviousmodal
Downnextmodal
Ctrl+Etheme.editmodal
Esccloseoverlay
?closeoverlay
PageUpscroll.upoverlay
PageDownscroll.downoverlay

Vim keys

Set tui.vim = true or type /vim to toggle vim keys in the composer. The status line shows -- NORMAL --, -- INSERT --, -- VISUAL -- or -- VISUAL LINE --. Enter still sends from any mode.

KeysModeEffect
i a onormalInsert before / after the cursor, on a new line below
Escinsert, visualBack to normal mode; in insert mode while a turn runs it interrupts the turn instead. In normal mode it only drops a pending command and never interrupts
h j k l w b e 0 ^ $ gg Gnormal, visualMotions; a count repeats them (3w, 2j), 5G goes to line 5
d c y + motionnormalDelete, change, yank over the motion (d2w, c$, yG)
dd cc yynormalWhole line; a count takes more lines (3dd)
iw aw i" a" i' a' i( a( i[ a[ i{ a{after d c y, visualText objects: inner / around a word, quotes or brackets (ci", da()
x XnormalDelete the character under / before the cursor
p PnormalPut after / before the cursor (linewise yanks put whole lines)
u Ctrl+RnormalUndo / redo; one insert session is one undo step
v VnormalCharacterwise / linewise visual mode; d c y x act on the selection
Ctrl+CanyInterrupts the running turn

Not supported: . repeat, macros and registers.

Loop

/loop <interval> <prompt> [--budget <usd>] repeats prompt on interval (30s, 5m, 1h, or a bare number of seconds) whenever the session is idle, until /loop stop or Esc on an empty composer ends it. --budget is this loop's own spend cap; leave it out and it defaults to the session cap (budget.session_usd), which still applies underneath either way.

The headless counterpart is cox run --loop <interval> -p "…" --max-iterations N: same interval grammar, no --budget of its own — it stops after N turns or once the session hits budget.session_usd, whichever comes first. Ctrl+C between iterations exits cleanly with whatever already ran.

DocContents
config.mdEvery configuration key
tools.mdBuilt-in tools, risk, subjects
observability.mdTraces, metrics, OTLP backends
ide.mdZed, JetBrains, Neovim via ACP
how-it-works.mdOne user turn on the event stream
compat.mdWhat cox reads from .claude/ / Codex setups

Costs land in cox stats. Screenshots of TUI states live in screenshots/.

Status line

One row under the composer, e.g. sonnet-5 · ctx ▰▰▰▱▱ 41% · $0.83/5 · workspace-write · 0 tasks · [plan]:

  • ctx is a five-cell mini bar with the context share and percent; the filled cells mark the cached share of the last call.
  • $ is the session spend over the session cap (budget.session_usd); it warns once past budget.warn_at.
  • [plan] is the permission mode badge; effort:xhigh appears only when /effort overrode the tier default.
  • ↻ 4m12s appears only while a /loop is running: the time left until its next turn. It disappears the moment no loop is active.
  • The session's title sits before the mode badge once it has one: generated after the first turn ([session] auto_title) or set with /rename <title>, which a generated title never replaces.
  • cox --plain prints the same segments as one status: … line per turn.

Narrow terminals drop segments from the right in this order: loop countdown → title → git counts → cache → tasks → effort → sandbox → model → cost → ctx (the ctx bar and the mode badge never drop).

Your own status row

[tui.status_line] adds one row above the status line, drawn from the first line your command prints. It reads the same JSON on stdin as a Claude Code statusline script, so one you already have runs unchanged:

# ~/.cox/config.toml (a project config cannot set this)
[tui.status_line]
command = "~/.cox/status.sh"
#!/bin/sh
# ~/.cox/status.sh: model, context share and branch
input=$(cat)
model=$(printf '%s' "$input" | jq -r '.model.display_name')
ctx=$(printf '%s' "$input" | jq -r '.context_window.used_percentage')
branch=$(printf '%s' "$input" | jq -r '.git.branch // "-"')
printf '%s · %s%% · %s\n' "$model" "$ctx" "$branch"

It runs sandboxed, read-only and offline, 300 ms after the status changes; colours are stripped. config.md lists every field and rule.

Status

cox is under active development. APIs, configuration, and install paths are not yet stable. Treat this tree as the current manual, not a frozen release surface.