pi-signal-footer

A readable status footer for Pi Coding Agent: model, tokens, cache, cost, context bar, streaming rate, and MCP/LSP status.

Packages

Package details

extension

Install pi-signal-footer from npm and Pi will load the resources declared by the package manifest.

$ pi install npm:pi-signal-footer
Package
pi-signal-footer
Version
0.6.2
Published
Oct 16, 2026
Downloads
639/mo · 240/wk
Author
chengzhi-c
License
MIT
Types
extension
Size
115.3 KB
Dependencies
0 dependencies · 2 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

pi-signal-footer

English | 简体中文

A readable status footer for Pi Coding Agent: model, tokens, cache, cost, context bar, streaming rate, and MCP/LSP status.

Text examples below use a 160-column terminal. Colors follow the host theme, and narrower terminals reflow the layout. LSP appears on demand and is hidden without an active or failed status.

classic (default)

C:/Users/dev/agent-demo · fix-context-bar  │  opencode-go › ◎ deepseek-v4-flash-0731 │ ✦ max │ ⎇ main                      ⎔ 12% [━━──────────────────] 36k/300k
↓ 220 ↑ 5.4k │ ↻ 51k (96.92%) ✎ 1.4k │ $0.087 │ ◷ 2m · 1 turn · 45 tok/s                                                                           ⇄ MCP tools 2

vivid (/footer theme)

📁 C:/Users/dev/agent-demo · fix-context-bar  │  opencode-go › 🐳 deepseek-v4-flash-0731 │ 🧠 max │ 🔀 main               📊 12% [━━──────────────────] 36k/300k
📥 220 📤 5.4k │ 🔄 51k (96.92%) 📝 1.4k │ 🪙 0.087 │ ⏳ 2m · 💬 1 turn · 🚀 45 tok/s                                                             🔌 MCP tools 2

Requires Pi Coding Agent >=0.84.4. Older hosts keep the native footer.

Every line is truncated to the terminal width before it is returned. If a render throws, the footer falls back to the previous frame re-truncated to the current width, so a failure cannot stop the host's render loop. The legend keeps a line of headroom at 80 columns.

Readings

  • ≈ marks an approximation, not a lower bound: the live rate and the in-flight ≈+N suffix on ↑ include visible text, thinking and tool-call arguments. Fixed densities (prose/thinking ≈4 chars/token, tool-call JSON ≈2, CJK/Hangul ≈1) can over- or underestimate; hidden reasoning is not observable. Changed positive provider output calibrates the live count, then only new visible content is estimated. A valid positive output at end becomes +N until that same message lands — a message replaced by another extension or an entry array shortened by a fork ends the reading too. Missing, invalid or zero usage does not make a non-empty estimate exact. Persisted totals use recorded usage, not estimates.
  • The live rate uses token and time deltas over the same observed span; one sample has no rate, and empty updates do not start the first-output clock. It updates only on chunks, not renders. The continuous window excludes long pauses; a pause holds the last measurement, and the short resume period may reuse it. Sparse chunks without a mature window use a request-wide observed average. At end, output ÷ first-observed-output-to-end time is frozen (request start is the fallback if no output was observed). This is observed throughput, not a provider speed benchmark.
  • Token, cache, cost and turn totals cover all entries in the current session file. Same-file /tree navigation keeps spent tokens from abandoned paths. /fork creates a new file with the selected path: this is not a total across every fork. Turns = user messages, including steering.
  • ◷ is estimated agent work time. While enabled, agent_start and session_before_compact append a small work-start custom entry, outside LLM context, to exclude preceding idle time consistently live, on landing and after reload. Human gaps and gaps before model/thinking/name/label changes are excluded; each counted gap caps at 15 minutes. Old unmarked history still uses entry gaps and cannot reconstruct every idle boundary. An aborted response rewinds entry-time minus message-start-time, at most its counted gap; error does not. Failed/cancelled compaction without a result leaves no added completed work.
  • ↻ is accumulated cache-read tokens; the parentheses are the latest landed assistant request with usable input dimensions, not the current streaming request: cacheRead / (input + cacheRead + cacheWrite), to two decimals. All-zero or unreported input dimensions carry over the last known rate (stale, never a faked 0.00%). A request with usable input but no read reports 0.00%.
  • Context comes from the host’s current-branch getContextUsage(), which may combine reported usage and new-message estimates; unknown values stay unknown. A reported window of 0 or NaN falls back to the model's contextWindow; ? shows only when neither is available. Cost sums recorded usage.cost.total, not a verified invoice. Colors follow the displayed rounded context percentage: ≥50% warns, ≥75% errors.

Install

pi install npm:pi-signal-footer

Pin with pi install npm:pi-signal-footer@<version>, update with pi update --extensions, or install from a tag: pi install git:github.com/chengzhi-c/pi-signal-footer@v<version>.

