@nicknisi/pi-statusline
v0.2.2
Published
Custom footer: model, cost, context usage, git branch, lines changed, tmux status files
Maintainers
Readme
@nicknisi/pi-statusline
Custom footer for pi's TUI, replacing the default footer with a single-line statusline showing model, session cost, lines changed, Anthropic usage limits, context window usage, and git branch/PR. Also writes tmux status files (working/done/idle) to ~/.cache/pi-status/ so tmux sidebars and other tooling can display per-pane agent state.
Install
pi install /Users/nicknisi/Developer/pi-extensions/packages/statuslineWhat it adds
- Custom footer — installed via
ctx.ui.setFooter(...)onsession_start. Single line, segments separated by│:- Model icon + name (nerd font icon chosen by name: opus/sonnet/haiku get distinct glyphs, everything else a generic robot icon). Appends the thinking level (e.g.
high) whenctx.model.reasoningis true, read live frompi.getThinkingLevel(). - Session cost (
$X.XX), summed fromAssistantMessage.usage.cost.totalacross the current branch. Hidden until cost > $0.001. - Lines changed (
+N/-M), summed fromlinesAdded/linesRemovedin toolResultdetails(i.e. the edit tool). Hidden when zero. - Anthropic usage limits (Anthropic models only): remaining-% bars for the 5-hour and 7-day OAuth usage windows, with time-until-reset (
↻3h20m). - Remaining context: a bar that stretches to fill available terminal width (clamped 5–20 cols), plus
N% ctx (tokens used). With Checkpoint loaded, its thresholds control the bar color without changing the fill, width, or labels. - Git branch (right-aligned), with a hyperlinked
#PRnumber whenghfinds an open PR for the branch.
- Model icon + name (nerd font icon chosen by name: opus/sonnet/haiku get distinct glyphs, everything else a generic robot icon). Appends the thinking level (e.g.
- Tmux status files — JSON state files at
~/.cache/pi-status/<pane>.statusfor external consumers (tmux sidebars, fleet monitors).
There are no slash commands, tools, keybindings, widgets, or custom message/entry types.
Events hooked
| Event | Action |
| ----------------------- | --------------------------------------------------------------------------------------------- |
| session_start | Installs the footer; writes idle status |
| agent_start | Writes working status |
| tool_execution_start | Writes working status with toolName |
| turn_start | Re-writes working to keep the status-file timestamp fresh during long tool-less generations |
| agent_end | Writes done status; spawns claude-notify waiting <session> <pane> (detached) |
| session_shutdown | Removes the pane's status file |
| thinking_level_select | Triggers a footer re-render so the new level shows immediately |
Checkpoint integration
When @nicknisi/pi-checkpoint is loaded, this same context bar uses its policy colors:
- Green (
success): below the soft notice threshold. - Blue (
accent, theme-dependent): soft notice. - Amber (
warning): warning threshold. - Red (
error): hard cutoff, invalid settings, or a failed handoff. - Dim: context usage is temporarily unknown.
The bar still shows remaining model context, not a compaction budget. Model, cost, usage limits, branch, and all other footer segments are unchanged. Checkpoint hides its separate above-editor widget while this footer owns the meter. Either extension can still run alone; removing Checkpoint restores the original remaining-capacity colors.
The optional integration uses Pi's event bus, not a package dependency: self-compact:context-color publishes a theme color (or undefined on shutdown), statusline:context-bar announces whether the footer is active, and statusline:request-context-bar handles late discovery. Subscriptions are removed on shutdown and footer disposal releases the widget fallback.
Tmux status files
Path: ~/.cache/pi-status/<pane>.status where <pane> is TMUX_PANE with the leading % stripped.
{
"state": "working",
"pane": "%12",
"session": "main",
"tool": "edit",
"ts": 1717600000
}stateis one ofworking,done,idle(the function signature also acceptscompleted, unused in practice).toolis the tool name fromtool_execution_start, else"".tsis a unix timestamp; external consumers can treat a staleworkingstate as idle (the code comments mention a 180s decay in "fleet").- The tmux session name is resolved with
tmux display-message -p '#{session_name}', falling back to parsing the socket path in$TMUX. - No-op when
TMUX_PANEis unset. All write/remove failures are swallowed so they can't break the agent.
Usage-limit segment (Anthropic only)
Shows remaining capacity for the 5-hour and 7-day rate-limit windows when ctx.model.provider === "anthropic":
5h ━━━╌╌ 62% ↻2h10m ╱ 7d ━━╌╌╌ 41% ↻3d4hData comes from https://api.anthropic.com/api/oauth/usage (header anthropic-beta: oauth-2025-04-20). The render path never does network or keychain I/O:
- Fresh cache (
< 120s) in$TMPDIR/pi-statusline-cache/usage.jsonis rendered as-is. - Stale/missing cache renders stale data (or nothing) and kicks a fire-and-forget
curlrefresh that re-renders the footer on completion.
OAuth token resolution order:
~/.pi/agent/auth.json→anthropic.access- macOS Keychain:
security find-generic-password -s "Claude Code-credentials" -w→claudeAiOauth.accessToken(Claude Code credentials)
PR lookup
Branch → PR mapping via gh pr view --json number,url --jq '"\(.number)\t\(.url)"', spawned fire-and-forget from the render path. Cached per-branch for 60s, including misses (so non-PR branches don't refetch every render window). The PR number is rendered as an OSC 8 hyperlink (hyperlink() from @earendil-works/pi-tui).
Performance notes
The footer render path is kept synchronous:
- Cost/lines totals require an O(session) walk of
ctx.sessionManager.getBranch(); they're cached and recomputed only when the branch entry count changes or every 5s. - Usage limits and PR data are both cache-reads on render, with async background refresh.
- Re-renders are requested on branch change (
footerData.onBranchChange), PR refresh completion, usage refresh completion, andthinking_level_select.
Configuration
Optional global config: ~/.pi/agent/configs/statusline.json. Changes take effect on the next session or after /reload.
{
"hiddenStatuses": ["mcp"]
}hiddenStatuses contains stable extension status keys—the first argument passed to ctx.ui.setStatus(key, text). Only matching extension-provided segments are omitted; built-in model, cost, context, usage, and branch segments are unaffected. The default is [], which preserves all extension statuses. Missing or invalid config falls back to the default and invalid config emits a warning.
For example, pi-mcp-adapter publishes its server-count segment under the mcp key, while this repository's subagent fleet uses subagents.
Environment variables read:
| Variable | Use |
| ----------- | -------------------------------------------------------- |
| TMUX | Detect tmux; parsed as fallback for session name |
| TMUX_PANE | Pane id for status files and claude-notify |
| HOME | Locates ~/.cache/pi-status and ~/.pi/agent/auth.json |
| TMPDIR | Usage cache directory (default /tmp) |
Hardcoded constants you may want to tweak in index.ts:
| Constant | Default | Meaning |
| ----------------- | ------------ | -------------------------------------------------- |
| USAGE_CACHE_TTL | 120 (s) | How fresh the usage cache must be before a refetch |
| PR_CACHE_TTL | 60 (s) | Branch→PR cache lifetime |
| Totals recompute | 5000 (ms) | Max age of cached cost/lines totals |
| Context bar width | 5..20 cols | Clamp for the stretch-to-fill context bar |
Dependencies
@nicknisi/pi-shared(workspace) —columns()two-column layout helper (left-truncates to ~45% when the line overflows).@earendil-works/pi-coding-agent(peer) —ExtensionAPI,Theme.@earendil-works/pi-ai(peer) —AssistantMessagetype for usage/cost.@earendil-works/pi-tui(peer) —hyperlink(),visibleWidth()for ANSI-aware width math.- Runtime external commands:
tmux,gh,curl,security(macOS),claude-notify.
Caveats
- pi internals: relies on
ctx.ui.setFooter,footerData.getGitBranch()/onBranchChange(),ctx.sessionManager.getBranch(),ctx.getContextUsage(),pi.getThinkingLevel(), and toolResultdetails.linesAdded/linesRemoved(an edit-tool implementation detail). Any of these can change across pi versions. - Theme colors: assumes theme color names
accent,success,warning,error,dim. - Nerd fonts: icons are private-use-area nerd font codepoints; renders as boxes without a patched font.
- macOS-only paths: the Keychain fallback for OAuth tokens and the
securitybinary are macOS-specific.claude-notifyis a personal script expected onPATH. - Anthropic-only usage segment: the OAuth usage API is called with a bearer token from pi/Claude Code auth; non-Anthropic models skip the segment entirely.
requirein ESM:removeStatus()usesrequire("node:fs")inside a"type": "module"package — works under pi's loader but would fail under plain Node ESM.- Duplicate
session_starthandlers (one for the footer, two for tmux status) — harmless but worth knowing when editing.
