@powerfool/pi-timing

Per-step, per-turn, and per-run timing extension for the pi coding agent

Packages

Package details

extension

Install @powerfool/pi-timing from npm and Pi will load the resources declared by the package manifest.

$ pi install npm:@powerfool/pi-timing
Package
@powerfool/pi-timing
Version
0.2.2
Published
Aug 31, 2026
Downloads
656/mo · 31/wk
Author
powerfool
License
MIT
Types
extension
Size
63.5 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-timing

Timing extension for the pi coding agent: per-tool-step, per-turn, and per-run timing rendered directly in the transcript, plus a live elapsed clock in the Working status line and a navigable runs list.

Features

  • Step timing in message bodies: built-in tool calls (read, edit, write, grep, find, ls) show ⏱ started MM-DD HH:MM:SS · Ns live while running and ⏱ started … · took Ns when done. Bash is not wrapped — pi's own bash timing line stays as the single source of truth.
  • Per-tool recording: every tool execution is persisted as a step-timing session entry, which powers the stats view. Wrapped built-in tools (their timing renders in-box) and ignored tools render these entries as hidden data-only rows; extension tools (subagent, MCP, etc.) show a visible timing line. Ignore only controls transcript noise — stats always aggregates everything recorded.
  • Turn timing: ⏱ Turn N · HH:MM:SS · took Ns entries after each turn.
  • Run timing: ⏱ Run · HH:MM:SS · took Ns (llm Ns · tools Ns) · N turns · N tok · $N on agent_settled (covers auto-retries and compaction). The llm/tools split is the union of all tool execution intervals within the run; tokens and cost come from pi's own per-message usage accounting.
  • Slow-run highlighting: run durations are colored by configurable thresholds — below fast dim, below slow normal, at/above slow as a warning (defaults 5s / 30s). Applies to the run summary entry and the runs list.
  • Working status line: Working... MM-DD HH:MM:SS · Ns ticks once per second. Start clocks always carry a date — MM-DD within the same year, YYYY-MM-DD across years.
  • /timing:runs: flat single-line list of the current branch's runs — start clock, colored total duration, turn count, tokens, cost, and prompt summary per row (run-level only; per-tool and per-turn rows are omitted). The start clock is MM-DD HH:MM:SS (YYYY-MM-DD HH:MM:SS across years), as are the run summary entry and the per-turn/per-tool timing lines in the transcript. Steering messages get their own rows, in-progress runs show running…. ↑↓ moves a cursor (full-width highlight). Ctrl+T cycles the detail level: off → detail → tools → off — detail adds the llm/tools time split, token usage detail (input / cache read / cache write / output), and the run's user/assistant conversation excerpt; tools interleaves tool calls (args, duration, error, result excerpt) into the timeline. Enter dives into the selected run: uncapped content with long messages wrapped to the terminal width — ↑↓ scrolls while reading, Enter/Esc collapses. ←/→ pages, g/G top/bottom, Esc to close. The detail level is remembered across opens and session switches for the lifetime of the process (not written to timing.json).
  • /timing:stats: aggregated view — run count, total/avg/max duration and llm/tools split for the current branch, token and cost totals, plus per-tool count/total/avg/max/errors. Everything is sourced from the branch's persisted timing entries, so the view survives /reload.

Everything is display-only: the LLM context is never modified.

Usage

/timing                      show current settings
/timing steps on|off         toggle in-box step timing (default on)
/timing turns on|off         toggle turn entries (default on)
/timing run on|off           toggle run summary entry (default on)
/timing status on|off        toggle the Working status clock (default on)
/timing ignore add <tool>    never show a timing entry for this tool
/timing ignore del <tool>    remove from ignore list
/timing thresholds 5 30      set fast/slow duration coloring thresholds (seconds)
/timing:runs                 open the timing runs list (aliases: /timing runs, /timing tree)
/timing:stats                open the aggregated stats view (alias: /timing stats)

Settings persist in ~/.pi/agent/timing.json. Default ignore list: start_bg_task, bg_task_status, stop_bg_task, bash (pi's bash renderer already shows its own timing).

Development

Requires Bun and a global pi install.

bun run link-deps    # symlink the global pi packages into node_modules (once)
bun test             # unit tests (bun:test)
bun run typecheck    # strict tsc

link-deps points node_modules/@earendil-works/* at the globally installed pi, so tests always run against the actual runtime version.

Install

pi install npm:@powerfool/pi-timing

Local development

bun run install-extension

This symlinks the repo as ~/.pi/agent/extensions/timing; pi discovers <dir>/index.ts automatically. Use /reload inside pi after changes.

Notes

  • Coexistence: if another extension has already replaced a built-in tool (e.g. a bg-tasks extension overriding bash), pi-timing yields instead of failing to load — pi rejects duplicate extension tool names. The yielded tool falls back to persistent timing entries (final duration only, no live ticking) and a notice is shown once per session.
  • Session entry custom types (step-timing, step-timing-turn, step-timing-run) are persisted in .jsonl session files — do not rename.
  • Bash is deliberately not wrapped: pi's bash renderer draws its own Elapsed/Took line, and re-registering bash would collide with extensions that replace it (e.g. bg-tasks). Bash is in the default ignore list, so no duplicate fallback timing entries appear. No pi internals are touched.