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

12 KiB
Raw Permalink Blame History

type, title, description, tags, timestamp
type title description tags timestamp
reference OMP Status Line Customization 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.
omp
tui
statusline
config
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). 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). /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
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.

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).