Architecture
llm-usage-metrics is organized around a small report-command architecture:
- report definitions describe command identity, shared option profile, and examples
- builders parse and normalize local usage data
- feature aggregators reshape that data into report-specific totals or series
- renderers emit terminal, JSON, Markdown, or share artifacts
- a shared report runtime handles output-format resolution, diagnostics, and stdout/stderr behavior
Report definitions and runtime
Section titled “Report definitions and runtime”src/cli/report-definitionsCanonical metadata fordaily,weekly,monthly,compare,efficiency,optimize,trends,session,wrapped,doctor, andprune(config initis registered directly increate-cli.ts)src/cli/report-runtimeShared prepare/run lifecycle for format validation, share handling, terminal overflow warnings, and stdout emission
This same metadata is reused for:
- Commander command registration
- generated CLI docs
- root help examples
That keeps command/docs drift low without duplicating option descriptions outside Commander help.
Report flows
Section titled “Report flows”buildUsageData(...) parses source events, applies pricing, aggregates by period/source/model, then renderUsageReport(...) formats the report.
Efficiency
Section titled “Efficiency”buildEfficiencyData(...) reuses usage parsing, attributes events to a repository root, joins them with local Git outcomes, then renderEfficiencyReport(...) formats the result.
Optimize
Section titled “Optimize”buildOptimizeData(...) reuses usage parsing and pricing, aggregates baseline token totals, computes candidate-model counterfactual costs, then renderOptimizeReport(...) formats the result.
Trends
Section titled “Trends”buildTrendsData(...) reuses usage parsing, loads pricing only for cost mode, aggregates to daily totals, projects those totals into daily trend series, then renderTrendsReport(...) formats terminal charts or JSON.
Compare
Section titled “Compare”buildCompareData(...) resolves the two date windows, builds one priced dataset spanning both, then aggregates each window separately and computes the deltas between them.
Session
Section titled “Session”buildSessionData(...) reuses usage parsing and pricing, then groups priced events per conversation session — or per repository root with --by-repo — with a top-N row limit.
Wrapped
Section titled “Wrapped”buildWrappedData(...) resolves the recap year in the report timezone, builds the priced dataset for that year’s date range, then aggregates a yearly recap.
Doctor and prune
Section titled “Doctor and prune”doctor and prune are non-report commands with their own runners: doctor checks source discovery health and runtime configuration, and prune previews or deletes departed files from the event store.
Event store and history
Section titled “Event store and history”src/persistence/event-store.ts owns a local SQLite ledger: schema, migrations, per-file ingest on parse, and stored-event revalidation. Schema changes run as migrations, and an unknown newer schema disables the store rather than rebuilding tables.
src/persistence/event-store-history.ts serves --history. It compares the current run’s discovered (source, file) pairs against stored files, replays departed files for the selected sources, and suppresses moved or copied files by content hash. History events join the pipeline before the provider/model/date filters, pricing, and aggregation, so report shapes stay unchanged. prune uses the same classification to delete departed-file rows and vacuum the ledger.
Aggregation and rendering details
Section titled “Aggregation and rendering details”aggregateUsage(...)supports anincludeModelBreakdownprofile Usage keeps per-model breakdowns; efficiency, optimize, and trends skip them.render/unicode-table.tsis driven by explicit row metadata (periodKey,rowKind) This keeps sorting and separators deterministic without coupling the generic table renderer to usage-row domain types.
Module boundaries
Section titled “Module boundaries”src/cli: command creation, report builders, diagnostics emissionsrc/cli/report-definitions: command metadata and shared option profilessrc/cli/report-runtime: shared report lifecyclesrc/config: TOML user-config loading and runtime override/env-var resolutionsrc/sources: adapter discovery and parsingsrc/domain: canonical normalized contractssrc/pricing: LiteLLM pricing loader/cache/cost enginesrc/persistence: SQLite event-store ledger, schema migrations, history suppressionsrc/aggregate: period/source usage aggregationsrc/efficiency: repo attribution and Git outcome joinssrc/optimize: counterfactual pricing aggregationsrc/trends: daily trend-series aggregationsrc/session: per-conversation and per-repository usage aggregationsrc/wrapped: yearly recap aggregationsrc/render: output formatterssrc/utils: shared fs, JSONL, time, and logging helpers
Invariants
Section titled “Invariants”- deterministic ordering for periods, sources, candidates, and models
- diagnostics on
stderr, report body onstdout - JSON/Markdown stay data-only on
stdout - provider identifiers are normalized at the domain boundary
- incomplete pricing remains explicit in output (
~, warnings, or incomplete flags)
Deep dives
Section titled “Deep dives”- Event Store — the SQLite usage-event ledger: ingest, fingerprints, history, and prune.
- Parse Pipeline — how source files are discovered, filtered, parsed, and parallelized.
- Pricing Pipeline — how LiteLLM pricing is fetched, cached, matched to model names, and turned into costs.
- Config & Logging — how the TOML config resolves against flags and env vars, and how stderr logging is leveled.