Live CLI and TUI dashboard for Pi coding-agent activity
  • HTML 49.7%
  • TypeScript 39.1%
  • Rust 10.4%
  • JavaScript 0.8%
Find a file
2026-10-09 14:55:12 -05:00
crates/monitor-core feat(desktop): add tauri gui with native rust collector 2026-10-09 04:45:50 -05:00
docs/research feat: add session attention and reconnect snapshot recovery 2026-10-09 02:41:46 -05:00
graphify-out feat(desktop): add tauri gui with native rust collector 2026-10-09 04:45:50 -05:00
gui fix(ui): rename finished filter label to done 2026-10-09 14:55:12 -05:00
reporter feat: add session attention and reconnect snapshot recovery 2026-10-09 02:41:46 -05:00
src fix(ui): rename finished filter label to done 2026-10-09 14:55:12 -05:00
src-tauri feat(desktop): add tauri gui with native rust collector 2026-10-09 04:45:50 -05:00
test fix(ui): rename finished filter label to done 2026-10-09 14:55:12 -05:00
.gitignore feat(desktop): add tauri gui with native rust collector 2026-10-09 04:45:50 -05:00
AGENTS.md feat(desktop): add tauri gui with native rust collector 2026-10-09 04:45:50 -05:00
Cargo.lock feat(desktop): add tauri gui with native rust collector 2026-10-09 04:45:50 -05:00
Cargo.toml feat(desktop): add tauri gui with native rust collector 2026-10-09 04:45:50 -05:00
LICENSE feat: add live Pi activity monitor 2026-08-01 03:52:12 -05:00
package-lock.json feat(desktop): add tauri gui with native rust collector 2026-10-09 04:45:50 -05:00
package.json feat(desktop): add tauri gui with native rust collector 2026-10-09 04:45:50 -05:00
README.md fix(ui): rename finished filter label to done 2026-10-09 14:55:12 -05:00
tsconfig.json feat: add live Pi activity monitor 2026-08-01 03:52:12 -05:00

pi-monitor

A local, live desktop GUI, CLI and TUI monitor for the Pi coding agent. It listens to the global Pi activity-reporter extension and renders what Pi is doing without replacing Pi's normal interactive interface.

The interactive dashboard monitors multiple Pi sessions, shows running tools, keeps a per-session timeline, tracks cumulative input/output tokens and the active model's context-window size, and aggregates tool activity:

 pi-monitor  ● 2 connected  3 sessions  2 working
──────────────────────────────────────────────────────────────────────────────
 SESSIONS                    RECENT EVENTS · FOLLOW        CURRENT ACTIVITY
 > ● pi-monitor  12.4s       03:50:09 ↳ EDIT  Editing…     Tokens: ↑123.5k ↓8.2k · ctx 200k
   ○ Bagsy  IDLE             03:50:08 ✓ READ  Completed…   WORKING · 12.4s
   ● website  4.1s           03:50:04 ↳ READ  Reading…     Tools: 8/9
──────────────────────────────────────────────────────────────────────────────
 ↑↓/jk move  tab panel  space pause  f follow  ? help  q quit

The line-oriented watcher remains available for scripts and log-oriented use:

03:49:48  ○ SESSION  [my-project] Pi session started
03:50:02  ? PROMPT   [my-project] Asked Pi: Refactor the authentication service
03:50:02  ● WORK     [my-project] Pi started working
03:50:04  ↳ READ     [my-project] Reading src/AuthService.ts
03:50:05  ✓ READ     [my-project] Completed read · 84ms
03:50:08  ↳ EDIT     [my-project] Editing src/AuthService.ts
03:50:09  ✓ EDIT     [my-project] Completed edit · 112ms
03:50:12  ✓ DONE     [my-project] Pi finished working · 10.2s

Desktop GUI (Tauri + Rust)

The desktop app uses a native Rust collector, not a Node sidecar. The existing TypeScript reporter and CLI/TUI remain compatible during migration.

npm install
npm run desktop:dev       # native window with Vite development frontend
npm run desktop:build     # production application bundle

Desktop development requires Rust/Cargo and the Tauri 2 platform prerequisites (Xcode Command Line Tools on macOS; WebKitGTK/build dependencies on Linux). This version supports macOS and Linux, using Unix sockets. Windows named pipes are not implemented. Close any CLI/TUI collector using the same socket before opening the desktop app; only one collector may own a socket. PI_ACTIVITY_SOCKET overrides the socket path; PI_MONITOR_PROJECT restricts collection to a project directory and its descendants.

