The dependency tree on screen
01vibe tree draws the packages a project resolved as a tree, says for each one how its text reaches the agent, and explains why. On a terminal it opens an interactive screen; in a script it prints a plain tree or JSON. This page is that command, its screen, and the way an agent can look at the screen without a terminal.
vibe tree --plain --path hello-vibe
project: <TMP>/work/hello-vibe
STATIC.md: (none)
packages: 1 roots: 1
columns: load T=transitive C=condition S=in STATIC.md
org.vibevm.world/wal dynamic . . .
What it shows
03The command reads the committed lock file, the manifests and the generated boot files of the project at --path, and writes nothing: it never touches the tree, a manifest, the lock or the generated files. Each package is one row, with the columns in a fixed order. The load column is the lane a package's boot snippet landed in, read from the generated files rather than recomputed, so the screen shows what an agent will read. A package that appears under several parents is drawn under each one and marked (*) after the first, without being expanded again. The status line shows the size of the compiled lane in bytes and lines.
04
vibe tree answers
that algorithmically: it renders the resolved package tree annotated with the
effective load type and the flags that explain it, and emits the same data as
JSON for downstream tools.
05 It operates on the workspace discovered from--path(default.), reading the committedvibe.lock, the node manifests, and the generated boot artifacts.
06
vibe tree is a read-only analyzer over the current project. It mutates
nothing (REQ: never writes to the tree, manifests, lockfile, or artifacts).
07 Each package is one row. Columns, left to right (REQ: this order):
08
The load value is the lane a package's boot snippet actually lands in, read
from the committed artifacts (REQ):
09vibe treerenders each package under each parent, marks a re-occurrence with a trailing(*)and does not re-expand it, and cycle-guards the walk on the package's qualifiedgroup/name(REQ).
10
The status line shows the size of the statically-compiled lane: the byte count
and line count of STATIC.md (REQ).
Three outputs
11On a terminal the default is the interactive screen. When the output is not a terminal, or with --plain, the command prints a static tree. --json emits one object with the same data the screen renders, valid against the shipped schema, for any tool downstream.
12 interactive TUI — the default on a tty (§2.11);
13
plain — a static ASCII tree when not on a tty, or under --plain.
14
--json — the machine surface (§2.7), the same data a GUI consumes;
15vibe tree --jsonemits one object, the same data model the TUI renders, valid against the shipped schema (REQ:schema_version = 1).
16Two warnings come with the tree. Root drift means the lock file's list of roots disagrees with the manifest, so the lock is behind. Stale artifacts means the committed boot files disagree with a fresh computation, so the project needs vibe reinstall.
17 root-drift —vibe.lockmeta.root_dependenciesdisagrees with the rootvibe.toml[requires.packages](the lock is behind).
18 stale-artifacts — the committed lanes disagree with a freshEffectiveBootrecompute (the tree needsvibe reinstall).
The screen
19The screen has three display modes, each a configuration of one tree widget and none a flat list, and three shapes of the tree, chosen per mode; the active mode is picked from a menu, shown in the status line and remembered. Primary actions sit on function keys: F1 opens Search Everywhere, F2 the sort menu, F3 the mode menu, F4 the settings screen, F6 copies the current screen and Shift+F6 opens the copy settings. The footer lists the active keys, with Shift written as an arrow to keep the hints short.
20
REQ. vibe tree has three display modes, each a Tree-widget (§3.1) configuration
(§3.2) — none is a flat list:
21 REQ. The pipeline offers three shapes, selectable per context on the F2 sort menu (§7.2), default = (a):
22 REQ. The active mode is chosen from the F3 menu (§7.1) — not a bare cycle key. The active mode is shown in the status line and persisted (§9).
23 REQ. Primary actions are on function keys:F1Search Everywhere (§7.3),F2sort menu (§7.2),F3mode menu (§7.1),F4settings (opens thevibe prefssettings UI — PROP-041),F6copy /Shift+F6copy-settings (§10.2). The footer lists the active keys with short labels;Shiftis written as↑(e.g.Shift+F6→↑F6) to keep hints short.
24Inside the tree the arrows move and pan, Space folds a node, Enter opens the detail card, and Shift with a horizontal arrow switches tabs anywhere in the application. Search Everywhere works as in an IDE: an «All» tab that searches everything and tabs per category. The sort menu depends on the mode. Controls are grouped, and Tab steps between groups. Modals stack: each new one draws over the last, Esc closes the top one, and at the base screen Esc asks before quitting, because a session may have been slow to load.
25 REQ. Within a tree:↑/↓move selection (+ scroll),←/→pan horizontally,Spacefolds/unfolds the selected node,Enteropens the detail card (§8).Shift+←/→switches tabs app-wide — the tree's display tabs (in tabs mode) and the Search Everywhere category tabs (§7.3); plain←/→stay tree-pan (which is why tab-switch takes Shift). These direct keys are exempt from the F-key scheme (they are navigation, not commands).
26 REQ.F1opens the Search Everywhere window — thevibe-actionsSearch Everywhere engine (PROP-039 §10), in the IntelliJ IDEA idiom: a hybrid "All" tab that searches everything, plus per-category tabs that narrow it (Tab/Shift+Tabcycle; the "All" tab carries a category checkbox filter). Three providers ship (PROP-039 §10.4):
27
REQ. F2 opens a dialog whose content depends on the active mode:
28 REQ. A screen's controls are partitioned into focus groups — cohesive clusters the user steps between withTab(andShift+Tabbackwards). Within a group the group's own keys act (arrows move a radio selection, typing edits a field,Enteractivates a button). A screen with more than one focus group declares a Tab Order — the explicit sequenceTabcycles — per screen; a single-group screen has no Tab Order (Tabis inert there).Tabnever switches display tabs (that isShift+←/Shift+→, §5.3) and never activates a control (that isEnter).
29 REQ. Modals form a stack: opening a modal pushes it; it draws over everything below (each lower layer still visible as backdrop); input goes only to the top modal;Escpops the top modal (returning to the one beneath). A feature may push a modal over a modal — e.g. copy-settings (§10.2) → file-path (§10.5) is a depth-2 stack. The base screen is the bottom of the stack;Escat the bottom triggers quit-confirm (§7.4), it does not pop.
30 REQ. At the base screen,Escopens a confirm dialog ("Really quit?") withYes/Nobuttons;Yes/Enter-on-Yes quits,No/Escreturns. The confirmation exists because loading may be slow andEscis used pervasively in modals — an accidental singleEscmust not discard the session.
31Copy is «what I see is what I copy»: each screen serialises what it shows, as text by default, as Markdown or a file on request; a PNG export is reserved and says so. The interface state, the mode and the sort, shape and order choices, is saved under ~/.vibe/tree/ and restored at the next launch; a missing or broken file falls back to the defaults. The colour tier is detected once from the environment and can be overridden in the settings.
32 REQ.F6copies the current screen straight to the clipboard as text (the provider's default format).Shift+F6opens the copy-settings modal: aRadioGroupfor format (Markdown / PNG — and later PlantUML / Mermaid) and aRadioGroupfor destination (clipboard / file). Confirming with destination = file pushes the file-path modal (§10.5) over it (§6 stack).
33 REQ. Each screen (tree/sub-tables/tabs; the card) supplies a copy provider — an object that knows how to serialize what is currently shown into a copy format. A tree provider serializes the tree with its current display options; the card provider serializes the card's fields. Copy is "what I see is what I copy": in tabs mode only the active tab is serialized.
34
REQ. PNG export (a rasterized tree image; a designed package info-card image) is
reserved — selecting PNG opens the ComingSoon modal (§2.10) until the
rasterization (font + image crates) is built. Named non-goal-for-now (§12).
35 REQ. UI state persists under~/.vibe/tree/as one or more JSON files. Saved: the active mode (§4.4), the per-context sort + shape + block-order choices (§7.2, §3.3). On TUI launch the settings are loaded and the UI restored to them; a missing/corrupt file falls back to defaults (never a hard error). Writing is atomic; the directory is created if absent. (This is the machine-global~/.vibe/config root, distinct from the project's.vibe/.)
36 REQ. Tier detection is a pure function over the environment —detect_tier(colorterm: Option<&str>, term: Option<&str>) -> Tier($COLORTERMfirst, then$TERM;crosstermexposes no colour-count API). The TUI reads the env once at launch in a sanctioned spot and feeds the values in; the detected tier is overridable through the settings system (§9). The default is Tier 3: anything not explicitly dumb (an unset or emptyTERM/COLORTERM, or a genericTERM=xterm) is assumed truecolor, because every incumbent terminal renders truecolor and several (notably on Windows) do not advertise the capability via env at all — defaulting to Tier 3 keeps a modern terminal colourful instead of degrading it to mono. The lower tiers are the fallback (the degradation path), reached only when the environment explicitly advertises a lower capability (a 256-colourTERM, or an explicitly dumbTERM=linux/dumb); Tier 0 is never reached from an unset env. A genuinely limited terminal is otherwise overridden viavibe.tree.tier.
A terminal of its own
37vibe term launches vibeterm, a terminal application hosting a shell, so the screen can be used and inspected on its own. A vibe tree started inside vibeterm does not open a second window: it takes over the current terminal for the session.
38
REQ. vibe term launches the vibeterm terminal app hosting an interactive
shell, so the terminal can be used and eyeball-debugged standalone.
39 Avibe treelaunched inside vibeterm (this env present) does not spawn a second window — it upgrades the current terminal in place: the-t/ vibeterm launch resolves to the in-terminal console TUI here, so a plain shell becomes a "VibeTree terminal" for the session (PROP-036 §2.13). Outside vibeterm,-tstill opens the desktop app.
Looking at the screen from an agent
40An agent has no terminal, so the vibe aiui commands render the screen for it. The render verb builds the same model vibe tree builds at --path, drives a key script at a given size, 80x24 by default, and prints a snapshot in one of two formats: plain text, or cells, a run-length encoding of every cell with its glyph, colours and modifiers, for assertions about style. A key script is a space-separated list of key names, function keys, arrows, Enter, Esc, Tab and the rest, with a Shift+ prefix where needed. Keys that would leave the process or change the world, F4 and the copy keys, are refused rather than executed. The model verb projects the state without rendering at all, and the terminal verbs drive a live vibeterm session.
41
REQ. vibe aiui is the agent-facing command family. Its render-plane verb:
42 builds thevibe treemodel at--path(the same resolvervibe treeuses), drives--send(§3) at--size(default80x24), and prints the--formatsnapshot (§2, defaulttext) to stdout.
43 REQ. A rendered Buffer projects to one of two snapshot formats, the same schema every observation plane emits:
44cells— JSON:{cols, rows, rows:[[run,…],…]}where each run is{n, ch, fg?, bg?, mods?}—ncells of glyphchsharing a style, run-length encoded per row;fg/bgare#rrggbb(or an ANSI role name),modsthe set ofbold/dim/italic/underlined/reversedpresent. Enables style/colour assertions (e.g. "the active group's border run is the accent colour").
45 REQ. A key script is a space-separated list of key names driving the TUI before the snapshot. The grammar: function keysF1–F12; navigationUp,Down,Left,Right;Enter,Esc,Tab,BackTab,Space,Backspace; aShift+prefix on any of them (e.g.Shift+Left,Shift+Tab≡BackTab). Names are case-insensitive.
46 REQ. A scripted key that would escape the process or mutate the world is refused, not executed:F4(spawns the settings subprocess) andF6/Shift+F6(write the clipboard) are rejected by the key-script parser (§3).
47 REQ. The model-plane verb projects the TUI state — no rendering at all:
48 REQ. The terminal-plane verbs drive a live vibeterm control session:
49Underneath, every action of the interface has an address, action://<group>/<name>, and the observable state is one serialisable snapshot with the focus, the open modals, the visible rows and the enabled actions with their reasons, carrying no rendering types. A headless surface lists the actions, invokes one by address, returns the snapshot and runs the search, which is how an agent drives the screen without seeing it.
50 REQ. An action is named by anActionAddrwith the textual formaction://<group>/<name>[?<params>], where<group>is a dotted namespace (e.g.vibe.tree,core),<name>is a dotted/kebab identifier (e.g.copy.markdown,search.everywhere), and the optional<params>is a&-separatedkey=valuequery carrying invocation parameters (§5).(group, name)is the identity; the query is not part of identity. Examples:action://vibe.tree/copy.markdown,action://vibe.tree/sort?by=name&dir=asc,action://core/search.everywhere.
51
REQ. The observable UI state is a serialisable ModelView snapshot (focus, open modals, visible
rows, current tree/selection, the active tab, the set of enabled actions with their addresses +
reasons). It is a pure projection of the Model and carries no rendering types — so an AI reads
structured state, never pixels.
52 REQ (built; the in-process form ships). The core offers a headless AIUI surface:list_actions(filter?)(enumerate the registry with live enablement + reasons + params),invoke(addr, args)(the sameinvokeas §7.1),state() -> ModelView(§11.2), andsearch(query, tab?)(drive §10 programmatically).vibe-actions/src/aiui.rsimplementslist_actions+invoke, andvibe aiui stateprojects the §11.2ModelViewlive, citing this section in its own--help. Because enablement is pure + introspectable, the model is serialisable, and invocation is address-based, this surface is a thin adapter with a no-oppresent. The architecture must keep it a thin adapter — it was prototyped on the TUI, and the remaining JSON-RPC / MCP bindings are further adapters over the same surface. This is the founding AIUI goal (DO18): the headless surface is the reference; visual surfaces are projections.
Edge cases and rules
53English is the complete locale of the interface and the last fallback; another locale may lag and falls back silently. A package may ship its own locale file, and a language pack may replace one.
54 REQ. English is the default, mandatory-complete locale and the terminating fallback. Other locales may lag and fall back silently. Locale switch is atomic (ArcSwap<Catalogue>); a package may shiplocales/<lang>.ftl, and a dedicated language-pack package may override, merged by explicit priority (language-pack > package locale > inline English).