Know what your AI coding tools actually cost.
Local-first analytics for AI-assisted development. Bring usage from Claude Code, Codex, OpenCode, and Grok Build into one private, consistent view.
Website · Documentation · Releases
AI coding tools make it easy to spend tokens across different providers, accounts, projects, and machines—and surprisingly hard to understand the total. StatsAI reads the logs those tools already keep, normalizes them locally, and turns them into useful answers:
- Which tools, models, projects, and devices use the most tokens?
- What would that usage cost at API-equivalent prices?
- Is a subscription delivering more value than its monthly price?
- What work did those sessions contribute to?
- How is usage changing over time?
No provider API keys. No request proxy. Your raw logs stay on your machine.
Note
StatsAI is under active development. The CLI is usable today, but its public API and data model may still change before 1.0.
| Capability | Value | |
|---|---|---|
| 📊 | One usage model | Compare providers, models, accounts, projects, and devices without reconciling incompatible reports. |
| 💸 | Cost and subscription context | Estimate API-equivalent cost and compare it with time-bounded subscription periods. |
| 🔒 | Local-first collection | Parse, normalize, store, and report on usage locally in SQLite. |
| 🧭 | Work and conversation history | Rebuild task spans and preserve a provider-independent, searchable local conversation archive. |
| ☁️ | Optional private dashboard | Sync sanitized rollups from multiple machines to statsai.dev. |
| 🧩 | CLI, SDK, and daemon | Use StatsAI interactively, embed it in Rust, or power local widgets through a loopback API. |
flowchart LR
A["Local AI tool logs"] --> B["StatsAI collector"]
B --> C["Normalized local SQLite store"]
C --> D["CLI reports"]
C --> E["Task and conversation archive"]
C --> F["Optional sanitized sync"]
F --> G["Private StatsAI dashboard"]
StatsAI discovers supported local sources, converts their records into a common schema, and writes them idempotently. Repeated scans use a lightweight file signature cache, so unchanged history is skipped while active logs stay current.
On macOS with Homebrew:
brew install starkdmi/tap/statsaiOr use the release installer:
curl -LsSf https://github.com/starkdmi/statsai/releases/latest/download/statsai-installer.sh | shYou can also install the published Rust crate:
cargo install statsai
# or
cargo binstall statsaiGitHub Releases include a universal macOS CLI archive and StatsAI.app.
Preview reads the default provider locations without writing to SQLite:
statsai scan --previewLimit the scan to one provider when needed:
statsai scan --provider codex --previewYou will see a normalized summary similar to:
codex account=work path=~/.codex-work usage_events=123 input=1,000,000 cached=800,000 output=20,000 total=1,030,000 est_cost=$1.23
statsai scan
statsai report weekly
statsai report monthly
statsai report all-timeScans are idempotent: running the same scan again refreshes improved metadata without duplicating usage events.
statsai auth login
statsai sync --sink http --since-lastNormal login opens statsai.dev to approve the device. For a remote or headless machine:
statsai auth login --headless --device-name "Build server"StatsAI separates local evidence from the smaller dataset needed for a hosted dashboard.
| Stays local | Can be included in hosted sync |
|---|---|
| Complete prompts, model responses, and archived conversations | Daily token and request rollups |
| Raw provider log lines | Provider, model, and account metadata |
| Parse evidence and local source paths | Project labels and repository anchors |
| — | Opt-in private task snapshots and verifications, including bounded task titles, summary previews, and todo excerpts |
Raw usage events and complete archived conversation records stay local and are
never included in hosted sync. StatsAI does not upload full prompts, full
responses, or raw provider logs. When hosted task sync is explicitly enabled
with statsai sync --include-tasks, sync_batch.v2 may include bounded
conversation-derived task titles, summary previews, and todo excerpts. You can
inspect the exact sync contract in
docs/sync-contract.md and verify the resolved sync
target with:
statsai sync --sink http --verifyStatsAI can also create a separate, local pseudonymized dataset from archived conversations. This is an explicit operation—not part of normal scanning or sync:
statsai privacy setup --help
statsai privacy filter
statsai privacy statusPrivacy setup requires paths to the MLX server, MLX model, and Kingfisher
helper. Run statsai privacy setup --help for the current asset options before
filtering.
Statistical detectors can miss sensitive content, so filtered data should be treated as pseudonymized rather than anonymous.
| Provider | Default locations |
|---|---|
| Claude Code | ~/.config/claude, ~/.claude |
| Codex | ~/.codex |
| OpenCode | ~/.local/share/opencode |
| Grok Build | ~/.grok |
Add non-default locations explicitly:
statsai source add --provider codex --path "$HOME/.codex-work"
statsai source listOpenCode and Grok Build also support OPENCODE_DATA_DIRS,
GROK_DATA_DIRS, and GROK_HOME for automation.
Map a source to the account that was active during a specific period:
statsai source connect \
--path "$HOME/.codex-work" \
--email work@example.com \
--label work \
--started-at 2026-05-01Then register subscription periods to add value context to reports:
statsai subscription add \
--provider codex \
--email work@example.com \
--plan Pro \
--price 20 \
--started-at 2026-05-01 \
--paid-at 2026-05-01
statsai report monthly --subscriptionsCanonical accounts are created when an identity such as --email,
--provider-user-id, or --provider-account-id is first used. Labels such as
work and personal are display metadata, not account identity.
For Claude Code sources in auto verification mode, StatsAI reads only
oauthAccount.accountUuid, oauthAccount.emailAddress, and
oauthAccount.profileFetchedAt from the local .claude.json profile. It does
not invoke the Claude CLI or contact a service. This is reported as an
inferred identity because Claude transcripts do not record per-session
credential provenance. Durable API-key, token, cloud-provider, gateway, and
credential-helper settings in managed, user, and project scopes suppress the
inference. Missing session indexes are recovered from bounded transcript
metadata when possible; otherwise attribution fails closed. One-shot environment
overrides cannot be reconstructed by file-only collection, so auto is a
best-effort source-wide policy. Use manual_only plus explicit source connections
for mixed-credential history.
Ask a scan to extract local task spans and rebuild derived work items:
statsai scan --include-tasks
statsai task list
statsai task show work_123 --include-evidence
statsai task verify accept work_123
statsai task benchmarkThe benchmark becomes a meaningful quality gate after you record verified
ground truth. See docs/task-collection.md and
docs/task-benchmarking.md.
Collect complete conversations into the local SQLite store, then search or export them independently of the original provider format:
statsai conversation collect --provider codex
statsai conversation search "database AND migration"
statsai conversation show conv_123
statsai conversation export conv_123 --format json
statsai conversation statsCollection is additive and incremental. StatsAI retains visible messages,
readable reasoning, and referenced artifacts within documented size limits.
Opaque encrypted reasoning is ignored. See
docs/conversation-archive.md for retention and
completeness guarantees.
When raw history is missing, import aggregate usage as reported evidence:
statsai import summary \
--path ./reported_usage_summaries.json \
--dry-run \
--verboseImported summaries remain separate from trusted local events and appear as “summary reports (not added to event totals).”
Source and account management
statsai source list
statsai source add --provider codex --path "$HOME/.codex-work"
statsai source history --path "$HOME/.codex-work"
statsai source connect --path "$HOME/.codex-work" --email work@example.com --started-at 2026-05-01
statsai source disconnect --path "$HOME/.codex-work" --email work@example.com --ended-at 2026-06-01
statsai source disable --source-id src_123
statsai source enable --source-id src_123
statsai source remove --source-id src_123
statsai source remove --source-id src_123 --delete-data
statsai account listsource remove deletes only the configuration unless --delete-data is
provided. With that flag it also removes linked local events, summaries,
rollups, and scan-cache entries.
Scanning and reporting
statsai scan --preview
statsai scan --provider opencode --preview
statsai scan --provider grok-build --preview
statsai scan --no-cache
statsai scan --replace
statsai report weekly
statsai report monthly --subscriptions
statsai report all-time --json --verboseNormal scans use a per-source file signature cache. --no-cache forces a
one-off reread; --replace performs a destructive source rebuild.
JSONL input is streamed with a 16 MiB per-record ceiling. Invalid or oversized records are counted and discarded through the next newline, after which parsing continues.
Subscriptions, tasks, and conversations
statsai subscription add --provider claude --email personal@example.com --plan Pro --price 20 --started-at 2026-05-15 --paid-at 2026-05-15
statsai subscription change --provider codex --email work@example.com --plan Pro --price 200 --started-at 2026-06-01
statsai task list
statsai task show work_123 --include-evidence
statsai task verify accept work_123
statsai task benchmark
statsai task export --level span --format jsonl
statsai conversation collect --provider codex --verbose
statsai conversation list
statsai conversation search "database AND migration"
statsai conversation show conv_123
statsai conversation export conv_123 --format json
statsai conversation statsAuthentication and sync
statsai auth login
statsai auth login --no-open
statsai auth login --headless --device-name "Mini server"
statsai auth status
statsai sync --sink file --output ./statsai-sync-batch.json
statsai sync --sink http --since-last
statsai sync --sink http --verify
statsai sync --status
statsai schema sync-batchHTTP sync uses the stored device session unless --auth-token or
STATSAI_SYNC_TOKEN is provided. Access tokens are refreshed automatically.
The daemon binds to loopback for local widgets and toolbar integrations.
/health is public; every other route requires the per-install bearer token
stored at ~/.statsai/daemon-token.
curl -H "Authorization: Bearer $(cat ~/.statsai/daemon-token)" \
http://127.0.0.1:8765/accountsBrowser-originated requests are rejected. Sync writes must use
Content-Type: application/json and stay below 8 MiB.
crates/statsai-sdk exposes a facade for embedding collection and reporting in
Rust applications. The backend-facing sync boundary begins at
sync_batch.v1; use statsai schema sync-batch to inspect it.
| Crate | Responsibility |
|---|---|
statsai-core |
Normalized types, stable IDs, schemas, and privacy metadata |
statsai-adapters |
Claude Code, Codex, OpenCode, and Grok Build adapters |
statsai-store |
Local SQLite persistence |
statsai-pricing |
Time-aware provider pricing and API-equivalent estimates |
statsai-privacy |
Local privacy filtering and pseudonymization |
statsai-sync |
Pluggable stdout, file, and HTTP sync sinks |
statsai-daemon |
Authenticated loopback API |
statsai-sdk |
Rust SDK facade |
statsai |
Command-line application |
statsai-menubar |
macOS menu bar application |
Local paths are hashed in source identity fields. Source and parse-evidence path labels are stripped from sync payloads, while project location labels can be retained for owner-facing project linking.
Cost figures are API-equivalent estimates, not subscription invoices. StatsAI selects published pricing by usage timestamp, preserves integer micro-USD values, and leaves cost unknown when a source does not prove the billable model.
This is a Rust workspace. Run the same checks used in CI:
./scripts/rust-ci.sh fullInstall repository-local hooks for formatting and Clippy before commits and the full suite before pushes:
./scripts/install-git-hooks.shFor an exceptional one-command bypass, set STATSAI_SKIP_LOCAL_CI=1.
The hosted API and web UI live in independent Git repositories nested in this workspace and deploy to Cloudflare. Follow their repository-specific runbooks:
- API Worker and D1 migrations:
apps/api/DEPLOY.md - Web UI on Cloudflare Pages:
ui/DEPLOY.md - Workspace overview and production URLs:
DEPLOY.md
To target a compatible local backend:
export STATSAI_API_URL="http://127.0.0.1:8787"
export STATSAI_WEB_URL="http://127.0.0.1:3000"
statsai auth login
statsai sync --sink http --endpoint http://127.0.0.1:8787/api/sync/batchesCredential-bearing requests require HTTPS except for explicit numeric loopback
hosts such as 127.0.0.1 and [::1].
StatsAI is open source under the Apache License 2.0.