Desktop boundaries

  • crates/monitor-core: UI-independent protocol validation, reducer, Unix-socket actor, disconnect-grace maintenance and shutdown. Bounded JSONL records, owner-scoped tool IDs and reporter reconnect snapshots retain existing semantics.
  • src-tauri: thin lifecycle/IPC adapter. monitor_snapshot returns a detached JSON snapshot; monitor-update publishes complete replacement snapshots; dismiss_session is the only shared-state mutation. Monotonic revision prevents delayed IPC responses from overwriting newer frontend state.
  • gui: React dashboard using framework-neutral src/dashboard-policy.ts. Filters, selection, sorting, timeline/follow and completion acknowledgement are local to each view. Acknowledge carries the observed completion ID and does not mutate collector state. Pausing freezes the display, not ingestion.

Snapshots omit arbitrary event data (including exact tool I/O), but retain summaries and prompt previews. Socket permissions are owner-only. The Rust collector uses a persistent advisory lock file alongside its socket and only removes the socket inode it owns on shutdown.

Validation

npm run check
npm test
npm run gui:build
cargo test -p monitor-core
cargo check -p pi-monitor-desktop

Rust reducer parity fixtures compare four event traces step-by-step against the TypeScript reducer. Regenerate them with npx tsx test/rust-parity.ts when intentionally changing reducer semantics. Real socket tests cover framing, UTF-8 fragmentation, project filtering, malformed/oversized records, stale socket recovery, exclusive ownership and shutdown cleanup.

Requirements

  • Node.js 22.19 or newer
  • The global Pi activity-reporter extension at ~/.pi/agent/extensions/activity-reporter/

The extension sends versioned JSONL activity over a user-specific local socket. It reports cumulative input, output, cache-read, and cache-write token counts along with the active model's configured context-window size, without sending message contents. Exact tool inputs and results are hidden by default; set PI_ACTIVITY_INCLUDE_TOOL_IO=1 in Pi's environment to publish them. This can expose file contents, commands, credentials, and other sensitive data. Shell-command previews are hidden unless explicitly enabled in the extension environment.

Install from this checkout

npm install
npm run build
npm link

This installs the pi-monitor command using a symlink to the checkout.

Usage

Open the interactive dashboard before or after Pi:

pi-monitor tui

pi-monitor dashboard is an alias. The extension reconnects automatically, so already-running Pi sessions will begin reporting once the monitor is available. The dashboard uses these keys:

↑/↓ or j/k   Select a session or scroll its timeline
Tab          Switch between sessions and timeline
d            Dismiss the selected stopped or unavailable session
s            Toggle attention-first / status sorting
v            Cycle all / working / errors / done / quiet filters
a            Acknowledge the selected session's completion
Space        Pause/resume visual updates (events continue buffering)
f            Follow the newest event
?            Show help
q            Quit

Sessions use two lines: name and attention badges, followed by running-tool count, longest-running tool duration, and time since the last event (or lifecycle status when no tool is running). Attention sorting is the default: recent tool errors first, then unacknowledged completions, then quiet working sessions; remaining ties retain status/recency order. Selection stays on the same session when ordering changes, unless it no longer matches the active filter.

ERROR means a failed tool event in the last five minutes, not necessarily an unresolved failure. DONE means an observed agent.settled event that has not been explicitly acknowledged with a; simply selecting a session does not clear it. Starting another run clears the old completion. quiet means a working session has emitted no event for at least two minutes, not that it is stuck or disconnected. Ages use reporter timestamps. Badges and acknowledgement are in-memory and do not survive monitor restart. Filtered-out sessions continue collecting events, and the header counts all tracked sessions.

Reporter health is separate from its last reported lifecycle state. Socket closure immediately shows reconnecting. After a five-second grace period, without an event on a replacement connection, the reporter becomes unavailable. Unavailable reporters no longer contribute running tools or live-working counts and are excluded from working/quiet filters. Their last reported lifecycle state and history remain visible; disconnect never creates a completion or DONE. Health maintenance continues while visual updates are paused and uses local monotonic time, independent of reporter timestamps.

Ownership is scoped to reporter instance ID and PID within each logical session; tool calls and lifecycle state are isolated per reporter. A newer connection for the same reporter supersedes the old connection. An accepted event, including a duplicate replay, restores ownership without replaying metrics. The reporter sends an authoritative snapshot after queued replay on every connection, including silent reconnects. Snapshots repair lifecycle state and running tools without inventing completion notifications or tool metrics. Monotonic per-instance sequence numbers prevent old replay from reversing the snapshot. Snapshots contain model/token totals and running-tool metadata, never exact inputs or outputs, and do not reset activity age or enter event history. Queue overflow triggers a fresh snapshot after surviving replay drains. An open but silent socket is not treated as unavailable. This is connection health, not a process-liveness or heartbeat check.

