The TUI#
fractal open launches the cockpit: a live, single-screen terminal
dashboard over the whole node tree. It shows every node’s status at a glance,
the focused node’s runs, costs, and activity, and its radio traffic — and it
lets you act from the operator’s seat: send radio messages, chat with a
node’s agent session, and attach to a node’s terminal. Everything else is
read-only: the cockpit continuously polls the tree and writes only on those
explicit actions, always as the user (root) node.
Launch#
The TUI ships with the package. From anywhere inside an initialized repository:
$ fractal open
The cockpit anchors on the user (root) node regardless of which worktree you launch it from, so it always shows the whole tree; running it from the repository root is the usual habit. The command surface:
Argument / option |
Meaning |
|---|---|
|
Branch to focus initially (default: the node at |
|
Worktree directory (default |
|
Open with the light palette. |
|
Open with the dark palette (the default). Mutually exclusive with
|
The screen background is always your terminal’s own — the palette only picks
the accent colors, so pass --light when your terminal uses a light color
scheme. The TUI has no configuration file of its own: it reads each node’s
.status and config.json plus the central database, and renders what
it finds.
The tree pane#
Every node in the tree, as box-drawing rows, each with a status dot; the
root row is tagged (user). A filled dot means the node is live (active,
or paused mid-work); a hollow dot means it is not running — settled, idle
awaiting a start, or retired. The dot’s color carries the specific lifecycle
status (see Node Lifecycle), and a pending signal overrides it. A node whose .status says active but whose tmux
session has vanished displays as exited — a display-only reconciliation;
nothing is written back.
up/down select a row, right/left unfold and fold a subtree,
and enter re-scopes the whole cockpit to the selected branch — the pane’s
primary interaction. enter on the already-scoped branch folds or
unfolds it instead. Hovering (or selecting) a truncated row unfolds its full
label. The pane’s foot shows N/M nodes running — the user node is not
counted — or the key hints while you drive the pane.
The node pane#
The right-hand pane shows the focused node in four zones, stepped through
with up/down: the card, the runs explorer, the log-scope toggle, and
the activity log.
The card#
The card’s first row shows the status glyph and word, the branch, and any
pending signal as a present participle (finishing, stopping, …).
The second reads run N · iter n/m · step k/T (name) — a sync pre-step
reads as plain sync. Below it, an ident block lists the agent, model,
and full session id of the running step.
The measures matrix is the cockpit’s cost-and-time view: rows step /
iter / run by columns time / cost, each cell
<current>/<ceiling> with a green-to-red gauge of the fraction used
(- and an empty gauge when uncapped). The run-cost gauge turns yellow
once spend enters the configured reserve budget and red at the cap. Open
rows’ clocks and gauges keep ticking between database writes.
Config chips render the node’s config.json values in schema order
(skipping the keys the card already shows, like max_iters and the cap
keys), packed into a few rows with a trailing ... for overflow;
hovering the chips shows the whole file as pretty-printed JSON. See
Configuration for the keys themselves.
enter on the card opens a chat against the card’s session in the message
pane. While the runs explorer drives a selection, the card “time-machines” to
the highlighted run, iteration, or step — its figures read as they stood at
that row’s end.
The runs explorer#
A run → iteration → step tree, newest run first, with a status dot, session,
elapsed, and cost per row. up/down select; right or enter
expands a run or iteration; left collapses; enter on a step forks
that step’s session into the chat composer. Sync passes fold into the
numbered step that follows them — the time spans both and the costs sum — and
a trailing sync with no following step stays a standalone sync row.
The activity log#
The unified activity timeline, newest first (a bounded window of the most
recent events, with centered date rules closing each day’s group): columns
for time, node, run, iter, step, event, elapsed, and cost. up/down
move a cursor — the selected row unfolds to its full text — and enter
jumps the runs explorer (and the card) to the event’s run, iteration, or
step.
Between the explorer and the log sits the log-scope toggle, a
node | descendants switch: left/right/enter/t flip the
log between the focused node’s own activity and its whole subtree’s, merged.
(t also works directly from the log rows.) The user node opens on the
descendants view — the whole tree’s timeline — and every other node on its
own.
The radio pane#
The focused node’s radio traffic, in three source tabs:
Messages — the focused node’s own mailbox: its top-level messages across all its channels, newest first. Replies stay behind their parents.
Feed — a broadcast digest of the focused node’s subtree: every descendant’s
publicandoutboxposts merged newest-first (a bounded window of the newest posts). This is not the subscription-drivenfractal radio feedcommand — the tab never touches subscriptions.Archive — the cockpit user’s saved messages. This is always the root node’s archive, whatever node is focused.
The pane is a zone ladder — source tabs, filter chips, message rows —
stepped with up/down; stepping above the top zone leaves the pane.
On the tabs, left/right cycle the source. On the filter chips,
left/right pick a chip and enter drops its option list
(esc closes it): the channel filter offers all / inbox /
outbox / public / private (the four default channels only —
custom channels are not filterable here), and the show filter all /
unread / read. Rows show an unread dot, the sender’s leaf name, the
channel, the subject, and the timestamp; the unread dot reflects the mailbox
owner’s read state, not yours.
enter on a row opens the detail view: sender (and the session that
wrote it), channel, timestamp, UUID, priority, subject, and the full body.
Opening a message in the root’s own mailbox writes a real read receipt;
messages in any other node’s mailbox are observed without touching their
read state. An action bar offers Reply / Chat / React / Save
(left/right cycle, enter activates, esc goes back):
Reply pre-fills the composer as a threaded radio reply; the detail stays open for reference.
Chat re-scopes the cockpit to the message’s sender and forks its session into the chat composer.
React drops a
+1/-1choice.Save archives the message into the root’s archive.
Action failures surface as toast notifications.
The message pane#
A segmented composer with a chat | radio kind toggle. Entering the pane
from the ring drops you straight into the body, ready to type; esc steps
back to field navigation, where the arrow keys move across the field grid,
tab/shift+tab cycle fields, and enter activates a field.
Combo fields — node, session, channel, priority — drop a filtered picker:
type to filter, up/down to highlight, enter commits, esc
cancels; typing a printable character on a combo field in the grid opens it
seeded with that character. The node combo offers every branch in the tree,
the channel combo the focused node’s actual channels (so custom channels are
reachable here), and the priority combo 10 down to 1.
Slash commands typed in the body set fields directly: /node,
/channel, /thread, /priority, and /subject <value> (a
recognized command highlights coral). /node accepts a full branch or a
unique leaf name.
Send keys: ctrl+s always sends. On terminals speaking the kitty
keyboard protocol — such as kitty, ghostty, WezTerm, and iTerm2 — enter
also sends and shift+enter inserts a newline; everywhere else enter
is the newline and ctrl+s the only send. The hint line under the body
shows which applies.
Radio kind#
Fields: node, channel, thread, priority, subject, and the body. Defaults:
the focused node, channel public, priority 10 — note these differ
from the CLI’s fractal radio send defaults. An empty subject falls back
to a short prefix of the body. With a message UUID in the thread field
the send becomes a threaded reply (as Reply pre-fills it); otherwise it
is a plain send to the node and channel. Every message is sent as the user
(root) node. Success toasts radio send → <node> · <channel>; failures
toast the error. See Radio for the messaging model.
Chat kind#
Fields: node, session, the per-branch transcript, and the body. Chat runs
one agent turn at a time, streamed into the transcript as bubbles (your
prompt, the agent’s reply, tool and meta lines, errors) with a thinking
spinner while the agent works; a turn that goes silent for too long (about
two minutes) is cancelled. The transcript is kept per branch — re-scoping
swaps it — and enter on the transcript switches to a scroll mode
(up/down scroll, esc back). Chat never writes radio messages.
How a turn reaches the node depends on its state:
An active or paused node whose agent supports session forking gets its newest live session forked — the primary flow is “pause it, then ask what it was doing”: the running loop (or the frozen session a paused run will resume) is never disturbed.
The cockpit’s own earlier chat thread resumes in place, so conversations are multi-turn.
Everything else — settled nodes, detached nodes, agents that cannot fork, no session woven yet — gets a fresh session seeded with the node’s context. When a live thread cannot be forked, the fresh-session fallback is announced with a warning toast.
The session field selects an explicit session to fork or resume; enter
on a step in the runs explorer, on the node card, or the Chat action in
the radio detail all pre-fill it.
Attaching to a session#
ctrl+a, from anywhere, suspends the cockpit and attaches the focused
node’s tmux session read-only: every key is ignored except esc,
which detaches (the session’s status line advertises esc detach for the
duration). If the node has no live session, a no running session toast
appears instead. When the cockpit itself runs inside tmux, ctrl+a
switches your tmux client to the node’s session instead — without the
read-only guard. For attaching outside the TUI, prefer fractal node
attach over raw tmux attach -t: tmux prefix-matches session names and
can attach the wrong one.
Keybindings#
Context |
Key |
Action |
|---|---|---|
Anywhere |
|
Attach the focused node’s tmux session (read-only). |
Anywhere |
|
Quit. |
Ring |
|
Move the focus between panes. |
Ring |
|
Enter the focused pane. |
Ring |
|
Quit. |
Any pane |
|
Step back (close a dropdown or detail, end an edit, leave the pane). |
Tree |
|
Select a node. |
Tree |
|
Unfold / fold the selected subtree. |
Tree |
|
Re-scope the cockpit to the selected node (fold it when already scoped). |
Radio |
|
Step the zone ladder (tabs → filters → rows); move the row cursor. |
Radio |
|
Cycle the source tabs; pick a filter chip. |
Radio |
|
Open a filter dropdown; open the selected message’s detail. |
Radio detail |
|
Cycle and activate Reply / Chat / React / Save. |
Node |
|
Move between zones and rows (card → runs → scope toggle → log). |
Node (card) |
|
Chat with the card’s session. |
Node (runs) |
|
Expand a run or iteration; |
Node (runs) |
|
Collapse the selected row. |
Node (log) |
|
Jump the explorer to the event’s run/iteration/step. |
Node |
|
Toggle the log between the node and its descendants. |
Message |
arrows |
Move across the field grid; leave the pane past the top row. |
Message |
|
Cycle the compose fields. |
Message |
|
Activate a field (toggle the kind, open a combo, edit, scroll the transcript). |
Message (body) |
|
Send (always). |
Message (body) |
|
Send / newline on kitty-protocol terminals; both insert a newline elsewhere. |
What the TUI writes — and what stays on the CLI#
The cockpit’s only writes are the explicit actions above, always attributed to the user (root) node:
sending radio messages and threaded replies (message pane, Reply action);
reacting
+1/-1and saving to the archive (detail actions);read receipts, when you open a message in the root’s own mailbox;
chat turns against a node’s agent session.
Everything that changes the tree or a node stays on the CLI: creating and
starting nodes, finish/stop/kill, pausing and resuming, merging
and deleting, cap and config updates, radio channel management,
subscriptions, unsend/unsave, and plans. See Command-Line Interface for
the full command surface — fractal node for lifecycle,
fractal radio for messaging, fractal plan for plans — and
The Fractal Skill for running the operator’s seat conversationally.