# The dependency tree on screen {#root}

@status:doc/work @audience:user,author,agent

[p01] `vibe 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.

[p02]
```sh
vibe tree --plain --path hello-vibe
```

```output
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 {#what-it-shows}

[p03] The command reads the committed [lock file](../glossary/index.xml#lock-file), the [manifests](../glossary/index.xml#manifest) 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](../glossary/index.xml#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.

> [p04] `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.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-cli/PROP-036#TREE-ANSWER>

> [p05] It
>   operates on the workspace discovered from `--path` (default `.`), reading the
>   committed `vibe.lock`, the node manifests, and the generated boot artifacts.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-cli/PROP-036#TREE-INPUTS>

> [p06] `vibe tree` is a **read-only** analyzer over the current project. It mutates
>   nothing (REQ: never writes to the tree, manifests, lockfile, or artifacts).
>
> <spec://org.vibevm.core/vibevm/modules/vibe-cli/PROP-036#TREE-READ-ONLY>

> [p07] Each package is one row. Columns, left to right (REQ: this order):
>
> <spec://org.vibevm.core/vibevm/modules/vibe-cli/PROP-036#ROW-PER-PACKAGE>

> [p08] The `load` value is the lane a package's boot snippet **actually lands in**, read
> from the committed artifacts (REQ):
>
> <spec://org.vibevm.core/vibevm/modules/vibe-cli/PROP-036#EFFECTIVE-FROM-ARTIFACTS>

> [p09] `vibe tree` renders 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 qualified `group/name` (REQ).
>
> <spec://org.vibevm.core/vibevm/modules/vibe-cli/PROP-036#DAG-RENDERING>

> [p10] The status line shows the size of the statically-compiled lane: the byte count
>   and line count of `STATIC.md` (REQ).
>
> <spec://org.vibevm.core/vibevm/modules/vibe-cli/PROP-036#STATIC-SIZE-INDICATOR>

## Three outputs {#three-outputs}

[p11] On 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.

> [p12] **interactive TUI** — the default on a tty (§2.11);
>
> <spec://org.vibevm.core/vibevm/modules/vibe-cli/PROP-036#OUT-TUI>

> [p13] **plain** — a static ASCII tree when not on a tty, or under `--plain`.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-cli/PROP-036#OUT-PLAIN>

> [p14] **`--json`** — the machine surface (§2.7), the same data a GUI consumes;
>
> <spec://org.vibevm.core/vibevm/modules/vibe-cli/PROP-036#OUT-JSON>

> [p15] `vibe tree --json` emits one object, the same data model the TUI renders, valid
>   against the shipped schema (REQ: `schema_version = 1`).
>
> <spec://org.vibevm.core/vibevm/modules/vibe-cli/PROP-036#JSON-CONTRACT>

[p16] Two 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`.

> [p17] **root-drift** — `vibe.lock` `meta.root_dependencies` disagrees with the root
>   `vibe.toml` `[requires.packages]` (the lock is behind).
>
> <spec://org.vibevm.core/vibevm/modules/vibe-cli/PROP-036#DIAG-ROOT-DRIFT>

> [p18] **stale-artifacts** — the committed lanes disagree with a fresh `EffectiveBoot`
>   recompute (the tree needs `vibe reinstall`).
>
> <spec://org.vibevm.core/vibevm/modules/vibe-cli/PROP-036#DIAG-STALE-ARTIFACTS>

## The screen {#the-screen}

[p19] The 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.

> [p20] REQ. `vibe tree` has three display modes, each a Tree-widget (§3.1) configuration
> (§3.2) — none is a flat list:
>
> <spec://org.vibevm.core/vibevm/modules/vibe-cli/PROP-037#THREE-MODES>

> [p21] REQ. The pipeline offers three shapes, selectable per context on the F2 sort menu
> (§7.2), **default = (a)**:
>
> <spec://org.vibevm.core/vibevm/modules/vibe-cli/PROP-037#THREE-SHAPES>

> [p22] 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).
>
> <spec://org.vibevm.core/vibevm/modules/vibe-cli/PROP-037#MODE-SELECT-REQ>

> [p23] REQ. Primary actions are on function keys: `F1` Search Everywhere (§7.3), `F2`
> sort menu (§7.2), `F3` mode menu (§7.1), `F4` settings (opens the `vibe prefs`
> settings UI — PROP-041), `F6` copy / `Shift+F6` copy-settings (§10.2). The footer
> lists the active keys with short labels; **`Shift` is written as `↑`** (e.g.
> `Shift+F6` → `↑F6`) to keep hints short.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-cli/PROP-037#F-KEY-SCHEME>

[p24] Inside 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.

> [p25] REQ. Within a tree: `↑`/`↓` move selection (+ scroll), `←`/`→` pan horizontally,
> `Space` folds/unfolds the selected node, `Enter` opens 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).
>
> <spec://org.vibevm.core/vibevm/modules/vibe-cli/PROP-037#TREE-KEYS-REQ>

