Skip to main content

Cockpit TUI

harness with no arguments opens the keyboard-driven dashboard onto a running daemon: every harness and its state at a glance, with a one-keystroke "hop" into any one as a live terminal. It's a calm ops cockpit — state legibility over decoration (see docs/design/ in the repo for the design exploration).

The dashboard​

The dashboard lists every harness with its state glyph, name, state, and restart count, over a faint sub-line carrying that harness's kind (or prompt), backend, last exit code, and PID. Vim-style navigation works everywhere (j/k, g/G).

A scheduled one-shot reads as scheduled rather than as switched off — it is always enabled = false, so "disabled" would be the wrong word for an armed cron job. Its row is tagged (scheduled) and carries the countdown to the next firing (next in 2h); its sub-line carries the cadence (daily 09:30, or the cron expression verbatim when it is too irregular to paraphrase). A harness bouncing in restart backoff shows that countdown instead — the imminent event wins the column.

A gated resident harness gets the same treatment for its hours: held reads off-hours (not stopped — that would read as a give-up, not an hours gate), and a graceful close in flight reads closing. The right-hand column follows: opens Mon 09:00 while held, closes 13:00 while running in hours, lease until 21:00 for an after-hours lease (harness start <name> --for 3h), and stops by 13:15 for a close's own deadline while it is in flight.

Beside the list, the preview pane is a live read-only view of the selected harness's screen and nothing else — move the selection and it follows, without attaching. The list takes only the width its rows need, so the preview gets every column that's left.

The preview is a real read-only attach under the hood, so the harness inside it is sized to the pane and follows it: a full-screen agent lays out at the size you're actually looking at, not at the 80×24 it was spawned with. Hop in with ↵ and it resizes again to your whole window; detach and the preview takes it back. If two clients are watching the same harness at different sizes, the smaller one wins — harness describe <name> lists every attached session and flags the one setting the minimum.

A claude-code one-shot prints stream-json, one JSON object per line. It runs on pipes rather than a PTY, so there is no terminal for the pane to size and no screen: its preview session gets the recent lines, then new ones as they arrive. The preview renders that backend's output readably instead of mirroring the raw feed (issue #13): heartbeat pings collapse into a single [N heartbeats over Xs] line that ticks while a tool runs, tool calls appear as their human-written description (▸ Push branch to GitHub), and the agent's messages show as plain text. Anything that is not modeled — stderr lines, unknown event kinds — still passes through untouched. Every other adapter keeps the byte-faithful mirror.

A one-shot — any harness with a prompt or prompt_file, scheduled or not — is read rather than sat in, so its preview is not its live output at all. It shows the run exactly as harness logs <name> prints it on a terminal: the run header, then every read, edit and command as one line each, with repeats collapsed (×2 11:45:31–11:45:45) and failures marked. It refreshes every two seconds while the run is live. A daemon too old to build that view, or a harness whose agent keeps no transcript, falls back to the mirror.

The chatroom (C) is every agent's activity in one stream, in the same visual language — clock, @harness, label, detail. Opening it also pulls each harness's latest run from the daemon and merges it in by time, so it is not empty just because nothing happened in the last few minutes, and it keeps pulling every ten seconds while open: a harness whose transcripts this machine cannot read (a remote daemon, a crush instance with its own data directory) still shows up. Rows the live stream and the daemon both deliver appear once.

KeyAction
↵attach to the selected harness
s / x / rstart / stop / restart
eedit the harness definition
nnew harness
ddelete a harness
pprofile switcher
ashow all harnesses
llogs
ycopy the selected harness name (OSC52)
/search
^k or :command palette
?full help
q / ^cquit
escback (always unwinds an overlay or attach)
↵/yconfirm a guarded action (e.g. delete)

Attached mode​

↵ (or harness attach <name>) opens the embedded terminal — a full-window x/vt emulator fed by the daemon's attach stream, with a 1-line status bar at the bottom: logo chip · harness identity + state · read-only badge, and on the right the compact attached keymap (hop / scrollback / detach / help).

In attached mode, bare keys reach the embedded process (an agent CLI, a REPL) unchanged. Harness reserves only the Ctrl-b prefix and a few intercept keys:

KeyAction
^b ddetach — return to the dashboard (the harness keeps running)
^b [ or PgUpenter scrollback
^b h / ^b lhop to the previous / next harness
^b s / ^b rstart / restart the attached harness
^b ?open the keymap help from attached mode

esc always works to back out of an overlay or a full-screen detach without a chord.

The hop​

The hop is the signature interaction: while attached to one harness, ^b h or ^b l instantly switches the attach to the previous/next harness. It stays attached the whole time — you never drop to the dashboard — and the status bar flashes a quick animation as you land. The harness never notices; you're just resubscribing to another screen.

A hop reopens at the current window size (never the 80×24 box of a freshly spawned PTY), so you land exactly where you were.

Scrollback​

Daemon-owned scrollback means history survives detach (ADR-0007). From attached mode, enter scrollback with ^b [ or PgUp:

  • Scroll with j/k, PgUp/PgDn, or the mouse wheel (q/esc returns to live).
  • Scrollback renders inert text with the harness's colors intact — it shows what the process produced, not a replay of its repaint traffic.
  • harness scrollback isn't a separate surface: scroll back in-place, then q returns you to the live stream.
  • The history comes from the harness's durable log, so it reaches back the same distance whatever [daemon] scrollback_bytes is set to. That setting sizes what an attach (and the dashboard preview) replays on open, not what you can scroll to.

Read-only attach​

harness attach <name> --ro opens the harness read-only: you see live output and scrollback but keystrokes are ignored. The status bar shows a read-only badge. This is also how a scoped SSH key attaches (see Remote access).

  • ^k or : opens the command palette — fuzzy access to every dashboard verb without the keymap.
  • / opens incremental search across the list; in scrollback, / searches the history.

Zero state​

With no harnesses defined, the dashboard shows a friendly zero state pointing you at harness new and the config file, rather than an empty list.

Remote​

The same cockpit is reachable over SSH — a remote session is just another thin client onto the daemon. See Remote access.