Add data layer support for real-time usage visualization:
- MinuteStats type: holds token counts for 5-minute buckets, enabling
granular recent-activity views (12 buckets covering the last hour).
- AggregateTodayHourly(): computes 24 hourly token buckets for the
current local day by filtering sessions to today's date boundary and
slotting each into the correct hour index. Tracks prompts, sessions,
and total tokens per hour.
- AggregateLastHour(): computes 12 five-minute token buckets for the
last 60 minutes using reverse-offset bucketing (bucket 11 = most
recent 5 minutes, bucket 0 = 55-60 minutes ago). Bounds-clamped to
prevent off-by-one at the edges.
Both functions filter on StartTime locality and skip zero-time sessions,
consistent with existing aggregation patterns in the pipeline package.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Address golangci-lint findings and improve error handling throughout:
Package doc comments:
- Add canonical "// Package X ..." comments to source, model, config,
pipeline, cli, store, and main packages for godoc compliance.
Security & correctness:
- Fix directory permissions 0o755 -> 0o750 in store/cache.go Open()
(gosec G301: restrict group write on cache directory)
- Fix config.Save() to check encoder error before closing file, preventing
silent data loss on encode failure
- Add //nolint:gosec annotations with justifications on intentional
patterns (constructed file paths, manual bounds checking, config fields)
- Add //nolint:nilerr on intentional error-swallowing in scanner WalkDir
- Add //nolint:revive on stuttering type names (ModelStats, ModelUsage)
that would break too many call sites to rename
Performance (perfsprint):
- Replace fmt.Sprintf("%d", n) with strconv.FormatInt(n, 10) in format.go
FormatTokens() and FormatNumber() hot paths
- Clean up redundant fmt.Sprintf patterns in FormatCost and FormatDelta
Code cleanup:
- Convert if-else chain to switch in parser.go skipJSONString() for clarity
- Remove unused indexedResult struct from pipeline/loader.go
- Add deferred cache.Close() in pipeline/bench_test.go to prevent leaks
- Add deferred cache.Close() in cmd/root.go data loading path
- Fix doc comment alignment in scanner.go decodeProjectName
- Remove trailing blank line in cmd/costs.go
- Fix duplicate "/day" suffix in cmd/summary.go cost-per-day formatting
- Rename shadowed variable 'max' -> 'maxVal' in cli/render.go Sparkline
Define the core data structures that flow through the entire pipeline:
- model/session.go: SessionStats (per-session aggregates including
token counts across 5 categories — input, output, cache_write_5m,
cache_write_1h, cache_read), APICall (deduplicated by message.id,
keyed to the final billed usage), and ModelUsage (per-model
breakdown within a session). Tracks subagent relationships via
IsSubagent/ParentSession fields.
- model/metrics.go: Higher-order aggregate types — SummaryStats
(top-level totals with per-active-day rates for cost, tokens,
sessions, and minutes), DailyStats/HourlyStats/WeeklyStats
(time-bucketed views), ModelStats (cross-session model comparison
with share percentages), ProjectStats (per-project ranking), and
PeriodComparison (current vs previous period for delta display).
- model/budget.go: BudgetStats with plan ceiling, custom budget,
burn rate, and projected monthly spend for the budget tab.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>