> [p26] REQ. `F1` opens the **Search Everywhere** window — the `vibe-actions` Search
> 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+Tab` cycle; the "All" tab carries a category checkbox filter). Three
> providers ship (PROP-039 §10.4):
>
> <spec://org.vibevm.core/vibevm/modules/vibe-cli/PROP-037#F1-SEARCH-REQ>

> [p27] REQ. `F2` opens a dialog whose content depends on the active mode:
>
> <spec://org.vibevm.core/vibevm/modules/vibe-cli/PROP-037#F2-SORT-MENU-REQ>

> [p28] REQ. A screen's controls are partitioned into **focus groups** — cohesive
> clusters the user steps between with `Tab` (and `Shift+Tab` backwards). Within a
> group the group's own keys act (arrows move a radio selection, typing edits a
> field, `Enter` activates a button). A screen with more than one focus group
> declares a **Tab Order** — the explicit sequence `Tab` cycles — per screen; a
> single-group screen has no Tab Order (`Tab` is inert there). `Tab` never switches
> display tabs (that is `Shift+←`/`Shift+→`, §5.3) and never activates a control
> (that is `Enter`).
>
> <spec://org.vibevm.core/vibevm/modules/vibe-cli/PROP-037#FOCUS-GROUPS-REQ>

> [p29] 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; `Esc` pops 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; `Esc` at the bottom
> triggers quit-confirm (§7.4), it does not pop.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-cli/PROP-037#MODAL-STACK-REQ>

> [p30] REQ. At the base screen, `Esc` opens a confirm dialog ("Really quit?") with
> `Yes`/`No` buttons; `Yes`/`Enter`-on-Yes quits, `No`/`Esc` returns. The
> confirmation exists because loading may be slow and `Esc` is used pervasively in
> modals — an accidental single `Esc` must not discard the session.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-cli/PROP-037#QUIT-CONFIRM-REQ>

[p31] Copy 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.

> [p32] REQ. `F6` copies the current screen straight to the clipboard as text (the
> provider's default format). `Shift+F6` opens the **copy-settings** modal: a
> `RadioGroup` for **format** (Markdown / PNG — and later PlantUML / Mermaid) and a
> `RadioGroup` for **destination** (clipboard / file). Confirming with destination
> = file pushes the file-path modal (§10.5) over it (§6 stack).
>
> <spec://org.vibevm.core/vibevm/modules/vibe-cli/PROP-037#COPY-FLOW-REQ>

> [p33] 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.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-cli/PROP-037#COPY-PROVIDERS-REQ>

> [p34] 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).
>
> <spec://org.vibevm.core/vibevm/modules/vibe-cli/PROP-037#PNG-RESERVED>

> [p35] 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/`.)
>
> <spec://org.vibevm.core/vibevm/modules/vibe-cli/PROP-037#SETTINGS-PERSISTENCE>

