Skip to content

Repository files navigation

pi-tracker

Model usage analytics for pi

Track token & cost usage by model, project, and session — daily / weekly / monthly / 6-monthly / yearly — in an interactive TUI dashboard. Optional budgets fire a warning when a threshold is crossed.

CI License: MIT


⚠️ This project is no longer maintained. Issues, pull requests, discussions, and support requests are not actively monitored.

What it does

pi-tracker is a pi extension that turns every assistant turn into a record and gives you a live, drill-down analytics view:

  • Capture — every finalized assistant message (message_end) is logged with its token breakdown and cost.
  • Pivot — group by model, project, or session, across 6 time windows.
  • Drill down — pick a model to see which projects use it, then which sessions, then back up. A breadcrumb keeps your bearings.
  • Budgets — set token/cost limits per scope & period; get warned at a threshold and again when exceeded.
  • Back-fill — import your full pi session history on day one so you start with real data, not an empty log.

The dashboard

pi-tracker — Model usage
window: All Time  •  37 model(s)  •  filter: —

  Model                       usage                 tokens         cost       turns
  ──────────────────────────────────────────────────────────────────────────────────
▶ anthropic/claude-opus-4-8   ████████████████████  1,844,364,365  $2388.42   6155
  openai-codex/gpt-5.5        ██░░░░░░░░░░░░░░░░░░  1,212,486,248  $2096.32   10475
  zai-anthropic/glm-5.2       ████████████████████  14,798,725,266 n/a        4621
  ──────────────────────────────────────────────────────────────────────────────────
  Total                                              17,855,575,879 $4484.74   21251

[1-6] window  [d] dimension  [enter] drill in  [u] back  [↑↓] move  [q] close

n/a = that provider didn't return pricing (e.g. a model without published costs), not $0.

Install

pi-tracker is a pi package. Install it with pi directly:

pi install npm:pi-tracker

Or, from source (for development):

git clone git@github.com:alpertarhan/pi-tracker.git
cd pi-tracker && npm install
ln -s "$(pwd)" ~/.pi/agent/extensions/pi-tracker

Restart pi (or /reload) and run /analytics. Then back-fill your history:

/analytics import

That scans ~/.pi/agent/sessions/ and recovers all past usage (idempotent — safe to re-run).

Commands

/analytics

In the TUI, running /analytics with no arguments opens an interactive main menu — pick from:

  • Dashboard — the analytics view
  • Footer preset — pick what shows in the footer
  • Budgets — view & add budgets
  • Import history — back-fill from pi sessions
  • Help — full command reference

Arguments still work directly for power users (see below).

/analytics [today|week|month|6months|year|all]

Opens the interactive dashboard. Keys:

Key Action
1–6 switch window (today / week / month / 6mo / year / all)
d cycle grouping dimension (model / session / project)
↑ ↓ move cursor (auto-scrolls)
enter drill into the cursor row — pivot to the next dimension
u / backspace pop the last filter (go back up a level)
q / esc close

/analytics import

Back-fill analytics from pi session history. Idempotent.

/analytics footer [preset]

Show live analytics in pi's footer status line. Without an arg, opens a SelectList picker; with an arg, sets it directly. Choice persists across sessions (footer.json). The status refreshes on every assistant turn.

Preset Shows Example
off nothing —
today-tokens tokens used today 📊 1.2M today
today-cost USD spent today 📊 $4.32 today
budget most urgent budget + ⚠ 📊 $4.32/$10.00 (43%)
model top model today + turns 📊 glm-5.2 · 23 turns

When a budget is at/over its warn threshold, the budget preset shows ⚠.

/budget

View configured budgets. Add new ones two ways:

  • Interactive (TUI): /budget add — SelectList flow.
  • Args: /budget add <scope> <period> <metric> <max> [warn]
scope:  global | session | project   (project uses the current cwd)
period: day | week | month | year
metric: cost | tot | in | out
max:    positive number (USD if metric=cost, else tokens)
warn:   fraction 0..1, default 0.8

How it works

  • Data source — the message_end event is the only place token usage appears (event.message.usage). after_provider_response has only HTTP status.
  • Storage — append-only JSONL. O(1) write, zero dependencies, crash-safe. An in-memory accumulator (rebuilt once per session, updated incrementally) keeps per-message budget checks O(budgets) instead of O(log).
  • Cost awareness — each record tracks whether the provider returned pricing (costKnown). A model row shows n/a when none of its turns had pricing, and $ as soon as any does.
~/.pi/agent/analytics/
├── usage.jsonl   append-only raw log (one record per assistant message)
├── budgets.json  budget limit config (optional)
├── warned.jsonl  append-only dedup keys for fired warnings (resets per period)
└── footer.json   chosen footer preset (optional)

Architecture

Clean, layered, dependency-injected — dependencies point inward only:

src/
  domain/        pure rules — ZERO dependencies (no fs, no pi)
    types.ts       data contracts (UsageRecord, BudgetLimit, …)
    time.ts        periodKey / windowStartMs / isoWeekKey
    aggregate.ts   aggregate / sumRows / dimensionKey
    budget.ts      budgetKey / statusForLimit / recordMatchesLimit
    enums.ts       DIMENSIONS / WINDOWS
    format.ts      fmtNum / fmtCost / fmtBar / padRight
    session-parser.ts  parse pi session history → UsageRecords
  storage/       infrastructure — implements the ports
    ports.ts       UsageStore + ConfigStore interfaces (total, never throw)
    jsonl-store.ts concrete JSONL/file adapter (real fs)
    memory-store.ts in-memory fake (tests)
  app/
    accumulator.ts in-memory read-side cache (takes UsageStore)
  ui/
    dashboard.ts   pure view — data injected, NO I/O during render, drill-down
    budget-add.ts  interactive /budget add flow (SelectList)
pi-tracker.ts     composition root — wires pi events to the layers (DI seam)
index.ts          re-exports factory for pi's dir-based discovery

There's a check:layers guard that enforces this in CI.

Why this shape: the domain layer has no deps, so it's unit-tested directly; storage is exercised through ports with an in-memory fake (no disk); the view never touches storage during render; every storage method is total (never throws) so a disk fault can't crash pi.

Development

npm install
npm test                # unit tests (pure domain + store fake)
npm run typecheck       # typecheck (strict + unused checks)
npm run check:layers    # architectural layer guard
npm run smoke           # runtime smoke (DI + capture + render)
npm run ci              # all of the above (what CI runs)

See CONTRIBUTING.md for the full guide.

Community

This project is no longer actively maintained, so issues, discussions, and pull requests are not monitored. Existing documentation remains available for historical reference.

License

MIT © Alper Tarhan

About

No description, website, or topics provided.

Resources

Code of conduct

Contributing

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages