Files
Frank 99b9a6a17c docs(pi): unify split package trees; move live OMP pages to core
Two directories documented one concept (pi npm packages): top-level
packages/ (browser-use, permission-system) and pi/packages/ (peon,
web-access, subagents). Merged into pi/packages/ — one row per package
in a single index, detail pages kept as archive records. The root
index's 'OMP packages and skills' section was doubly wrong (packages/
was pi, not OMP); it becomes a Skills tooling section pointing at the
consolidated /skills/ tree.

Moved the two LIVE omp pages out of the retired-harness banner:
pi/statusline.md -> core/statusline.md, pi/subagent-limits.md ->
core/subagent-limits.md (both document omp behavior against
agent/config.yml + the installed bundle; pi/index.md itself admitted
the mis-shelf). Retargeted every inbound link (core/index, system/
dankserver, log x2, pi/index, settings x2, moved files' own xrefs).

Refs #29
2026-10-19 03:32:07 -07:00

200 lines
12 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
type: reference
title: OMP Status Line Customization
description: How the omp status line actually works — presets vs custom arrays, composer-shape segment routing, the context meter (box/band only), the 27-segment catalog, and the fleet's tuned layout.
tags:
- omp
- tui
- statusline
- config
timestamp: 2026-10-18
---
# OMP Status Line Customization
Ground truth from the installed bundle (`@oh-my-pi/pi-coding-agent/dist/cli.js`),
verified live on blankpad 2026-10-18 and re-audited line-by-line against the
minified source the same day (see [Audit log](#audit-log)). Status knobs live
in `agent/config.yml` under `statusLine:`; the two composer knobs
(`composer.shape`, `composer.tokenRate`) under `composer:`. The file is
file-watched — edits repaint a running session without restart (verify
visually: niri screenshot, see [niri capture recipe](../system/index.md)).
`/settings` (Appearance tab) previews these live with sample data.
## Preset vs custom — the trap
`statusLine.preset` ships whole configurations. **`leftSegments` /
`rightSegments` are ignored unless `preset: custom`** (`s ? user ?? preset
: preset` in the resolver). **`segmentOptions` is NOT gated** — user options
merge over the preset's own options under *every* preset.
| preset | leftSegments | rightSegments | separator |
|---|---|---|---|
| `default` | pi vim model mode collab stream path git pr context_pct cost | session_name | powerline-thin |
| `minimal` | vim path git | session_name mode context_pct | slash |
| `compact` | vim model mode git pr | session_name cost context_pct | powerline-thin |
| `full` | pi vim hostname model mode path git pr subagents | session_name cache_hit token_in token_out token_rate cache_read cost context_pct time_spent time | powerline |
| `nerd` | pi vim hostname model mode path git pr session subagents | session_name token_in token_out cache_read cache_write token_rate cost context_pct context_total time_spent time | powerline |
| `ascii` | vim model mode path git pr | session_name token_total cost context_pct | ascii |
| `custom` | your `statusLine.leftSegments` | your `statusLine.rightSegments` | powerline-thin |
The built-in default for `statusLine.leftSegments`/`rightSegments` themselves
is the `ascii`-era pair (`vim model mode path git pr` / `session_name
token_total cost context_pct`) — a config carrying those arrays with a named
preset is running the preset, not the arrays. The fleet runs `preset: custom`.
## Composer shape routes the segments
`composer.shape` decides *where* the two arrays render. Eight shapes ship in
the bundle; the plugin API can register more (`zG()` = built-ins + a live
registration map). Internally each shape carries `statusAttachment`,
`bottomBar`, `bottomBarGap`:
| shape | attach | bottom bar | leftSegments | rightSegments |
|---|---|---|---|---|
| `band` (fleet, default) | top-band | none | merged single row above editor, left | same row, right (`session_name` skipped — title rides the working row) |
| `box` | top-border | none | box top border | box top border |
| `claude` | top-rule-chip | left | bottom strip, left | top rule chip, right-aligned |
| `rule` | top-rule-chip | left + gap | like claude, gap above strip | like claude |
| `pi` | none | full | single bottom bar | single bottom bar |
| `borderless` | none | full | single bottom bar | single bottom bar |
| `field` / `rail` | none | full + gap (filled surface) | single bottom bar | single bottom bar |
The chip/strip layouts paint their rules as **session-accent fillers, not
meters** — the context meter exists only in `box` and `band`
(see next section). The top-rule chip renders right segments only, the bottom
strip left segments only.
The working row is composer chrome, not segment-addressable: session title +
shimmer, plus the tok/s readout when `composer.tokenRate: true` (default
false). The readout is estimated from streamed deltas, corrected by billed
output counts; the bundle also mentions a native-composer-bar placement in
filled styles.
## The context meter
`statusLine.contextLine` (enum `off` / `percentage` / `annotated` /
`embedded`, default `embedded`) tunes the rule between the segment groups.
The knob description says "box composer only" — **that is wrong**: the meter
renders in `box` **and** `band`; in every other shape the rule is a constant
session-accent filler regardless of this setting (live-verified claude with
`off` vs `embedded`: identical, and not context-proportional).
In box/band:
- `off` → plain accent rule (single color, full width).
- `embedded` → the inline labeled meter: `60% ——— × 200K`. Requires
`contextWindow > 0`, a `context_pct`/`context_total` segment present in
either array, and at least one non-context segment. The context segment(s)
are **hoisted out of the arrays** — they never render as text in this mode.
- `percentage` / `annotated` → unlabeled proportional meter painted into the
rule (live-verified in band: colored fill ∝ usage at the rule's right end);
`context_pct` stays a normal text segment.
Fill length ∝ `min(100, contextPercent)`; >100% paints the raw percent and
shortens the meter. Meter/segment color thresholds scale with window size —
warning at ≥ min(50 %, 150K tokens), purple ≥ min(70 %, 270K), error ≥
min(90 %, 500K); below that the theme's `statusLineContext` color.
## Segment universe (27)
`pi`, `status`, `model`, `mode`, `path`, `git`, `pr`, `subagents`,
`token_in`, `token_out`, `token_total`, `token_rate`, `cost`, `context_pct`,
`context_total`, `time_spent`, `time`, `session`, `hostname`, `cache_read`,
`cache_write`, `cache_hit`, `session_name`, `usage`, `collab`, `stream`, `vim`.
Zero/inactive segments render nothing — dead weight costs no width.
| segment | renders | trigger / notes |
|---|---|---|
| `pi` | brand glyph + turn-elapsed | while streaming; focused-agent ghost id (warning color) when a subagent is focused |
| `status` | hook/extension statuses, dot-joined | extension pushes via `ctx.ui.setStatus()` (ordered by key); suppressed in footer when `statusLine.showHookStatus: false` |
| `model` | display `name` + thinking level | suffix hidden by `segmentOptions.model.showThinkingLevel: false`; `statusLine.compactThinkingLevel: true` (default) swaps in a compact glyph; fast-mode, advisor/tariff badges attach here |
| `mode` | `Plan` / `Prewalk` / `Goal` / `Vibe` / `Loop …` | only in those modes (paused variants add a pause glyph); there is **no yolo/auto-approval display** in this segment |
| `path` | cwd or `project/worktree` label | see [Path segment](#path-segment-and-stripworkprefix) |
| `git` | branch + dirty badges | `+N` staged, `*N` unstaged, `?N` untracked (`segmentOptions.git.showStaged/showUnstaged/showUntracked`, default true); `showBranch: false` hides branch text and keeps badges (git icon carries them); color flips on dirty |
| `pr` | `#<number>` hyperlink | only once the session has an associated PR |
| `subagents` | count | hidden at 0; when subagents are actively running the composer paints its own live badge and this segment self-suppresses |
| `token_in` / `token_out` | cumulative session input/output tokens | hidden at 0; `cache_read` / `cache_write` likewise cumulative |
| `token_total` | `input + output + cache_write + orchestration_in + orchestration_out` | hidden at 0 |
| `token_rate` | `N.N tok/s` | hidden when not streaming (the working-row readout is a separate, `composer.tokenRate`-gated thing) |
| `cost` | session cost | includes subagent-tree + advisor cost; `↑`/`↓` peak/off-peak arrow for time-priced providers |
| `context_pct` | `NN.N%/200K` (or window alone when percent is unknown) | hoisted into the meter in `embedded` box/band; auto-compact speculation icon rides along |
| `context_total` | absolute context tokens | also hoisted by the embedded meter |
| `time_spent` | active session time | hidden under 1 s; this is *active* time, not wall clock |
| `time` | wall clock | `segmentOptions.time.format: "12h"/"24h"`, `showSeconds` |
| `session` | first 8 chars of session id (`new` before resume exists) | resume-by-id handle |
| `session_name` | session title, accent | in `band` it renders on the working row instead |
| `hostname` | short hostname | accent-tinted with session color |
| `usage` | rate-limit windows | 5h / daily / 7d / monthly bars + tier badge, when the provider reports them |
| `collab` | `collab:N` / `collab guest:N` | only in collab sessions |
| `stream` | `● LIVE <viewers>` / `● REC` | only while streaming/recording |
| `vim` | mode glyph/word | only while vim keybindings are active; selected-line count appended (` 3L`); `display` icon vs text comes from the vim runtime, not config |
## Path segment and `stripWorkPrefix`
`segmentOptions.path.stripWorkPrefix` (default **true** — the code tests
`!== false`) gates two behaviors in the `path` renderer:
1. **Worktree sessions:** `true` never shows the disk path — it renders the
logical `projectName/worktreeName` label (bare `projectName` when the
current branch is named after the worktree) with the worktree glyph.
`false` opts out and shows the real cwd with a folder glyph, plus a
` ↳ relativeRepoRoot` suffix inside a repo. Base-directory-only is not
renderable in either mode.
2. **All sessions:** `true` uses the pretty display path (scratch sessions
get the scratch-folder icon); `false` uses the raw absolute cwd.
`abbreviate` (default **true**: home → `~`; `false` keeps the full path —
also affects tool labels/errors per upstream docs) and `maxLength` (clip,
default 40) apply afterwards in both modes. The fleet runs `true` — logical
label, worktree glyph (verified live 2026-10-18; an earlier trial of `false`
for the full cwd was rejected by eye).
## Status knobs (registry-verified)
`statusLine.preset` · `.leftSegments` · `.rightSegments` · `.segmentOptions` ·
`.separator` (`powerline` `powerline-thin` `slash` `pipe` `block` `none`
`ascii`; default `powerline-thin`) · `.contextLine` · `.sessionAccent`
(default true; hashes the session name into the editor border *and* the
accent-tinted segments/meter color) · `.transparent` (terminal background,
no status bg fill) · `.showHookStatus` · `.compactThinkingLevel` (default
true). Plus `symbolPreset` at top level (`unicode` `nerd` `ascii` — segment
glyphs follow it).
## Fleet configuration (2026-10-18)
- Shape `band`: one merged status row. Left group: `session_name`→working
row (skipped), `vim`, `mode`, `hostname`, `path`, `git` (no branch text —
worktree branches are autogenerated names), `pr`. Right group (array order
= paint order, so the last entry hugs the screen edge): `model`, `cost`,
`context_pct`, `time_spent` — `context_pct` hoists out of the run to paint
the labeled inline meter (`60% ——— × 200K`) between the groups under
`contextLine: embedded`.
- Deliberately cut: `token_rate` (working-row readout covers it), `time`
(panel clock), `cache_*` (noise), `context_total` (implied by the meter),
`subagents` (live badge supersedes it), `session`, `token_in`/`token_out`
(cumulative totals are in `/session` anyway).
- `models.yml`: frank-archon's display `name` slimmed to `"frank-archon"`
(endpoint suffix dropped); frank-dragoon/frank-zealot keep their endpoint
suffixes. `name:` is free-form display, `id:` is the wiring — renaming ids
is the cutover procedure, see [Pi Models](/pi/models.md).
## Audit log
2026-10-18 full-source re-audit corrected the first pass of this page:
- ~~claude shape's top rule is the context meter~~ — **false**. The
meter code path runs only for `box`/`band`; claude/rule/pi/borderless/
field/rail rules are constant session-accent fillers. (Earlier reading
mistook the accent rule for a meter.)
- ~~working-row composer-bar variant is "Tern-only"~~ — **false**; the
tok/s readout is the `composer.tokenRate` option (default false), title
placement is composer chrome.
- ~~`segmentOptions` gated to `preset: custom`~~ — **false**; options merge
under every preset, only the segment *arrays* are gated.
- ~~`mode` covers yolo~~ — **false**; states are Plan/Prewalk/Goal/Vibe/Loop.
- `contextLine` "(box composer only)" — the *bundle description* is wrong;
band honors it too (verified live: `off` plain rule, `percentage` rule
meter, `embedded` inline meter, screenshots + code).