> [p36] REQ. Tier detection is a **pure function** over the environment —
> `detect_tier(colorterm: Option<&str>, term: Option<&str>) -> Tier` (`$COLORTERM`
> first, then `$TERM`; `crossterm` exposes 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 empty `TERM`/`COLORTERM`, or a generic
> `TERM=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-colour `TERM`, or an explicitly dumb `TERM=linux`/`dumb`);
> Tier 0 is never reached from an unset env. A genuinely limited terminal is
> otherwise overridden via `vibe.tree.tier`.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-cli/PROP-037#TIER-DETECTION>

## A terminal of its own {#the-terminal-app}

[p37] `vibe 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.

> [p38] REQ. `vibe term` launches the **vibeterm** terminal app hosting an interactive
>   shell, so the terminal can be used and eyeball-debugged standalone.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-cli/PROP-042#TERM-LAUNCHER>

> [p39] A `vibe tree` launched
>   **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, `-t` still opens the desktop
>   app.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-cli/PROP-042#IN-PLACE-UPGRADE>

## Looking at the screen from an agent {#for-agents}

[p40] An 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.

> [p41] REQ. `vibe aiui` is the agent-facing command family. Its render-plane verb:
>
> <spec://org.vibevm.core/vibevm/modules/vibe-cli/PROP-042#AIUI-FAMILY>

> [p42] builds the `vibe tree` model at `--path` (the same resolver `vibe tree` uses),
>   drives `--send` (§3) at `--size` (default `80x24`), and prints the `--format`
>   snapshot (§2, default `text`) to stdout.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-cli/PROP-042#RENDER-VERB-SEMANTICS>

> [p43] REQ. A rendered Buffer projects to one of two **snapshot formats**, the same
> schema every observation plane emits:
>
> <spec://org.vibevm.core/vibevm/modules/vibe-cli/PROP-042#SNAPSHOT-FORMATS>

> [p44] **`cells`** — JSON: `{cols, rows, rows:[[run,…],…]}` where each **run** is
>   `{n, ch, fg?, bg?, mods?}` — `n` cells of glyph `ch` sharing a style, run-length
>   encoded per row; `fg`/`bg` are `#rrggbb` (or an ANSI role name), `mods` the set
>   of `bold`/`dim`/`italic`/`underlined`/`reversed` present. Enables style/colour
>   assertions (e.g. "the active group's border run is the accent colour").
>
> <spec://org.vibevm.core/vibevm/modules/vibe-cli/PROP-042#FMT-CELLS>

> [p45] REQ. A **key script** is a space-separated list of key names driving the TUI
>   before the snapshot. The grammar: function keys `F1`–`F12`; navigation `Up`,
>   `Down`, `Left`, `Right`; `Enter`, `Esc`, `Tab`, `BackTab`, `Space`, `Backspace`;
>   a `Shift+` prefix on any of them (e.g. `Shift+Left`, `Shift+Tab` ≡ `BackTab`).
>   Names are case-insensitive.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-cli/PROP-042#KEY-SCRIPT-GRAMMAR>

> [p46] REQ. A scripted key that would **escape the process or mutate the world** is
>   refused, not executed: `F4` (spawns the settings subprocess) and `F6`/`Shift+F6`
>   (write the clipboard) are rejected by the key-script parser (§3).
>
> <spec://org.vibevm.core/vibevm/modules/vibe-cli/PROP-042#SIDE-EFFECT-KEYS-REFUSED>

> [p47] REQ. The **model-plane** verb projects the TUI state — no rendering at all:
>
> <spec://org.vibevm.core/vibevm/modules/vibe-cli/PROP-042#MODEL-VERB>

> [p48] REQ. The **terminal-plane** verbs drive a live vibeterm control session:
>
> <spec://org.vibevm.core/vibevm/modules/vibe-cli/PROP-042#TERMINAL-VERBS>

[p49] Underneath, 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.

> [p50] REQ. An action is named by an **`ActionAddr`** with the textual form
> `action://<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 `&`-separated `key=value` query 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`.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-actions/PROP-039#ADDRESS-GRAMMAR>

> [p51] 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.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-actions/PROP-039#MODEL-VIEW-DEF>

> [p52] 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 same `invoke` as §7.1), `state() -> ModelView` (§11.2), and
> `search(query, tab?)` (drive §10 programmatically). `vibe-actions/src/aiui.rs` implements
> `list_actions` + `invoke`, and `vibe aiui state` projects the §11.2 `ModelView` live, 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-op
> `present`. 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.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-actions/PROP-039#AIUI-REFERENCE>

## Edge cases and rules {#edge-cases}

[p53] English 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.

> [p54] 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 ship
> `locales/<lang>.ftl`, and a dedicated language-pack package may override, merged by explicit priority
> (language-pack > package locale > inline English).
>
> <spec://org.vibevm.core/vibevm/modules/vibe-actions/PROP-039#I18N-FALLBACK-LAW>