d dismisses a stopped or unavailable session from the current dashboard; it does not delete Pi's session file. The header still includes retained history in its total session count. The reporter source is maintained in reporter/. Install both files and reload or restart Pi to activate reconnect snapshots:

mkdir -p ~/.pi/agent/extensions/activity-reporter
cp reporter/index.ts reporter/transport.ts ~/.pi/agent/extensions/activity-reporter/

Legacy reporters still work, but cannot provide snapshot recovery. Reporter shutdown drains an established connection for up to 250 ms; offline shutdown still relies on disconnect health rather than guaranteed delivery.

For the existing line-oriented stream, run:

pi-monitor
# or: pi-monitor watch

Filter to one project

The selected directory and nested working directories are included:

pi-monitor --project .

Machine-readable JSONL

pi-monitor --json

JSON mode validates each event against activity protocol version 1 before writing it to stdout. Monitor diagnostics remain on stderr. When exact tool I/O is enabled in the reporter, tool.started.data.input contains the call arguments and tool.completed.data contains both input and output. JSON mode preserves them exactly. The human-readable watch view shows compact representations fitted to the terminal width; the TUI shows only the tool event summary and never renders exact call arguments or results.

Options

Commands:
  watch             Stream line-oriented activity (default)
  tui               Open the interactive activity dashboard

Options:
  --socket <path>   Override the activity socket
  --project <path>  Show only activity under this project directory
  --json            Emit validated JSONL (watch only)
  --no-color        Disable ANSI colors
  -h, --help        Show help
  -v, --version     Show version

The default socket is /tmp/pi-activity-<uid>.sock. Both the monitor and Pi extension honor PI_ACTIVITY_SOCKET.

To monitor exact tool calls and their output, start Pi with:

PI_ACTIVITY_INCLUDE_TOOL_IO=1 pi

Use pi-monitor --json when the complete, untruncated values are required.

UI-independent architecture

src/monitor-service.ts owns the local activity socket, project filtering, activity store, connection-health maintenance, and asynchronous start/stop. It has no terminal or GUI dependency. The TUI uses this service; the raw watch stream remains unchanged.

  • snapshot() returns detached, JSON-native monitor state (schema version 1). Tool entries preserve their owner-scoped keys; metric categories are arrays. Event history excludes arbitrary data, including opt-in exact tool I/O. Prompt and tool summaries remain visible, so snapshots are not fully redacted.
  • subscribe(listener) delivers complete snapshots on activity, health, client count, warning, and dismissal changes. It returns an unsubscribe function; obtain initial state explicitly with snapshot(). Subscriber exceptions are isolated and reentrant updates are queued. Elapsed-time displays still need their own UI clock: silence does not publish periodic snapshots.
  • dispatch({ type: "dismiss", key }) dismisses only stopped/unavailable sessions.
  • start() and stop() serialize lifecycle transitions and are idempotent. Stopping disconnects retained owners; restarting reuses retained history.

src/dashboard-policy.ts provides a framework-neutral DashboardController with typed filter, sort, select, move, scroll, follow, acknowledge, and dismiss commands. Acknowledgement includes the observed completion ID, so stale clients cannot acknowledge a newer completion. Filters, selection, and acknowledgements are per-view; dismissal changes shared collector state. A GUI can adapt snapshot sessions to its DashboardSource interface without importing terminal code. The controller owns status/recency ordering (including attention tie-breaking), so adapters need not pre-sort sessions. Synchronous adapters supply removeStoppedSession; remote adapters call prepareDismissal(key) before mutation and invoke its returned completion only after success. The controller owns acknowledgement cleanup and successor selection, waits for removal to reach the displayed snapshot, and does not overwrite navigation made during the request.

src/dashboard.ts retains keyboard decoding, ANSI styling, responsive terminal layout, help, and visual pause. Its interaction policies delegate to the shared controller. src/tui-app.ts only connects the service to terminal lifecycle. The service's store-shaped accessors are an in-process compatibility adapter for the TUI; external consumers should use snapshots and commands.

These boundaries do not add network access or change the reporter protocol. Only one collector can own a given socket; multiple future views must share it.

Safety and lifecycle

  • The socket is created with mode 0600.
  • A second monitor refuses to replace an active monitor's socket.
  • A stale socket is detected and removed.
  • A non-socket filesystem path is never removed.
  • SIGINT and SIGTERM close clients and remove the owned socket.
  • Malformed, oversized, and unsupported-version events are ignored with a warning.

Development

npm run check
npm test
npm run build

The dashboard uses @earendil-works/pi-tui for differential terminal rendering. Tests use Node's test runner via tsx.