ccusage by @ryoppippi. It parses the same kinds of local agent logs, applies the same deduplication logic for Claude, and prices tokens with the same LiteLLM snapshot.
Parsing and pricing run on your Mac, against files the agent CLIs already wrote there. No usage data leaves the device unless you explicitly turn on Mac → iPhone sync or link the Mac to an organization. The one column that arrives from elsewhere is Continuum-hosted inference, which has no local log to read.
ccusage daily is the ground truth. If Continuum’s numbers diverge from ccusage, ccusage is correct.
What gets parsed
The loader walks each provider’s own on-disk logs. A provider whose store isn’t there — because you don’t use it, or its CLI isn’t installed — is skipped cleanly rather than treated as an error, and providers you’ve turned off in Settings → Providers get no column at all.The parsed-source list grows as Continuum adds providers. Every column except Continuum-hosted comes from a log the agent CLI wrote on your own machine — Continuum reads those files, it does not instrument the CLIs.
Deduplication
Claude’s JSONL frequently repeats the same assistant turn across files when a session resumes. Continuum collapses those on the message’sid — with the top-level requestId as an optional qualifier when present. (Recent Claude Code builds dropped requestId, so requiring both would double-count resumed history; keying on message.id matches ccusage’s outcome.) Cross-file duplicates are caught at merge time, not just within a file.
Codex carries a cumulative total_token_usage snapshot rather than per-turn counts, so Continuum computes per-turn deltas by subtracting the previous cumulative. Zero-delta heartbeats are dropped, and a non-monotonic drop (a session reset mid-file) is treated as a fresh baseline rather than a negative delta. Codex records carry no dedup key — the delta math is the dedup.
Multi-account aggregation
You can add a second account on every provider (see multi-account), but only Claude and Codex contribute extra history to this loader. Those two write their logs under a per-instance config directory ($CLAUDE_CONFIG_DIR/projects, $CODEX_HOME/sessions), so the loader merges each account’s root into the Claude and Codex passes alongside the default account and the history rolls into the same aggregate totals.
The other providers isolate accounts by other means — an API key at spawn, an isolated HOME, an XDG_DATA_HOME — which routes and bills the session correctly but produces no second on-disk log root to walk. Their spend still lands in analytics; it just isn’t split by account.
The extra roots are re-read on every refresh, so an account you add in Settings joins the totals on the next refresh without rebuilding the store or relaunching the app.
Totals are aggregated across accounts in this version — there is no per-account spend breakdown yet. See Providers for the multi-account setup flow.
Time windows
Windows are calendar-day aligned in your local timezone, matching ccusage’s
daily default. UTC bucketing was evaluated and rejected — it diverges from ccusage near midnight.
The tokens-by-model breakdown on the Usage tab is windowed slightly differently (it also exposes a trailing-90d view), but the dollar totals and per-repo split use the four windows above.
Repo identity
Continuum walks up the directory tree from each record’s working directory to find the canonical git repository, so every branch and worktree of one repo rolls up to a single row. It handles:- Regular git directory — the directory containing
.gitis the repo root. - Worktree pointer file — reads the
gitdir:line (resolving a relative pointer against the.gitfile’s own parent) and buckets under the main worktree. - Conductor workspace (
<…>/conductor/workspaces/<repo>/<branch>) — introspects a live sibling branch’s.gitpointer to discover the underlying main repo, falling back to a stable per-repo bucket. - Claude Code worktree (
<repo>/.claude/worktrees/<branch>) — collapses every worktree of a repo into one bucket. - Sole-git-child descent — if a directory isn’t itself a repo but contains exactly one child that is, it collapses to that child (covers launching an agent from a parent folder).
Pricing
Token costs are calculated from a bundled LiteLLM pricing snapshot, so analytics work offline and totals are reproducible. Tiered pricing — rates that change above a prompt-size threshold — is applied per model. Models that aren’t in the snapshot are tracked under “unpriced model tokens” so their token counts still appear even when no dollar figure can be attached. A second bundled file of manual overrides is merged on top of the snapshot and wins per key. It exists for rates LiteLLM hasn’t shipped yet — newly released frontier models, xAI and Z.ai GLM ids, and the models Continuum hosts itself — and doubles as an audit trail: each manual entry carries a note recording the source of the rate and the date it was verified. Contributors can refresh the snapshot withtools/refresh-pricing.sh, which fetches the current LiteLLM table, filters it to the model families Continuum tracks (claude-*, gpt-*, o[0-9]*, chatgpt-*, gemini-*, gemma-*, grok-*, xai/*), then re-applies the manual overrides on top.
Cache
Parsed results cache to the app’s container, falling back to:Mac → iPhone sync
The Mac writes a usage snapshot to iCloud key-value storage under the keycloud.analytics.v1. iPhone picks it up when iCloud reports the change and renders the same totals, chart, and per-repo split. Nothing leaves the device except through this key.
If this Mac is linked to an organization, detailed per-repo analytics are never mirrored to personal iCloud — the snapshot is suppressed and any snapshot written before the link is cleared, so an employee’s repo names don’t end up in personal iCloud while their usage reports to the org. See Privacy and Organization.
Loader performance
The loader walks every provider directory in parallel, parsing files at background priority so a multi-gigabyte cold reparse doesn’t starve the rest of the app. Overlapping refresh requests coalesce — a second caller awaits the first’s result instead of starting a parallel walk — and a lightweight mtime probe lets the store skip a refresh entirely when nothing on disk changed since the last load.Related
- Usage — the live quota gauges and the analytics panel below them.
- Providers — connecting subscriptions and adding multiple accounts.
- Hosted inference — where the Continuum column’s numbers come from.
- Mobile — iCloud sync to iPhone.
- Privacy — what stays local and what (with opt-in) leaves the device.