Customizes the default pi editor with a powerline-style status bar, welcome overlay, and built-in or AI-generated "vibes" for loading messages. Inspired by Powerlevel10k and oh-my-pi.
Editor stash — Press Alt+S to save your editor content and clear the editor, type a quick prompt, and your stashed text auto-restores when the agent finishes. Toggles between stash, pop, and update-existing-stash. A stash indicator appears in the statusbar while text is stashed.
Working Vibes — Themed loading messages from built-in packs (whimsical: star-wars, star-trek, etc.) or AI-generated. Set the vibe preset with /vibe preset whimsical for instant themed messages, or /vibe source generated for contextual AI vibes. Safe mode on by default, shimmer animation included.
Welcome overlay — Branded splash screen shown as centered overlay on startup. Shows gradient logo, model info, keyboard tips, loaded AGENTS.md/extensions/skills/templates counts, and recent sessions. Auto-dismisses after 30 seconds or on any key press.
Rounded box design — Status renders directly in the editor's top border, not as a separate footer.
Fixed editor cluster — In interactive TUI sessions, chat/feed content scrolls above the fixed Pi working/status line, statusbar rows, editor, ghost suggestions, bash transcript, and last-prompt/status rows. Scroll chat with the mouse wheel, PageUp/PageDown, Command+PageUp/PageDown, Ctrl+Shift+Up/Down, or message-jump shortcuts; the editor stays put. Drag text to copy it, drag selection to the viewport edge to scroll, double-click a line to select it, and right-click to open the terminal menu. Use /statusbar fixed-editor off for Pi’s regular scrolling layout, or /statusbar mouse-scroll off for native terminal selection.
Live thinking level indicator — Shows current thinking level (think:off, think:med, etc.) with per-level colors. High and xhigh levels use a rainbow effect inspired by Claude Code's ultrathink.
Smart defaults — Nerd Font auto-detection for iTerm, WezTerm, Kitty, Ghostty, and Alacritty with ASCII fallbacks. Colors matched to oh-my-pi's dark theme.
Git integration — Async status fetching with 1s cache TTL. Automatically invalidates on file writes/edits. Shows branch, staged (+), unstaged (*), and untracked (?) counts.
Context awareness — Color-coded warnings at 70% (yellow) and 90% (red) context usage. During streaming, the context segment refreshes from live assistant usage instead of waiting for the next turn. Auto-compact indicator when enabled. If pi-custom-compaction is installed and enabled, the statusbar automatically hides native context segments so the footer does not show stale post-summary usage.
Token intelligence — Smart formatting (1.2k, 45M), subscription detection (shows "(sub)" vs dollar cost).
Sticky bash mode — Toggle bash mode with ctrl+shift+b or /bash-mode. It keeps a managed shell session alive for the current pi session, shows a dedicated shell_mode segment, streams command output into an embedded transcript below the editor, and lets cd or exported state persist across commands.
Shell ghost suggestions — Bash mode is now ghost-first. Successful per-project shell history is the primary source, while deterministic path and git continuations can still extend an existing command. Shell-native completion probes are disabled so !command predictions never spawn interactive shell completion subprocesses. At command position, short stems first resolve from the newest successful local command, can use guarded global shell history for high-confidence heads like git, and finally fall back to a tiny curated default set when history is absent. Right now that curated set is g → git status and c → cd ... If the bash prompt is empty, bash mode shows the newest successful project-history ghost suggestion when one exists, otherwise it stays empty. The same inline predictions now also kick in for one-off !command and !!command prompts. Right Arrow or Tab accepts ghost text into the editor, and Enter runs the current shell command.
pi install npm:@bumpyclock/pi-statusbarRestart pi to activate.
Entry point: index.ts is the stable Pi runtime entry, wired via package.json → pi.extensions → "./index.ts". It bootstraps the extension and should not be split or renamed.
Root files are intentionally minimal:
index.ts— stable Pi runtime entry, wired viapackage.json → pi.extensions → "./index.ts".types.ts— shared type contracts used across implementation modules and tests.
Domain folders contain the actual implementation:
| Folder | Purpose |
|---|---|
core/ |
Settings merge, shortcuts, stash helpers, prompt/stash history |
statusbar/ |
Config parsing, presets, segments, layout, render scheduling, git/context helpers |
theme/ |
Theme config, semantic colors, ANSI helpers, icons, separators |
commands/ |
Slash-command handlers (/statusbar, /vibe, /bash-mode) |
vibes/ |
Vibe config, packs, picker, animation, manager |
welcome/ |
Welcome overlay component and dismiss scheduler |
bash-mode/ |
Shell session, completion, history, transcript, editor |
fixed-editor/ |
Terminal compositor, mouse handling, cluster rendering |
New internal implementation should live in the appropriate domain folder. Root TypeScript files should stay limited to index.ts and types.ts; package publishing lists those files explicitly plus each runtime domain folder.
Activates automatically. Toggle with /statusbar, switch presets with /statusbar <name>, fixed-editor mode with /statusbar fixed-editor on|off|toggle, and wheel mode with /statusbar mouse-scroll on|off|toggle.
Fixed editor and mouse-scroll handling are both on by default.
/statusbar fixed-editor off— return to Pi’s regular scrolling layout/statusbar fixed-editor on— re-enable the fixed editor/statusbar fixed-editor toggle— switch between the two/statusbar mouse-scroll off— keep fixed editor on, but let the terminal handle native selection
You can also set the defaults explicitly in ~/.pi/agent/settings.json or project-local .pi/settings.json:
{
"statusbar": {
"preset": "default",
"fixedEditor": true,
"mouseScroll": true
}
}Set "fixedEditor": false to return to Pi’s regular scrolling layout. Set "mouseScroll": false if you want native terminal selection instead of fixed-editor mouse handling.
| Preset | Description |
|---|---|
default |
Model, thinking, path (basename), git, context, tokens, cost |
minimal |
Just path (basename), git, context |
compact |
Model, git, cost, context |
full |
Everything including hostname, time, abbreviated path |
nerd |
Maximum detail for Nerd Font users |
ascii |
Safe for any terminal |
custom |
Reserved built-in preset with selectable baseline segments (model, shell_mode, path, git, tokens/cost/context); this name cannot be used for user-defined presets |
Environment: STATUSBAR_NERD_FONTS=1 to force Nerd Fonts, =0 for ASCII.
Preset selection is saved to ~/.pi/agent/settings.json under statusbar and restored on startup.
Run /statusbar default to switch back to the default preset.
Define named presets in statusbar.presets when you want custom layouts without editing presets.ts. The selected statusbar.preset can be either a built-in preset or one of your user-defined preset names. Built-in names always win on conflicts, so use names like daily, work, or debug instead of redefining default.
{
"statusbar": {
"preset": "daily",
"presets": {
"daily": {
"extends": "default",
"leftSegments": [
"model",
"thinking",
"shell_mode",
"path",
"git",
"custom:ci"
],
"rightSegments": ["context_pct", "cache_read", "cost"],
"secondarySegments": ["extension_statuses"],
"separator": "powerline-thin",
"segmentOptions": {
"path": { "mode": "basename" },
"git": {
"showBranch": true,
"showStaged": true,
"showUnstaged": true,
"showUntracked": true
}
}
}
},
"customItems": [
{
"id": "ci",
"statusKey": "ci-status",
"prefix": "CI",
"color": "warning"
}
]
}
}User preset fields:
extends(optional): built-in or user preset to inherit from; defaults todefaultleftSegments,rightSegments,secondarySegments(optional): segment order arrays; omitted arrays inherit from the parent, while[]clears that groupseparator(optional):powerline,powerline-thin,slash,pipe,block,none,ascii,dot,chevron, orstarsegmentOptions(optional):model.showThinkingLevel,path.mode,path.maxLength,git.showBranch,git.showStaged,git.showUnstaged,git.showUntracked,time.format, andtime.showSeconds
Available segment ids: model, thinking, shell_mode, path, git, subagents, token_in, token_out, token_total, cost, context_pct, context_total, time_spent, time, session, hostname, cache_read, cache_write, extension_statuses, and custom:<id> for a configured custom item.
leftSegments and rightSegments are concatenated left-to-right into one primary responsive flow; they are not fixed screen-left and screen-right alignment zones. secondarySegments are lower-priority segments that can move between rows depending on terminal width.
Custom item placement can be controlled in two places. If a preset includes custom:<id> in leftSegments, rightSegments, or secondarySegments, that preset-level placement wins. If no preset segment array references the custom item, customItems.position is used as the fallback placement. customItems.position is still supported; preset arrays are the more explicit layout control.
When merging global ~/.pi/agent/settings.json with project-local .pi/settings.json, the extension settings loader merges object fields (like statusbar.presets and segmentOptions) field-by-field, but segment arrays (leftSegments, rightSegments, secondarySegments) from project settings completely replace the global arrays. This settings file merge is separate from preset extends inheritance, where omitted segment arrays inherit from the parent preset. Restart pi or start a new session after editing settings, because settings changes are read during startup/session setup.
You can promote any extension status key into its own dedicated statusbar item. This gives you a general way to register your own status items without changing this extension.
- Any extension can publish status text through
ctx.ui.setStatus("my-key", "...value..."). - Configure
statusbar.customItemsto place those keys on the left, right, or secondary row.
{
"statusbar": {
"preset": "default",
"customItems": [
{
"id": "ci",
"statusKey": "ci-status",
"position": "right",
"prefix": "CI",
"color": "warning"
},
{
"id": "review",
"position": "secondary",
"hideWhenMissing": false,
"prefix": "review"
}
]
}
}customItems fields:
id(required): unique item id (a-z,A-Z,0-9,_,-)statusKey(optional): extension status key to read, defaults toidposition(optional):left,right, orsecondary(defaultright)prefix(optional): text shown before the live status valuecolor(optional): any Pi theme color (warning,accent, etc.) or hex (#RRGGBB)hideWhenMissing(optional): hide item when no status is present (defaulttrue)excludeFromExtensionStatuses(optional): omit this key from the aggregateextension_statusessegment (defaulttrue)
For shorthand config, "statusbar": "default" selects the default preset.
Toggle bash mode with either:
ctrl+shift+b/bash-mode on/bash-mode off/bash-mode toggle
Reset the managed shell with /bash-reset.
While bash mode is active:
- Enter runs the current shell command
- Right Arrow accepts ghost text into the editor without running it
- Tab accepts the current ghost suggestion when one exists; otherwise it does nothing
- Up and Down browse matching shell history
escapeexits bash mode and returns to normal prompt modectrl+cinterrupts the active shell job before falling back to normal pi behavior
The managed shell is persistent for the current pi session. Command output appears in a transcript below the editor, and shell cwd changes are reflected in the footer path and shell_mode segment. If the bash prompt is empty, bash mode shows the newest successful project-history ghost suggestion immediately when one exists, including right after mode entry or after the prompt is cleared again. One-off !command and !!command prompts reuse the same shell prediction pipeline, including ghost text. Mode entry stays quiet: there is no automatic or manual dropdown completion surface, and ghost suggestions do not run shell-native completion probes.
In ~/.pi/agent/settings.json:
{
"bashMode": {
"toggleShortcut": "ctrl+shift+b",
"transcriptMaxLines": 2000,
"transcriptMaxBytes": 524288
}
}Use Alt+S / Option+S as a quick stash toggle while drafting. It keeps one active stash and clears the editor when stashing.
| Editor | Stash | Alt+S result |
|---|---|---|
| Has text | Empty | Stash current text, clear editor |
| Empty | Has stash | Restore stash into editor |
| Has text | Has stash | Update stash with current text, clear editor |
| Empty | Empty | Show "Nothing to stash" |
Auto-restore after an agent run only happens when the editor is still empty. If you typed meanwhile, the stash is preserved.
The stash indicator appears in the statusbar (on presets with extension_statuses). Active stash is still session-local and resets on session switch / disable, but stash history is persisted to ~/.pi/agent/pi-statusbar/stash-history.json so it survives restarts.
Open prompt history with either:
ctrl+alt+h/stash-history
Prompt history now has two sources:
- stashed prompts — up to 12 recent stashed prompts (newest first)
- recent project prompts — up to 50 recent user-submitted prompts pulled from pi sessions in the current project folder
Selecting an entry inserts it into the editor. If the editor already has text, you can choose Replace, Append, or Cancel.
ctrl+alt+c— copy full editor contentctrl+alt+x— cut full editor content (copy, then clear)cmd+up— scroll the fixed-editor chat viewport upcmd+down— scroll the fixed-editor chat viewport downcmd+shift+up— move the editor cursor to the start of the first linecmd+shift+down— move the editor cursor to the end of the last linectrl+shift+u— jump the fixed-editor chat viewport to the previous user messagectrl+shift+i— jump the fixed-editor chat viewport to the next user messagectrl+alt+,— jump the fixed-editor chat viewport to the previous LLM messagectrl+alt+.— jump the fixed-editor chat viewport to the next LLM messagectrl+shift+g— jump the fixed-editor chat viewport to the bottom Copy/cut actions do not modify stash state or stash history. Dragging files, folders, images, or screenshots from Finder into the custom editor inserts their path strings. Chat jumps require fixed-editor mode because they use its app-owned scroll viewport. Submitting editor text also returns that viewport to the bottom so new output stays in view.
You can override shortcut keys in ~/.pi/agent/settings.json:
{
"statusbarShortcuts": {
"stashHistory": "ctrl+alt+h",
"copyEditor": "ctrl+alt+c",
"cutEditor": "ctrl+alt+x",
"jumpPreviousUserMessage": "ctrl+shift+u",
"jumpNextUserMessage": "ctrl+shift+i",
"jumpPreviousLlmMessage": "ctrl+alt+,",
"jumpNextLlmMessage": "ctrl+alt+.",
"jumpChatBottom": "ctrl+shift+g",
"scrollChatUp": "cmd+up",
"scrollChatDown": "cmd+down",
"editorStart": "cmd+shift+up",
"editorEnd": "cmd+shift+down"
}
}After changing bindings, run /reload. Invalid bindings, reserved key conflicts (like Alt+S), or duplicate conflicts automatically fall back to safe defaults. cmd and command are accepted aliases for Pi's super modifier for the documented Command navigation keys; unsupported Command-letter bindings such as cmd+c are ignored instead of matching plain text input. Some terminals, including Ghostty, bind Command+Arrow themselves; remap those terminal keys to send \x1b[1;9A / \x1b[1;9B for chat scrolling and \x1b[1;10A / \x1b[1;10B for editor-boundary navigation if you want Pi to receive them.
Transform boring "Working..." messages into themed phrases that match your style. Vibes are configured entirely under statusbar.vibe in settings — if you previously used the standalone pi-whimsical extension, it can be retired or disabled after enabling vibes here.
Vibe presets are named bundles of packs; whimsical is the only built-in vibe preset and enables every built-in pack. This is separate from statusbar layout presets such as default, minimal, and compact. Packs are individual themed message sets such as star-wars or star-trek. Themes are freeform strings used by the AI-generated prompt; /vibe generate <theme> stores that theme by rewriting the generated prompt template and switching source to "generated" — there is no separate theme setting. Running /vibe generate star trek bakes "star trek" into the prompt and enables generated vibes in one step.
/vibe preset whimsical → Activate the built-in whimsical vibe preset
/vibe pack list → Show available packs and disabled status
/vibe pack disable star-wars → Disable a specific pack
/vibe pack enable star-wars → Re-enable a specific pack
/vibe pack reset → Re-enable all packs
/vibe safe on|off → Toggle safe mode (filters unsafe messages, default: on)
/vibe animation shimmer|none → Shimmer animation on working message (default: shimmer)
/vibe source generated → Switch to AI-generated vibes
/vibe generate star trek → Set AI-generated vibe theme/source to "star trek"
/vibe source packs → Switch back to built-in packs
/vibe off → Disable vibes entirely (back to "Working...")
/vibe → Show current statusIn ~/.pi/agent/settings.json under statusbar.vibe:
{
"statusbar": {
"vibe": {
"source": "packs",
"disabledPacks": ["star-wars"],
"safeMode": true,
"animation": "shimmer",
"generated": {
"model": "openai-codex/gpt-5.4-mini",
"refreshInterval": 30,
"prompt": "Generate a 2-4 word {theme} themed loading message for: {task}"
}
}
}
}| Source | Description | Pros | Cons |
|---|---|---|---|
packs |
Built-in curated message packs | Instant, zero cost, works offline | Not contextual |
generated |
On-demand AI generation | Contextual, hints at actual task | Model-dependent cost and latency |
Pack source (source: "packs") uses curated message sets, each with a distinct theme (star-wars, star-trek, etc.). Messages tagged unsafe are filtered when safe mode is on (the default). A message is tagged or treated as unsafe when the shared profanity matcher catches obvious or obfuscated profanity (fuck, f***ing, shit, bitch, bullshit, dick, goddamn). Tags are applied automatically at pack definition time and checked again at runtime; safe mode removes those messages from the pick pool. Disable individual packs with /vibe pack disable <id>, re-enable one with /vibe pack enable <id>, or re-enable all with /vibe pack reset.
Generated source calls an AI model on each refresh. Prompt template variables:
{theme}— the default generated theme placeholder;/vibe generate <theme>writes the chosen theme into the prompt (e.g., "star trek"){task}— context hint (user prompt, then agent's response text or tool info on refresh){exclude}— recent vibes to avoid (auto-populated)
How it works:
- When you send a message, a themed message appears
- If
sourceis"packs": picks from pre-built messages; shimmer is applied whenanimationis"shimmer" - If
sourceis"generated": calls AI in the background with a fixed 3s timeout - If
sourceis"generated": during long tasks, refreshes on tool calls (rate-limited bygenerated.refreshInterval, default 30s) - If
sourceis"generated": cost and latency depend on the configuredgenerated.model
The thinking segment shows live updates when you change thinking level:
| Level | Display | Color |
|---|---|---|
| off | think:off |
gray |
| minimal | think:min |
purple-gray |
| low | think:low |
blue |
| medium | think:med |
teal |
| high | think:high |
rainbow |
| xhigh | think:xhigh |
rainbow |
The path segment supports three modes:
| Mode | Example | Description |
|---|---|---|
basename |
pi-statusbar |
Just the directory name (default) |
abbreviated |
…/extensions/pi-statusbar |
Full path with home abbreviated and length limit |
full |
~/.pi/agent/extensions/pi-statusbar |
Complete path with home abbreviated |
Configure via preset options: path: { mode: "full" }
model · thinking · shell_mode · path · git · subagents · token_in · token_out · token_total · cost · context_pct · context_total · time_spent · time · session · hostname · cache_read · cache_write · extension_statuses
Note: The
subagentssegment is currently inert — it renders nothing and is hidden. It is a placeholder awaiting Pi platform support for subagent tracking. Including it in a preset layout is harmless but has no visible effect.
powerline · powerline-thin · slash · pipe · dot · chevron · star · block · none · ascii
Colors are configurable via pi's theme system. Each preset defines its own color scheme, and you can override individual colors and icons with a theme.json file in the extension directory.
| Semantic | Theme Color | Description |
|---|---|---|
model |
#d787af |
Model name |
shellMode |
accent |
Bash mode segment |
path |
#00afaf |
Directory path |
gitClean |
success |
Git branch (clean) |
gitDirty |
warning |
Git branch (dirty) |
thinking |
thinkingOff |
Thinking level (off) |
thinkingMinimal |
thinkingMinimal |
Thinking level (minimal) |
thinkingLow |
thinkingLow |
Thinking level (low) |
thinkingMedium |
thinkingMedium |
Thinking level (medium) |
context |
dim |
Context usage |
contextWarn |
warning |
Context usage >70% |
contextError |
error |
Context usage >90% |
cost |
text |
Cost display |
tokens |
muted |
Token counts |
Create ~/.pi/agent/extensions/pi-statusbar/theme.json:
{
"colors": {
"pi": "#ff5500",
"model": "accent",
"shellMode": "accent",
"path": "#00afaf",
"gitClean": "success",
"thinking": "thinkingOff",
"thinkingMinimal": "thinkingMinimal",
"thinkingLow": "thinkingLow",
"thinkingMedium": "thinkingMedium"
},
"icons": {
"auto": "↯",
"warning": ""
}
}Colors can be:
- Theme color names:
accent,muted,dim,text,success,warning,error,border,borderAccent,borderMuted - Hex colors:
#ff5500,#d787af
Icons can be any string, including "" when you want to suppress a specific glyph entirely.
See theme.example.json for all available options.
