pi-asterisk-tui

Polished Pi coding agent TUI: transcript collapse modes (native / per-tool / grouped / run lines), claude-hud style dashboard, telemetry, and bilingual settings.

Packages

Package details

extension

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

$ pi install npm:pi-asterisk-tui
Package
pi-asterisk-tui
Version
0.9.0
Published
Sep 30, 2026
Downloads
1,157/mo · 741/wk
Author
no_world
License
MIT
Types
extension
Size
571.1 KB
Dependencies
0 dependencies · 3 peers
Pi manifest JSON
{
  "image": "https://raw.githubusercontent.com/No-World/pi-asterisk-tui/main/assets/preview_dashboard_1.png",
  "extensions": [
    "./extensions/asterisk-tui/index.ts"
  ]
}

Security note

Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.

README

pi-asterisk-tui

English | 简体中文

A Pi terminal experience where everything the model does folds into tidy ✻ lines — Claude Code style — plus a claude-hud style status dashboard.

preview

pi install npm:pi-asterisk-tui

Or from git (tracks main):

pi install git:github.com/No-World/pi-asterisk-tui

✻ Transcript

The transcript renders as answer text plus compressed activity lines. How much compresses is the compression mode (/*tui → Collapse):

Mode Rendering
native untouched pi rendering — no compression
single one line per tool (▸ bash · $ npm test), nothing merged
group-same consecutive same-type tools merge (✻ read 3 files); thinking merges its own ✻ Thought for 11s line — kinds never mix
group-all Claude-Code style: consecutive thinking + tools merge into one line (default)
✻ Thought for 19s, searched for 9 patterns, listed 1 directory, ran 1 shell command
  • Run lines (group-all): verbs read like a sentence — ran 3 shell commands, edited 2 files, read 5 files, listed 2 directories, searched for 9 patterns, called playwright ×2 (leading verb capitalized when no thinking precedes). Thinking durations come from live telemetry; history turns read ✻ Thought, ran 1 shell command.
  • Per-tool overrides: every tool can be set to default (follow the mode), single (one line), group-same (same-type group line, never absorbed into run lines) or expand (native box); * matches unnamed tools. Per-item states are absolute — group-same groups even in native mode.
  • Thinking blocks share the same lattice (turnCollapse.thought): default (follow the mode) / single (one ✻ label per message) / group-same (grouped Thought line, never merged with tools) / expand (inline, pi native). Whole-run absorption exists only via group-all mode + default. pi's native hideThinkingBlock is kept as a mirror (ctrl+t flips are adopted as explicit states).
  • Line spacing: compact (flush) or classic (blank line around compressed lines, adjacent compressed lines separated by a single blank; ✻ label lines count as compressed lines, keeping them apart from their text tails).
  • One-click expand/collapse: click a compressed line to open the full reasoning and every tool's bordered output at once — including the thinking of text-bearing messages, no second tap on labels. Click any member line to fold it all back. A click is an unmodified press that releases on the same cell — drag-selecting text never toggles a run (pi's fullscreen selection keeps working untouched).
  • Per-message thinking labels: ✻ Thought… (history) / ✻ Thinking… (streaming), individually clickable to expand just that message's reasoning, styled identically to run lines (same accent ✻, same muted upright text).
  • Running tools render as an animated one-liner (⠋ bash · $ npm test) with live output streaming beneath — and never drag completed neighbors out of their folded lines (turnCollapse.liveTools: false drops the streaming box, spinner line only; native- override tools always keep their box)
  • Live thinking (turnCollapse.liveThinking, default on): while a message streams thinking-only content it renders inline in real time; once text starts (or the message stops streaming) it folds back to the ✻ label / run line immediately — no need to wait for the run to settle (native mode keeps pure pi boxes).
  • Retry UX: the countdown carries the failure reason (Retrying (2/10) in 5s… · 429 rate_limit_error); intermediate errors are held back, a successful retry prints nothing, and only the last error shows if the run fails (togglable independently of the mode).
  • Regular-mode support: compressed lines work in the regular (non-fullscreen) TUI too — every line ends with the effective expand-all shortcut hint (e.g. (ctrl+\ to expand)); pressing it expands every run (reasoning + all tool output), pressing again collapses all. Fullscreen keeps per-run click-to-expand.
  • Compact spacing: pi's internal spacer padding and OSC shell-integration markers around ✻ lines are folded away.

Telemetry

  • Working indicator: every segment carries its own glyph — elapsed (clock), speed, input, output, cache hit, tool count (wrench): Working… ( 34s · 󰓅 61.8 tok/s · ↑ 1.2k · ↓ 3.4k · 96.0% · 3) — elapsed always leads; every other segment is a per-surface toggle on the Working tab (workingLine.*). Output tokens are run-cumulative (stream-estimated while streaming, exact on message completion, kept across tool calls); the speed shown here is the run average from submission to now — the HUD footer's speed segment is the session average.
  • Turn telemetry after each run: TPS, TTFT, duration, tool-call count, input/output token breakdown with cache-read and cache-write, cache hit rate, stall count/time, actual run cost and the blended $/M rate (cache-read dominated).
  • Persistence: each run's telemetry is stored as a session custom entry (extension-owned, never sent to the model) and rendered as a transcript line in place — the same line at the same position whether the run just finished or the session was resumed, re-formatted with the current icon/language settings; rewinding past a run prunes its entry. telemetry.persist toggles it (on by default); when off, the old transient one-shot status line is shown for the live session only.
  • Tools/summaries side spend: token usage attached to tool results (the tool's own LLM calls, e.g. subagents) and to compaction/branch summaries is real session cost, but not main-context accounting — mirroring pi's own Tools/summaries bucket it is tracked separately and shown as a dimmed suffix on the cost segment ($0.012+$9.500 tools / 费用 $0.01+$9.50 工具) in both footers; today's cost sums the same scope. Zero side spend renders exactly as before.
  • Classic footer summary: ✓ done 12s · ✻ 8s · 2 shell commands after each run.

HUD footer

A claude-hud style four-line dashboard (a starship-style classic preset is also built in):

  1. Status line — model with context window, thinking level (moon-phase icons; the model name, icon, and level text share the thinking-level color, matching the editor border — all levels including max), git branch with dirty marker, ahead/behind, per-file diff totals [+71 -5], session name, cumulative working time, cost, today's cost, live output speed (tok/s; only updated when the message streamed for at least 1s — burst-flushed responses are not measurable and keep the last credible speed).
  2. Context line — usage bar with percent and token counts, cache hit rate, and a compaction-count suffix (hud.compactions, on by default, hidden until the session has compacted): · compact 2. Token stats have three presentations (hud.tokens): localized verbose labels, language-independent compact shorthand (icon + value, e.g. 77M (U 855k + R 77M) │ 266k │ 98.9%), or off. The stat segments (time, cost, today's cost, output speed, tokens, cache hit) render per hud.statStyle: icon (glyphs only), icon+text (default), or text; glyphs come from the same set as the telemetry line and classic footer, with ASCII fallback under icons.mode: "ascii".
  3. Tools line — per-tool usage counts with ✓, running tool labels.
  4. Environment line — MCP server count (pi ≥0.99 counts native mcp.json servers — global, trusted project, and extension-registered — while pi-mcp-adapter's config chain keeps counting during transitions; same-name native entries win), memory usage, compaction count, pi version.

Plus: OSC 8 hyperlinks on the working directory and changed files (click to open), powerline-styled git segment, ahead/behind indicators, and full subdirectory git detection (pi normally fails to show branch state when started inside a repository subdirectory).

Editor & settings

  • Framed editor with block / bar / underline cursor styles.
  • Working status (/*tui → Working tab, workingStatus): where the live run status renders — line (pi's working line only), border (editor top border only), or both (default). The two surfaces are information peers, each with its own content options in the same order (elapsed, speed, input, output, cache hit, run cost, tools), shown only while the mode includes that surface. The cost segment is run-scoped — the spend of the current submission so far, not the session total (the footer keeps that) — and is a tri-state like the footers (off / cost / cost+rate, with the blended $/M rate appended). Input tokens are a tri-state — off, run total, or total with its cache-read part (↑ 348k (R 298k), updated at message boundaries). Either surface falls back to the elapsed time when everything else is off; the border degrades by width (segments → elapsed → glyph). Speed semantics: the working surfaces show the run average over response time — run tokens over wall-clock elapsed since submission minus tool-execution time (TTFT counts as response time, tool waits do not), reconciling by eye with the elapsed and token segments beside it; both surfaces draw from one 500ms snapshot (the border renders on every streaming repaint but only shows the cached text, so it can never run a beat ahead of the line). The HUD footer's speed segment is the session average — every message this session over its summed streaming windows, never reset across runs, and re-seeded on resume from the persisted run telemetry so it survives restarts. The border status is painted with the frame color, recoloring with thinking-level and bash-mode borders; narrow frames degrade to a glyph-only rung and the scroll hint (↑ 3 more) keeps its centered slot. Legacy borderWorkingStatus configs migrate (true→both, false→line). In border mode pi's own retry countdown and compaction statuses ride the border too (native embed routing); line/both keep them as standalone lines.
  • Inline footer (inlineFooter, default off, classic style only): moves the classic footer's two main rows into the editor frame borders — top carries the location segments (cwd · host · session · git) left and the model block right; bottom carries the done summary left and the stats row (compact context · tokens · cost) right. Extension status rows stay below the editor. The right block survives shrinking widths first, mirroring the plain footer's alignRight priorities. While working, the bottom-left stays empty only when the workingStatus mode includes the border — in line mode the working timer falls back to the bottom cell, so the state is never invisible. Saves two rows of vertical space on small terminals. Inert under footerStyle: "hud".
  • Selection copy (fullscreen) in three modes (/*tui panel or selection.copy, default unwrapped): plain copies visual content (pi stock, row by row); unwrapped copies logical content — soft-wrapped rows join back into their logical line (paragraphs, list items, quotes, table rows with wrapped cells); raw copies source content — markdown-covered runs yield the pre-render source (**bold**, $x^2$, table pipes; selecting a whole message yields its original text; non-markdown rows degrade to logical content for those rows only). Note: this depends on pi-tui rendering internals and may silently stop working after a pi upgrade (it falls back to stock copy without errors); updating this extension restores it. A separate switch, selection.trimPadding (default on), keeps the selection highlight off padded margins and drops the leading/trailing margin spaces from visual-content copies.
  • Bilingual /*tui settings panel (English / 简体中文) — the language choice also localizes HUD labels — covering footer segments, HUD toggles, telemetry fields, icon mode (nerd / ascii / auto), cursor style, fullscreen wheel-scroll speed, and a Collapse tab (compression mode, line spacing, retry-error folding, thinking visibility, per-tool overrides — extension/MCP tools appear there once seen) — with named style presets (hud / classic / custom).
  • Version-guarded compatibility shims: fullscreen wheel speed falls back to pi defaults if the runtime shape changes. On pi ≥0.99 the wheel speed setting stands down entirely — pi ships it natively (with an accelerating "auto" mode) and re-applies its own value, so the settings entry points at /settings → fullscreenWheelScrollLines instead (ADR-0009).

Fresh installs default pi's hideThinkingBlock to true (existing choices are never overridden) so the ✻ experience works out of the box.

Requirements

  • Pi 0.85+ (framed-editor mouse click alignment needs pi-tui's component mouse events)
  • UTF-8 terminal; a Nerd Font for the full icon set (ASCII icons are built in)
  • Both TUI modes work: in regular mode compressed lines expand/collapse via the expand-all shortcut (default ctrl+\, annotated at the end of each compressed line); **per-run click-to-expand** needs pi's fullscreen mouse capture (/settings → TUI mode, or "tuiMode": "fullscreen" in ~/.pi/agent/settings.json).

Font and icons

The default auto mode checks the terminal environment, not the installed font file — the emulator owns font selection and no environment variable can prove a Nerd Font is active (ADR-0006). Auto uses Nerd Font icons in interactive UTF-8 TTYs (including over SSH, where terminal-name sniffing never propagates), and falls back to ASCII for non-interactive output, TERM=dumb, or an explicitly non-UTF-8 locale. The first time auto resolves to nerd, a one-time hint notes the remediation path in case icons render as boxes. Modes under /*tui → Appearance:

  • nerd: force Nerd Font icons after configuring a Nerd Font in the terminal profile
  • ascii: plain-text icons, no patched font required
  • auto: nerd in interactive UTF-8 TTYs; ASCII for dumb/non-TTY/non-UTF-8

If icons appear as boxes, either set ascii or install a Nerd Font and select it in the terminal profile — in VS Code, Windows Terminal, and similar apps the font must be set in the terminal profile, not only installed on the OS.

Configuration

Run /*tui, or edit ~/.pi/agent/asterisk-tui.json. Settings from a legacy open-tui.json are adopted automatically on first run; the old file is kept. Notable keys:

Key Default Effect
footerStyle "hud" hud / classic footer presets
turnCollapse.mode "group-all" native / single / group-same / group-all compression
turnCollapse.style "compact" compact / classic spacing around compressed lines
turnCollapse.retryErrors true hold retry errors during a run; collapse repeated request errors into one ⚠ … ×N line (click to expand)
turnCollapse.thought "default" thinking: default / single / group-same / expand
turnCollapse.liveThinking true stream thinking inline while it arrives; fold back after
turnCollapse.liveTools true render running tool output boxes below the spinner line
turnCollapse.expandAllKey "ctrl+\\" expand-all shortcut in regular mode (pi KeyId; empty disables; applies after restart/reload)
turnCollapse.tools {} per-tool default / single / group-same / expand; * wildcard
icons.mode "auto" nerd / ascii / auto icon set; auto = nerd in interactive UTF-8 TTYs (ADR-0006), one-time hint on first nerd resolution
cursorStyle "block" editor cursor style
telemetry.* on working-indicator and post-turn telemetry fields; telemetry.cost is a tri-state (off / cost / cost+rate); telemetry.persist (on) stores each run as a session entry rendered as a transcript line (survives resume)
footerSegments.* mixed classic footer segment toggles
footerSegments.hostname false opt-in short host name segment (first label of the machine's host name) — for telling SSH targets apart at a glance; same toggle exists as hud.hostname
footerSegments.capitalizeProviderName true uppercase the provider name's first letter; false keeps the raw provider id casing (proxy-style ids like cc-switch-zhipu-glm)
footerSegments.capitalizeProviderName true uppercase the provider name's first letter; false keeps the raw provider id casing (proxy-style ids like cc-switch-zhipu-glm)
workingStatus "both" live run status on pi's working line, the editor top border, or both; legacy borderWorkingStatus migrates (true→both, false→line)
workingLine.* / workingBorder.* mixed per-surface content options (elapsed, speed, input/output tokens, cache hit, tools); *.input is a tri-state (off / total / total+cache) and *.cost a tri-state (off / cost / cost+rate); shown per the active mode
workingBorder.elapsed true border always degrades to elapsed → glyph when narrow
inlineFooter false classic footer rows render inside the editor frame borders instead of dedicated rows; inert under footerStyle: "hud"
hud.* on every HUD segment individually toggleable (hud.tokens: verbose / compact / off; hud.statStyle: icon / icon+text / text; hud.cost: off / cost / cost+rate)
fullscreen.wheelScrollLines 4 mouse wheel lines per tick; inert on pi ≥0.99 — use pi's native /settings wheel entry instead
selection.copy "unwrapped" selection copy: plain (visual content) / unwrapped (logical content, default) / raw (source content); depends on pi-tui internals, may silently fall back to stock after a pi upgrade
selection.trimPadding true trim selection margins: highlight skips padded blanks; visual-content copies carry no margin spaces
selection.tabWidth 3 tab width for source-content copies: any integer 2–8 (3 as rendered) or tab for literal tabs (recovered deterministically from the original text for per-block copies)

How it works

Everything is a runtime patch over pi's extension surface, version-guarded and inert on mismatch: the chat container's render is wrapped to re-chunk the transcript (containers, messages, and tools are classified by content, not appearance), the fullscreen viewport's mouse input is observed (never consumed) to route same-cell press→release clicks through per-render line segments, and pi-tui's loader messages carry retry reasons. No pi files are modified on disk.

Local development

npm install
npm test && npm run typecheck
pi -e .

Acknowledgements

  • OldSuns/pi-open-tui — this project began as a fork of it; the original integration work and its credits carry over.
  • claude-hud — the HUD footer layout.
  • pi-haiku — footer structure and working timer.

License

MIT