Configure

Settings live in pi-signal-footer.json under Pi's agent directory (usually ~/.pi/agent/); a missing or invalid file falls back to these defaults. Commands below write this file.

{
  "enabled": true,
  "locale": "auto",
  "theme": "classic",
  "showProject": true,
  "showSessionName": true,
  "showDuration": true,
  "showTurns": true,
  "showSpeed": true,
  "showBranch": true,
  "showCacheRatio": true
}

locale: auto / zh / en. theme: classic (neutral gray with a few semantic colors) or vivid (colorful emoji, dim separators and tracks, second example above).

Commands

/signal-footer and the short alias /footer work identically:

/footer                     show the metric legend (same as /footer legend)
/footer hide                hide the legend (this session)
/footer help                show every command
/footer off                 restore the native footer
/footer on                  enable this footer
/footer path|session|time|turns|speed|branch|cache [on|off]
                            show/hide one item (omit on|off to toggle)
/footer status              show the current settings
/footer locale auto|zh|en   set the UI language
/footer theme [classic|vivid]
                            switch theme (omit to toggle)

off and on persist across sessions. Turning off clears transient stream/rate/compaction state, skips tracking and work markers, and hides the legend. Turning on does not reconstruct chunks missed while disabled; changing theme or display settings while enabled keeps the active request.

The legend is session UI, independent of enabled: an explicit /footer off or /footer hide and session end clear it, while a settings file that turns the footer off externally does not.

MCP / LSP status

This footer reads public extension statuses and MCP tool inventories, not private connection state. Known LSP failures come first, then explicitly failed or partially connected MCP servers, then normal/unknown statuses in stable key order. Unknown text keeps its original colors. Zero MCP connections can mean idle lazy connections, not failure.

Status examples: explicit MCP attention states color the ratio and ✗N red; LSP failures are also red.

State classic vivid
MCP needs attention ⇄ MCP 1/2 ✗1 · tools 2 🔌 MCP 1/2 ✗1 · tools 2
Tool inventory only ⇄ MCP tools 2 🔌 MCP tools 2
Active LSP ⇄ MCP tools 2 · LSP typescript 🔌 MCP tools 2 · 🛠️ LSP typescript
Failed LSP (shown first) LSP ✗ typescript · ⇄ MCP tools 2 🛠️ LSP ✗ typescript · 🔌 MCP tools 2
  • LSP: the footer displays published active or failed statuses in full (LSP Active: … / LSP Failed: …) or compact (LSP ✓ / LSP ✗) form. Failures take priority; LSP Inactive or an unpublished status is hidden. Absence alone does not establish server health. If a published active/failed status is missing, widen the terminal to rule out clipping.
  • MCP health: a published connected/enabled status takes priority. The native status contract MCP native C/E failed F renders as MCP C/E, with a red ✗F when servers explicitly need attention (failed, disconnected, or awaiting authentication). Full connections use classic text / vivid success color; partial connections warn; zero without an explicit failure stays muted. Native health can include · tools N for discovered mcp__ tools. Compact ratio statuses are also supported; recognized MCP 0/0 is hidden and suppresses inventory fallback. A bare N/M connected is read as a ratio only when its status key names MCP, so a connection pool under another key stays an ordinary status. Pi has never published a connected/enabled status, so built-in MCP shows only the tool count; the ratio appears when an extension publishes a supported status.
  • MCP inventory: without a recognized health status, MCP tools N shows public getAllTools() entries whose names start with mcp__. MCP tools join that list as their servers connect, so the count grows over the first seconds of a session. Labels stay muted; numbers use the theme's ordinary readout color. Inventories are not connection health counts. While this footer is active, public in-memory inventories are checked once per second; a tick that finds a different status than the last one any path read requests a redraw, and disposing the footer stops the check. No servers or network are polled; missing/unavailable APIs do not fabricate a zero.

Native health requires the host to publish a supported status; server configuration and tool discovery alone do not establish health. This package only reads status; it does not patch the host or create connections. Inventory can update without a health publisher when the extension is loaded and the host provides compatible public inventory APIs; this is not a guarantee for every future host version. A config-only server may have no indicator before discovery. Use the host’s /mcp command for server details and widen the terminal to rule out clipping.

Development checks

npm run check runs tests and strict type checking. npm run bench:footer includes the host’s shallow getEntries() copy; npm run bench:estimate measures controlled streams. These local microbenchmarks are not end-to-end Desktop performance measurements. npm run pack:check checks package contents.

npm run bench:reality -- <session-directory> performs read-only recorded-entry replay and formula/render consistency checks. Without a directory it scans nothing. Estimate/output statistics are descriptive, not independent tokenizer accuracy or ground truth.

License

MIT