feat(stats): add --by-repo and --by-time breakdowns to teamai stats#193
Conversation
Suggestion: make this PR independent of #191/#192I confirmed that the stats change itself is functionally standalone — the stacking here is purely at the git level, not in the code. The actual commit for this PR (
The Could you rebase this so it stands alone? Cherry-picking git fetch origin
git switch -c feat/stats-by-repo-only origin/main
git cherry-pick 6cc98eff
# force-push to the PR branch, or open a new PR against origin/mainIndependent of the split, three small follow-ups on the stats code:
|
…ats` `teamai stats` aggregated tokens/turns/interventions only in one lump. This adds two optional views over the same local dashboard event stream — no new data collection: teamai stats --by-repo # per-project sessions / turns / tools / tokens / interventions teamai stats --by-time # 24h activity sparkline, peak hour, active minutes, night-owl % Ports the shape of claude-cloud-sync's stats.json analytics (by_repo, timeline_by_hour, active_minutes, night-owl ratio) to teamai-cli's data. Attribution is at session granularity (one cwd per session), not per-turn. New building blocks: - utils/repo-attribution.ts: attributeRepo(cwd) → project label; canonicalRepo() ports repo_canon so `github.com/o/r` and `cnb.cool/o/r` collapse to the same platform-independent `owner/repo`. - session-analytics.ts: attributeByRepo() + timeAnalytics() pure folds. Review feedback addressed: - renderHourSparkline now renders empty hours as `·`, distinct from `▁` low-but-nonzero activity. - --by-repo prints `… and N more` when there are over 15 repos. - Added direct unit tests for renderHourSparkline (all-zero + single-peak). Default `teamai stats` output is unchanged; new sections appear only with the flags. Test Plan: - npx tsc --noEmit - npx vitest run (142 files, 1760 tests; 15 in session-analytics.test.ts) - npm run build - real-CLI E2E: seeded ~/.teamai/dashboard/events.jsonl across 17 repos and several hours; `stats --by-repo --by-time` printed the top-15 table with `… and 2 more`, and an hourly sparkline with `·` for idle hours and bars for active ones (peak 02:00, night-owl 67%). Claude-Session: https://claude.ai/code/session_01GEV81Xj6mhPzSPsyBrDPSd
6cc98ef to
c373fde
Compare
|
Rebased to stand alone — cherry-picked onto a fresh branch from All three follow-ups done:
Re-verified: Note: the CI DSN-scanner finding is not from this PR's files, but the whole-repo scan may still flag it until #207 (the |
Summary
teamai statsreported tokens / turns / interventions as a single lump. This adds two optional breakdowns over the same local dashboard event stream — no new data collection:Ports the shape of claude-cloud-sync's
stats.jsonanalytics (by_repo,timeline_by_hour,active_minutes, night-owl ratio) to the data teamai-cli already has.Type of Change
Details
utils/repo-attribution.ts—attributeRepo(cwd)maps a working directory to a project label (basename), andcanonicalRepo()portsrepo_canonso a remote-formgithub.com/o/randcnb.cool/o/rcollapse to the same platform-independentowner/repo. Today a filesystem cwd degrades to its basename; the canonicalization is future-proof for when a git remote is captured.session-analytics.ts—attributeByRepo()andtimeAnalytics()are pure folds;timeAnalyticsuses the machine's local timezone for hour-of-day.teamai statsoutput is unchanged; the new sections appear only with the flags.Test Plan
npx tsc --noEmitnpx vitest run— 141 files, 1754 tests (13 new insession-analytics.test.ts)npm run build~/.teamai/dashboard/events.jsonlacross two repos and two hours;node dist/index.js stats --by-repo --by-timeprinted a per-repo table (alpha 150 tok / beta 15 tok, sorted) and an hourly sparkline (peak02:00, active4 min, night-owl67%) — all matching the seed.Notes for Reviewers
no_repois used for home/root/ops cwds; the per-repo table includes it when present.stats(the sync fixed this to Beijing time; local felt more appropriate for a per-user CLI). Easy to make configurable if preferred.