token-finops-cli
Read-only, local-first token usage and budget-runway tracker for AI coding agents. v0.3.0 grew this out of the original 0.2.x line — a single 600-line script that read GitHub Copilot CLI's local telemetry — into a modular tool with 9 adapters (Copilot, Claude Code, Codex CLI, Gemini CLI, Hermes Agent, OpenCode/Kilo, Cline/Roo/Kilo, Aider, Continue.dev), a shared runway engine, a self-audit for Claude Code sessions, and a local-vs-cloud savings estimator. The philosophy hasn't changed: standard library only, no runtime dependencies, and it never writes to any tool's own data store — SQLite is opened read-only/immutable where applicable.
Everything below that talks about the original Copilot budget/cycle/session reports still works exactly as it did in 0.2.x.
Install
uv tool install token-finops-cli # recommended
pipx install token-finops-cli
pip install token-finops-cli
60-second tour
$ token-finops adapters
tool found root
copilot yes ~/.copilot
claude_code yes ~/.claude
codex no ~/.codex
gemini_cli no ~/.gemini
hermes no ~/.hermes
opencode no ~/.local/share/opencode
cline no ~/.config/Code/User/globalStorage
aider no ~
continue no ~/.continue
Empty output? doctor says why, per tool, and what to do about it:
$ token-finops doctor
token-finops doctor — what `report` can and cannot see on this machine
token-finops-cli 0.4.0
Python 3.12.7 (/usr/local/bin/python3)
platform Darwin 24.5.0
[found] Claude Code (claude_code) — 13305 events, latest 2026-09-14 20:14 UTC
probed: ~/.claude/projects (exists)
hint: 5h/7d quota percentages only exist in the status line: wire
`token-finops collect-statusline` as statusLine.command in ~/.claude/settings.json.
[empty] OpenAI Codex CLI (codex)
probed: ~/.codex (exists)
hint: Codex CLI's home exists but has no rollout-*.jsonl sessions — run `codex` once.
[missing] Gemini CLI (gemini_cli)
probed: ~/.gemini (not found)
hint: No ~/.gemini. Install the Gemini CLI (`brew install gemini-cli` or
`npm install -g @google/gemini-cli`), or set GEMINI_CLI_HOME.
...
summary: 1 with data, 1 installed but no usage yet, 7 not found
found = the adapter read usage events; empty = the tool's data location
exists but holds no usage yet; missing = the coding agent itself does not
look installed (or its data lives somewhere else — every entry names the
environment variable that repoints it). doctor always exits 0: an empty
machine is a normal state, not a failure. --tool narrows it to one adapter.
$ token-finops report --compact
GitHub Copilot CLI [######--------------] 29.4% runway 41.2d OK
Claude Code [############--------] 61.0% runway 1.9h WARN
binding constraint: Claude Code (5h) — runway 1.9h -> WARN
$ token-finops sessions --tool claude_code --since 30d
Claude Code sessions (last 30d):
session_id started calls tokens API-eq $
0bbc8283-… 2026-09-05 09:12 428 73.1M 87.28
…
$ token-finops self-audit
Self-audit: Claude Code session 0bbc8283-…
API calls (deduplicated): 428 = main loop 211 + sub-agents 217
total tokens: 73.1M (95 % of them cache reads)
API-equivalent: $87.28
By model:
claude-fable-5-1 255 calls $73.25 84%
claude-sonnet-5 172 calls $14.03 16%
Sub-agents = 30 % of API-equivalent cost
$ token-finops savings --hardware mac-studio-m4-max-128gb --model qwen3-32b --utilization 0.2
local total: $8.86 / 1M tok (capex-dominated at 20 % utilisation)
cloud (sonnet): $3.20 / 1M tok -> CLOUD CHEAPER
break-even utilisation: 60 % of 24/7
$ token-finops break-even --hardware mac-studio-m4-max-128gb
replaceable cloud usage (haiku/sonnet-class): 35.5M tokens = $14.94 API-equivalent
local alternative: energy $14.29 + capex $116.87 = $131.15
-> cloud still cheaper by $116.21; at the current pace the box never pays off
$ token-finops synth --out /tmp/demo-home --print-env && \
eval "$(token-finops synth --out /tmp/demo-home --print-env)" && \
token-finops report --compact
runs the whole CLI against a fully synthetic, throwaway fake-home tree — no
real telemetry anywhere. See docs/SYNTH.md in the repo root for scenarios
(steady, burst, exhausted, weekend, fresh, quiet,
subagent-heavy).
GitHub Copilot CLI (the original tool)
token-finops-cli still reads ~/.copilot/session-store.db
(assistant_usage_events) directly — no external API calls, nothing
leaves your machine, the DB is opened read-only. (The one exception is
opt-in and never on by default: --online, below.)
token-finops report --tool copilot # 7-day summary + runway (default window)
token-finops report --tool copilot --budget 1500 --cycle-day 1 # your plan's monthly AI units
token-finops report --tool copilot --compact # 2-line minimal output
token-finops report --tool copilot --watch 5 # live-refreshing view every 5s
Session history and break/gap reports (per-session archival view, distinct from the runway report):
token-finops sessions # list all past Copilot sessions
token-finops sessions --since 30d --limit 10
token-finops sessions --session <id> # detailed break/gap report
token-finops sessions --session <id> --gap-minutes 60
token-finops sessions --totals # ONE combined report across ALL sessions
The detailed per-session view reports "breaks" — gaps between requests
longer than --gap-minutes (default 30) — and active vs. idle wall-clock
time. The "AI unit" is GitHub's own cost/usage unit (total_nano_aiu / 1e9)
— the same number shown on the Copilot billing page, not raw token counts.
Claude Code: status-line hook
Anthropic subscriptions expose only a percentage of a rolling 5h/7d window, and only through the status line. Wire the collector as your status line command; every redraw stores a snapshot, and two snapshots in the same window are enough for a burn estimate:
// ~/.claude/settings.json
{ "statusLine": { "type": "command", "command": "token-finops collect-statusline" } }
That alone only writes ~/.token-finops/quota.json; it doesn't render anything in the
status line. If you want the status line itself to show runway (via token-finops status --tool claude_code), note that status --tool claude_code depends on quota.json already
existing (see adapters/claude_code.py's quota(), which reads that file — with it missing,
compute_runway() reports Status.UNKNOWN). Claude Code's statusLine.command only runs
one command, so both steps have to be chained into a single shell line:
// ~/.claude/settings.json
{
"statusLine": {
"type": "command",
"command": "input=$(cat); echo \"$input\" | token-finops collect-statusline >/dev/null 2>&1; token-finops status --format plain --tool claude_code --fresh"
}
}
The collect-statusline half consumes Claude Code's JSON payload from stdin (hence
echo "$input" | ...) and updates quota.json/quota_history.jsonl; the status half then
reads that fresh quota to compute runway. --fresh skips status's own cache so it always
reflects the snapshot collect-statusline just wrote. Run them separately (as below) only
when you're inspecting output by hand, not as the statusLine.command itself.
token-finops report --tool claude_code --json
Claude Code — window: 5h (resets 2026-09-05 12:29 UTC)
budget: [############------------------] 40.0%
window: [############------------------] 40.0% elapsed
pace: 1.00 (on pace)
burn: 20.0% per hour (from snapshots)
runway: 3.0h vs 3.0h left -> OK
Status bars: tmux, starship, waybar, SwiftBar
token-finops status # CC 61% 2h! | CX 17% 3h | CP 29% 41d | binds: CC
token-finops status --format tmux # for status-right (contrib/tmux/token-finops.tmux)
token-finops status --format starship # binding constraint only
token-finops status --format waybar # JSON; also polybar, i3, xbar, json
status serves a cache (~/.token-finops/last.json) and rescans only with --fresh or when
the cache is older than --max-age seconds. Refresh it from one timer (contrib/refresh/) and let
every widget poll freely. Details: docs/TMUX.md.
Self-audit
Claude Code writes one line per content block, so naive counting
over-reports by roughly 1.9x. self-audit deduplicates on
(message.id, requestId), includes sub-agent transcripts, and breaks the
bill down by model — the model, not the raw token count, is what decides
the cost.
token-finops self-audit # latest session
token-finops self-audit --session <id-prefix>
token-finops self-audit --json # machine-readable, for CI/PR checklists
Local vs. cloud: savings and break-even
token-finops savings --list # available hardware/model/tariff keys
token-finops savings --hardware rtx-4090-workstation --model llama-3.3-70b
token-finops savings --power solar-de-feed-in --utilization 0.8
token-finops break-even --since 90d --replaceable-tiers haiku,sonnet
Solar is priced at the feed-in tariff you forgo, not at zero. Capex is
amortised per hour of actual inference. Only Haiku/Sonnet-class work
counts as replaceable by a local model — a 32B model does not do
Opus/Fable-class work. Hardware and energy numbers live in editable JSON
(savings/hardware_profiles.json, savings/energy.json) with sources and
review dates — measure your own box and overwrite them.
Environment variables
Every adapter reads its data root from an override variable before falling
back to the tool's real default location. None of these are required —
they exist so you can point the CLI at a different install, a synthetic
fixture tree, or a non-default $HOME.
| Variable | Overrides | Default |
|---|---|---|
TOKEN_FINOPS_COPILOT_DB |
Copilot session-store DB path | ~/.copilot/session-store.db |
TOKEN_FINOPS_DB |
(legacy alias, sessions/original CLI) Copilot DB path |
~/.copilot/session-store.db |
CLAUDE_CONFIG_DIR |
Claude Code config root (self-audit --config-dir also sets this) |
~/.claude |
CODEX_HOME |
OpenAI Codex CLI home | ~/.codex |
GEMINI_CLI_HOME |
Gemini CLI home | ~/.gemini |
HERMES_HOME |
Hermes Agent home | ~/.hermes |
CLINE_CLI_HOME |
Cline CLI (non-VS Code) home | ~/.cline |
TOKEN_FINOPS_CLINE_DIRS |
Cline/Roo/Kilo VS Code globalStorage roots |
per-OS VS Code data dir |
TOKEN_FINOPS_OPENCODE_DB |
OpenCode/Kilo CLI sqlite DB path | $XDG_DATA_HOME/opencode/opencode.db |
XDG_DATA_HOME |
Base for the OpenCode DB default above | ~/.local/share |
TOKEN_FINOPS_AIDER_DIRS |
Aider chat-history search roots | ~ |
TOKEN_FINOPS_AIDER_ANALYTICS |
Aider opt-in analytics JSONL path | ~/.aider/analytics.jsonl |
CONTINUE_GLOBAL_DIR |
Continue.dev sessions root | ~/.continue |
TOKEN_FINOPS_HARDWARE_JSON |
Override the savings hardware-profiles JSON |
packaged hardware_profiles.json |
TOKEN_FINOPS_ENERGY_JSON |
Override the savings energy/tariff JSON |
packaged energy.json |
TOKEN_FINOPS_ONLINE_CACHE |
Where --online caches provider responses |
~/.token-finops/online-cache.json |
Live quota: --online (opt-in)
token-finops report --online # ask the provider instead of inferring locally
token-finops status --online # same, and implies a rescan
Off by default — without the flag nothing leaves your machine. With it, four providers are
queried for their own current number: Copilot (copilot_internal/user), Claude Code
(api.anthropic.com/api/oauth/usage), Gemini CLI (retrieveUserQuota) and OpenRouter
(/api/v1/key, for Hermes). Credentials you already have — gh auth token/$GITHUB_TOKEN,
~/.claude/.credentials.json, ~/.gemini/oauth_creds.json, $OPENROUTER_API_KEY — are read
to build one request header and are never written, refreshed or cached.
It fails closed: no network, no credential, a 429, an endpoint that changed shape — all of
it falls back silently to the normal offline report, with no error and no traceback. Responses
and failures are cached for 180 s (~/.token-finops/online-cache.json), which is what keeps a
polled status bar from getting rate-limited. Three of the four endpoints are
reverse-engineered and unversioned: docs/sources.md has the table, the ADRs have the
reasoning.
Settings
The variables above say where the data is. A second, smaller set says how you want it
judged — budget thresholds, the monthly cycle day, per-tool allowances, and a default
tool so you need not type --tool claude_code every time. Each has a config.json twin
in ~/.token-finops/config.json, and one precedence rule applies to all of them:
explicit CLI flag > TOKEN_FINOPS_* env var > config.json > hardcoded default
| Variable | config.json key |
Default |
|---|---|---|
TOKEN_FINOPS_WARN_AT |
budget.warn_at |
0.75 |
TOKEN_FINOPS_CRITICAL_AT |
budget.critical_at |
0.90 |
TOKEN_FINOPS_CYCLE_DAY |
budget.cycle_day |
1 |
TOKEN_FINOPS_BUDGET |
budget.allowance.copilot |
per adapter |
TOKEN_FINOPS_ALLOWANCE |
budget.allowance.<tool> |
per adapter |
TOKEN_FINOPS_WINDOW_HOURS |
budget.window_hours |
per adapter |
TOKEN_FINOPS_DEFAULT_TOOL |
default_tool |
none (all detected tools) |
TOKEN_FINOPS_CONFIG |
— | ~/.token-finops/config.json |
TOKEN_FINOPS_WARN_AT=0.5 token-finops report # try a stricter threshold for one run
token-finops doctor # print the settings actually in effect
Invalid values are reported once on stderr and fall back to the default rather than
aborting the run. Full schema, precedence details, and the rule that provider-reported
data (a real resets_at, a real used%) is never overridden by configuration:
docs/CONFIG.md.
Documentation and design rules
This package is one component of the token-finops repository. For the
full guide, the read-only/fractions-not-dollars design rules, the
per-assistant Architecture Decision Records, and the 50 executable use
cases, see the repository root:
README.md— project overview, design rules, related toolsdocs/PLAN.md— structured plan and statusdocs/ADAPTERS.md— how to add an adapterdocs/adr/— one ADR per coding assistantdocs/SYNTH.md— synthetic telemetry generatordocs/USECASES.md— every executable use casedocs/TMUX.md—status --format …for tmux, starship, waybar, polybar, i3, SwiftBardocs/INTEGRATIONS.md— editor / agent-native / desktop integrations (design doc)CHANGELOG.md— this repo's own release notes
License
AGPL-3.0-or-later.
Credit
The Copilot CLI budget/runway/session-break logic in this package is a direct port of the original token-finops-cli by tronicum (oh-my-agent-code/token-finops-cli), kept verbatim where possible. Everything else in this repository builds on that foundation.
Metadata
Release files for token-finops-cli 0.4.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| token_finops_cli-0.4.0.tar.gz | 229.4 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| token_finops_cli-0.4.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 382.5 kB
Release files / token_finops_cli-0.4.0.tar.gz
| Download URL | token_finops_cli-0.4.0.tar.gz |
|---|---|
| Size | 229.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
b04573a362668bf1e6dc5bc89aee8cd65ce71d37d42dc548f3a7c3a083ef4904
|
|
BLAKE2b-256 checksum How to use checksums |
2d79392c788d5923dbd85a7489ea8aeedfa71d258859a3c85da216aa61fff0fb
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 12, 2026.
Transparency logRelease files / token_finops_cli-0.4.0-py3-none-any.whl
| Download URL | token_finops_cli-0.4.0-py3-none-any.whl |
|---|---|
| Size | 153.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
ce073ea77ad386390c4d7e85b7ae3e602a95cc75173f4058882e535f36d9dc5e
|
|
BLAKE2b-256 checksum How to use checksums |
21c5776e22ccecd2b9f0fae6232a186234b7993caf85d0839c89ca97e40fa2b7
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 12, 2026.
Transparency log