Repository navigation
feat(cli): add agentplay stats - #71
Conversation
Closes Entelligence-AI#63. `agentplay stats` sums the sessions `list` would show, one row per agent, plus a total. It reads only `~/.agentplay/index.json`, so it needs no map files and makes no network call. $ agentplay stats --since 7d sessions cost time calls errors claude-code 41 $38.20 11.4h 3,912 118 codex 12 $6.75 3.1h 804 22 cursor 5 n/a 0.9h 211 4 total 58 $44.95 15.4h 4,927 144 Shared filtering, not a copy: the `--agents` / `--since` loop moves out of `cmd_list` into `_selected_rows`, and the two flag definitions into `_filter_flags`, so a window selects the same sessions in both commands and cannot drift. Subagent and helper rows stay excluded, so work already counted in a parent map is not counted twice. `cmd_list` is unchanged behaviourally; the mtime fallback for imported maps and discovery's zero sentinel is now `_changed_since`. Cost is None, shown as `n/a` and serialised as `null`, when no session in a row had a priced request. Cursor records no usable token counts, and a $0.00 there reads as free rather than unknown. This is the same distinction `facts.hasCost` already draws, computed from the same `costUsd` field rather than a second definition of "unpriced". Rows order by spend descending, then session count, then name, so an agent with no cost data at all sorts last. Column widths are computed from the cells, so a long agent name (`antigravity-cli`) or a six-figure call count still lines up. The total row is bolded after padding, so the escape codes cannot shift a column, and `NO_COLOR` drops them. `--json` emits `{"agents": [...], "total": {...}}` with `pricedSessions` alongside each `costUsd`, and still emits a zeroed report when nothing matches, so a script gets the same shape either way. Tests (11, in test_cli_output.py): per-agent and overall totals, subagent/helper exclusion, both filters asserted to select the same sessions as `list`, the n/a case and its note, both empty messages, empty `--json`, and rejected filters. make check passes: ruff, tsc, 1453 python tests, 354 viewer tests, the hygiene guard.
EntelligenceAI PR SummaryAdds flowchart TD
classDef newBehavior fill:#dcfce7,stroke:#16a34a,color:#14532d;
A["agentplay stats"] --> B["Read local index"]
B --> C["Shared session filters"]
C --> D["Group rows by agent"]
D --> E["Sum metrics and totals"]
E --> F["Table or JSON output"]
G["agentplay list"] --> C
class A,C,D,E,F newBehavior;
Review Scorecard
Safe to merge with a low blast radius; code quality rated Excellent (5/5): the review found nothing to flag. Key Findings:
Evaluated against
|
WalkthroughAdds ChangesFiles
Added the Files
Documented |
|
LGTM, thanks @coderwithsense for the contribution! |
Closes #63.
agentplay statssums the sessionslistwould show, one row per agent, plus a total. It reads only~/.agentplay/index.json, so it needs no map files and makes no network call.Shared filtering, not a copy: the
--agents/--sinceloop moves out ofcmd_listinto_selected_rows, and the two flag definitions into_filter_flags, so a window selects the same sessions in both commands and cannot drift. Subagent and helper rows stay excluded, so work already counted in a parent map is not counted twice.cmd_listis unchanged behaviourally; the mtime fallback for imported maps and discovery's zero sentinel is now_changed_since.Cost is None, shown as
n/aand serialised asnull, when no session in a row had a priced request. Cursor records no usable token counts, and a $0.00 there reads as free rather than unknown. This is the same distinctionfacts.hasCostalready draws, computed from the samecostUsdfield rather than a second definition of "unpriced".Rows order by spend descending, then session count, then name, so an agent with no cost data at all sorts last. Column widths are computed from the cells, so a long agent name (
antigravity-cli) or a six-figure call count still lines up. The total row is bolded after padding, so the escape codes cannot shift a column, andNO_COLORdrops them.--jsonemits{"agents": [...], "total": {...}}withpricedSessionsalongside eachcostUsd, and still emits a zeroed report when nothing matches, so a script gets the same shape either way.Tests (11, in test_cli_output.py): per-agent and overall totals, subagent/helper exclusion, both filters asserted to select the same sessions as
list, the n/a case and its note, both empty messages, empty--json, and rejected filters.make check passes: ruff, tsc, 1453 python tests, 354 viewer tests, the hygiene guard.
Summary
Test plan
make checkpasses