@nicknisi/pi-statusline
Custom footer: model, cost, context usage, git branch, lines changed, tmux status files
Package details
Install @nicknisi/pi-statusline from npm and Pi will load the resources declared by the package manifest.
$ pi install npm:@nicknisi/pi-statusline- Package
@nicknisi/pi-statusline- Version
0.2.1- Published
- Aug 15, 2026
- Downloads
- 326/mo · 14/wk
- Author
- nicknisi
- License
- MIT
- Types
- extension
- Size
- 61.5 KB
- Dependencies
- 1 dependency · 3 peers
Pi manifest JSON
{
"extensions": [
"./index.ts"
]
}Security note
Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.
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/statusline
What 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). - Context window usage: a bar that stretches to fill remaining terminal width (clamped 5–40 cols), plus
N% ctx (tokens). - 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 |
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% ↻3d4h
Data 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..40 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.