token-finops-cli 0.2.0__tar.gz → 0.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. token_finops_cli-0.3.0/PKG-INFO +267 -0
  2. token_finops_cli-0.3.0/README.md +244 -0
  3. token_finops_cli-0.3.0/examples/generate_synthetic_db.py +40 -0
  4. token_finops_cli-0.3.0/pyproject.toml +53 -0
  5. token_finops_cli-0.3.0/src/token_finops_cli/__init__.py +7 -0
  6. token_finops_cli-0.3.0/src/token_finops_cli/__main__.py +3 -0
  7. token_finops_cli-0.3.0/src/token_finops_cli/adapters/__init__.py +22 -0
  8. token_finops_cli-0.3.0/src/token_finops_cli/adapters/aider.py +338 -0
  9. token_finops_cli-0.3.0/src/token_finops_cli/adapters/base.py +53 -0
  10. token_finops_cli-0.3.0/src/token_finops_cli/adapters/claude_code.py +412 -0
  11. token_finops_cli-0.3.0/src/token_finops_cli/adapters/cline.py +416 -0
  12. token_finops_cli-0.3.0/src/token_finops_cli/adapters/codex.py +423 -0
  13. token_finops_cli-0.3.0/src/token_finops_cli/adapters/continue_dev.py +290 -0
  14. token_finops_cli-0.3.0/src/token_finops_cli/adapters/copilot.py +212 -0
  15. token_finops_cli-0.3.0/src/token_finops_cli/adapters/gemini_cli.py +368 -0
  16. token_finops_cli-0.3.0/src/token_finops_cli/adapters/hermes.py +520 -0
  17. token_finops_cli-0.3.0/src/token_finops_cli/adapters/opencode.py +445 -0
  18. token_finops_cli-0.3.0/src/token_finops_cli/burn/__init__.py +440 -0
  19. token_finops_cli-0.3.0/src/token_finops_cli/burn/history.py +168 -0
  20. token_finops_cli-0.3.0/src/token_finops_cli/burn/plans.json +31 -0
  21. token_finops_cli-0.3.0/src/token_finops_cli/cli.py +527 -0
  22. token_finops_cli-0.3.0/src/token_finops_cli/core/__init__.py +1 -0
  23. token_finops_cli-0.3.0/src/token_finops_cli/core/model.py +142 -0
  24. token_finops_cli-0.3.0/src/token_finops_cli/core/pricing.py +167 -0
  25. token_finops_cli-0.3.0/src/token_finops_cli/core/runway.py +262 -0
  26. token_finops_cli-0.3.0/src/token_finops_cli/cost_per_token.py +172 -0
  27. token_finops_cli-0.3.0/src/token_finops_cli/report.py +184 -0
  28. token_finops_cli-0.3.0/src/token_finops_cli/savings/__init__.py +258 -0
  29. token_finops_cli-0.3.0/src/token_finops_cli/savings/energy.json +21 -0
  30. token_finops_cli-0.3.0/src/token_finops_cli/savings/hardware_profiles.json +67 -0
  31. token_finops_cli-0.3.0/src/token_finops_cli/status.py +243 -0
  32. token_finops_cli-0.3.0/src/token_finops_cli/synth/__init__.py +161 -0
  33. token_finops_cli-0.3.0/src/token_finops_cli/synth/env.py +52 -0
  34. token_finops_cli-0.3.0/src/token_finops_cli/synth/scenarios.py +109 -0
  35. token_finops_cli-0.3.0/tests/conftest.py +181 -0
  36. token_finops_cli-0.3.0/tests/test_aider.py +75 -0
  37. token_finops_cli-0.3.0/tests/test_burn.py +350 -0
  38. token_finops_cli-0.3.0/tests/test_burn_history.py +338 -0
  39. token_finops_cli-0.3.0/tests/test_claude_code.py +147 -0
  40. {token_finops_cli-0.2.0 → token_finops_cli-0.3.0}/tests/test_cli.py +11 -0
  41. token_finops_cli-0.3.0/tests/test_cli_and_savings.py +642 -0
  42. token_finops_cli-0.3.0/tests/test_cli_smoke_workflow.py +153 -0
  43. token_finops_cli-0.3.0/tests/test_cline.py +89 -0
  44. token_finops_cli-0.3.0/tests/test_codex.py +111 -0
  45. token_finops_cli-0.3.0/tests/test_continue_dev.py +60 -0
  46. token_finops_cli-0.3.0/tests/test_copilot.py +122 -0
  47. token_finops_cli-0.3.0/tests/test_core.py +92 -0
  48. token_finops_cli-0.3.0/tests/test_cost_per_token.py +129 -0
  49. token_finops_cli-0.3.0/tests/test_e2e_matrix.py +338 -0
  50. token_finops_cli-0.3.0/tests/test_gemini_cli.py +92 -0
  51. token_finops_cli-0.3.0/tests/test_hermes.py +52 -0
  52. token_finops_cli-0.3.0/tests/test_opencode.py +73 -0
  53. token_finops_cli-0.3.0/tests/test_pricing.py +224 -0
  54. token_finops_cli-0.3.0/tests/test_report.py +284 -0
  55. token_finops_cli-0.3.0/tests/test_robustness.py +673 -0
  56. token_finops_cli-0.3.0/tests/test_runway_windows.py +485 -0
  57. token_finops_cli-0.3.0/tests/test_self_audit.py +245 -0
  58. token_finops_cli-0.3.0/tests/test_status.py +107 -0
  59. token_finops_cli-0.3.0/tests/test_statusline.py +264 -0
  60. token_finops_cli-0.3.0/tests/test_synth.py +143 -0
  61. token_finops_cli-0.3.0/tests/test_usecases.py +1051 -0
  62. token_finops_cli-0.2.0/PKG-INFO +0 -166
  63. token_finops_cli-0.2.0/README.md +0 -144
  64. token_finops_cli-0.2.0/examples/generate_synthetic_db.py +0 -24
  65. token_finops_cli-0.2.0/pyproject.toml +0 -44
  66. token_finops_cli-0.2.0/src/token_finops_cli/__init__.py +0 -6
  67. {token_finops_cli-0.2.0 → token_finops_cli-0.3.0}/.github/workflows/ci.yml +0 -0
  68. {token_finops_cli-0.2.0 → token_finops_cli-0.3.0}/.github/workflows/release.yml +0 -0
  69. {token_finops_cli-0.2.0 → token_finops_cli-0.3.0}/.gitignore +0 -0
  70. {token_finops_cli-0.2.0 → token_finops_cli-0.3.0}/LICENSE +0 -0
  71. /token_finops_cli-0.2.0/src/token_finops_cli/cli.py → /token_finops_cli-0.3.0/src/token_finops_cli/copilot_legacy.py +0 -0
  72. {token_finops_cli-0.2.0 → token_finops_cli-0.3.0}/src/token_finops_cli/examples_helper.py +0 -0
@@ -0,0 +1,267 @@
1
+ Metadata-Version: 2.5
2
+ Name: token-finops-cli
3
+ Version: 0.3.0
4
+ Summary: Read-only, local-first token usage and budget-runway tracker for AI coding agents (Copilot CLI, Claude Code, Codex CLI, Gemini CLI, Hermes Agent, ...) plus a local-vs-cloud savings estimator
5
+ Project-URL: Homepage, https://github.com/tronicum/burn-token-burn
6
+ Project-URL: Origin, https://github.com/oh-my-agent-code/token-finops-cli
7
+ Project-URL: Issues, https://github.com/tronicum/burn-token-burn/issues
8
+ Author: tronicum
9
+ License-Expression: AGPL-3.0-or-later
10
+ License-File: LICENSE
11
+ Keywords: claude-code,cli,codex,copilot-cli,finops,gemini-cli,github-copilot,hermes-agent,local-llm,token-usage
12
+ Classifier: Environment :: Console
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: GNU Affero General Public License v3 or later (AGPLv3+)
15
+ Classifier: Operating System :: OS Independent
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Topic :: Utilities
18
+ Requires-Python: >=3.10
19
+ Provides-Extra: dev
20
+ Requires-Dist: pytest>=8.0; extra == 'dev'
21
+ Requires-Dist: ruff>=0.6; extra == 'dev'
22
+ Description-Content-Type: text/markdown
23
+
24
+ # token-finops-cli
25
+
26
+ Read-only, local-first token usage and budget-runway tracker for AI coding
27
+ agents. **v0.3.0** grew this out of the original 0.2.x line — a single
28
+ 600-line script that read GitHub Copilot CLI's local telemetry — into a
29
+ modular tool with **9 adapters** (Copilot, Claude Code, Codex CLI, Gemini
30
+ CLI, Hermes Agent, OpenCode/Kilo, Cline/Roo/Kilo, Aider, Continue.dev), a
31
+ shared runway engine, a self-audit for Claude Code sessions, and a
32
+ local-vs-cloud savings estimator. The philosophy hasn't changed: standard
33
+ library only, no runtime dependencies, and it never writes to any tool's
34
+ own data store — SQLite is opened read-only/immutable where applicable.
35
+
36
+ Everything below that talks about the original Copilot budget/cycle/session
37
+ reports still works exactly as it did in 0.2.x.
38
+
39
+ ## Install
40
+
41
+ ```bash
42
+ uv tool install token-finops-cli # recommended
43
+ pipx install token-finops-cli
44
+ pip install token-finops-cli
45
+ ```
46
+
47
+ ## 60-second tour
48
+
49
+ ```bash
50
+ $ token-finops adapters
51
+ tool found root
52
+ copilot yes ~/.copilot
53
+ claude_code yes ~/.claude
54
+ codex no ~/.codex
55
+ gemini_cli no ~/.gemini
56
+ hermes no ~/.hermes
57
+ opencode no ~/.local/share/opencode
58
+ cline no ~/.config/Code/User/globalStorage
59
+ aider no ~
60
+ continue no ~/.continue
61
+ ```
62
+
63
+ ```bash
64
+ $ token-finops report --compact
65
+ GitHub Copilot CLI [######--------------] 29.4% runway 41.2d OK
66
+ Claude Code [############--------] 61.0% runway 1.9h WARN
67
+
68
+ binding constraint: Claude Code (5h) — runway 1.9h -> WARN
69
+ ```
70
+
71
+ ```bash
72
+ $ token-finops sessions --tool claude_code --since 30d
73
+ Claude Code sessions (last 30d):
74
+ session_id started calls tokens API-eq $
75
+ 0bbc8283-… 2026-09-05 09:12 428 73.1M 87.28
76
+
77
+ ```
78
+
79
+ ```bash
80
+ $ token-finops self-audit
81
+ Self-audit: Claude Code session 0bbc8283-…
82
+ API calls (deduplicated): 428 = main loop 211 + sub-agents 217
83
+ total tokens: 73.1M (95 % of them cache reads)
84
+ API-equivalent: $87.28
85
+
86
+ By model:
87
+ claude-fable-5-1 255 calls $73.25 84%
88
+ claude-sonnet-5 172 calls $14.03 16%
89
+
90
+ Sub-agents = 30 % of API-equivalent cost
91
+ ```
92
+
93
+ ```bash
94
+ $ token-finops savings --hardware mac-studio-m4-max-128gb --model qwen3-32b --utilization 0.2
95
+ local total: $8.86 / 1M tok (capex-dominated at 20 % utilisation)
96
+ cloud (sonnet): $3.20 / 1M tok -> CLOUD CHEAPER
97
+ break-even utilisation: 60 % of 24/7
98
+ ```
99
+
100
+ ```bash
101
+ $ token-finops break-even --hardware mac-studio-m4-max-128gb
102
+ replaceable cloud usage (haiku/sonnet-class): 35.5M tokens = $14.94 API-equivalent
103
+ local alternative: energy $14.29 + capex $116.87 = $131.15
104
+ -> cloud still cheaper by $116.21; at the current pace the box never pays off
105
+ ```
106
+
107
+ ```bash
108
+ $ token-finops synth --out /tmp/demo-home --print-env && \
109
+ eval "$(token-finops synth --out /tmp/demo-home --print-env)" && \
110
+ token-finops report --compact
111
+ ```
112
+
113
+ runs the whole CLI against a fully synthetic, throwaway fake-home tree — no
114
+ real telemetry anywhere. See `docs/SYNTH.md` in the repo root for scenarios
115
+ (`steady`, `burst`, `exhausted`, `weekend`, `fresh`, `quiet`,
116
+ `subagent-heavy`).
117
+
118
+ ## GitHub Copilot CLI (the original tool)
119
+
120
+ `token-finops-cli` still reads `~/.copilot/session-store.db`
121
+ (`assistant_usage_events`) directly — no external API calls, nothing
122
+ leaves your machine, the DB is opened read-only.
123
+
124
+ ```bash
125
+ token-finops report --tool copilot # 7-day summary + runway (default window)
126
+ token-finops report --tool copilot --budget 1500 --cycle-day 1 # your plan's monthly AI units
127
+ token-finops report --tool copilot --compact # 2-line minimal output
128
+ token-finops report --tool copilot --watch 5 # live-refreshing view every 5s
129
+ ```
130
+
131
+ Session history and break/gap reports (per-session archival view, distinct
132
+ from the runway report):
133
+
134
+ ```bash
135
+ token-finops sessions # list all past Copilot sessions
136
+ token-finops sessions --since 30d --limit 10
137
+ token-finops sessions --session <id> # detailed break/gap report
138
+ token-finops sessions --session <id> --gap-minutes 60
139
+ token-finops sessions --totals # ONE combined report across ALL sessions
140
+ ```
141
+
142
+ The detailed per-session view reports "breaks" — gaps between requests
143
+ longer than `--gap-minutes` (default 30) — and active vs. idle wall-clock
144
+ time. The "AI unit" is GitHub's own cost/usage unit (`total_nano_aiu / 1e9`)
145
+ — the same number shown on the Copilot billing page, not raw token counts.
146
+
147
+ ## Claude Code: status-line hook
148
+
149
+ Anthropic subscriptions expose only a percentage of a rolling 5h/7d window,
150
+ and only through the status line. Wire the collector as your status line
151
+ command; every redraw stores a snapshot, and two snapshots in the same
152
+ window are enough for a burn estimate:
153
+
154
+ ```jsonc
155
+ // ~/.claude/settings.json
156
+ { "statusLine": { "type": "command", "command": "token-finops collect-statusline" } }
157
+ ```
158
+
159
+ ```bash
160
+ token-finops report --tool claude_code --json
161
+ ```
162
+
163
+ ```
164
+ Claude Code — window: 5h (resets 2026-09-05 12:29 UTC)
165
+ budget: [############------------------] 40.0%
166
+ window: [############------------------] 40.0% elapsed
167
+ pace: 1.00 (on pace)
168
+ burn: 20.0% per hour (from snapshots)
169
+ runway: 3.0h vs 3.0h left -> OK
170
+ ```
171
+
172
+ ## Status bars: tmux, starship, waybar, SwiftBar
173
+
174
+ ```bash
175
+ token-finops status # CC 61% 2h! | CX 17% 3h | CP 29% 41d | binds: CC
176
+ token-finops status --format tmux # for status-right (contrib/tmux/token-finops.tmux)
177
+ token-finops status --format starship # binding constraint only
178
+ token-finops status --format waybar # JSON; also polybar, i3, xbar, json
179
+ ```
180
+
181
+ `status` serves a cache (`~/.token-finops/last.json`) and rescans only with `--fresh` or when
182
+ the cache is older than `--max-age` seconds. Refresh it from one timer (`contrib/refresh/`) and let
183
+ every widget poll freely. Details: `../docs/TMUX.md`.
184
+
185
+ ## Self-audit
186
+
187
+ Claude Code writes one line per content block, so naive counting
188
+ over-reports by roughly 1.9x. `self-audit` deduplicates on
189
+ `(message.id, requestId)`, includes sub-agent transcripts, and breaks the
190
+ bill down by model — the model, not the raw token count, is what decides
191
+ the cost.
192
+
193
+ ```bash
194
+ token-finops self-audit # latest session
195
+ token-finops self-audit --session <id-prefix>
196
+ token-finops self-audit --json # machine-readable, for CI/PR checklists
197
+ ```
198
+
199
+ ## Local vs. cloud: savings and break-even
200
+
201
+ ```bash
202
+ token-finops savings --list # available hardware/model/tariff keys
203
+ token-finops savings --hardware rtx-4090-workstation --model llama-3.3-70b
204
+ token-finops savings --power solar-de-feed-in --utilization 0.8
205
+ token-finops break-even --since 90d --replaceable-tiers haiku,sonnet
206
+ ```
207
+
208
+ Solar is priced at the feed-in tariff you forgo, not at zero. Capex is
209
+ amortised per hour of *actual* inference. Only Haiku/Sonnet-class work
210
+ counts as replaceable by a local model — a 32B model does not do
211
+ Opus/Fable-class work. Hardware and energy numbers live in editable JSON
212
+ (`savings/hardware_profiles.json`, `savings/energy.json`) with sources and
213
+ review dates — measure your own box and overwrite them.
214
+
215
+ ## Environment variables
216
+
217
+ Every adapter reads its data root from an override variable before falling
218
+ back to the tool's real default location. None of these are required —
219
+ they exist so you can point the CLI at a different install, a synthetic
220
+ fixture tree, or a non-default `$HOME`.
221
+
222
+ | Variable | Overrides | Default |
223
+ |---|---|---|
224
+ | `TOKEN_FINOPS_COPILOT_DB` | Copilot session-store DB path | `~/.copilot/session-store.db` |
225
+ | `TOKEN_FINOPS_DB` | (legacy alias, `sessions`/original CLI) Copilot DB path | `~/.copilot/session-store.db` |
226
+ | `CLAUDE_CONFIG_DIR` | Claude Code config root (`self-audit --config-dir` also sets this) | `~/.claude` |
227
+ | `CODEX_HOME` | OpenAI Codex CLI home | `~/.codex` |
228
+ | `GEMINI_CLI_HOME` | Gemini CLI home | `~/.gemini` |
229
+ | `HERMES_HOME` | Hermes Agent home | `~/.hermes` |
230
+ | `CLINE_CLI_HOME` | Cline CLI (non-VS Code) home | `~/.cline` |
231
+ | `TOKEN_FINOPS_CLINE_DIRS` | Cline/Roo/Kilo VS Code `globalStorage` roots | per-OS VS Code data dir |
232
+ | `TOKEN_FINOPS_OPENCODE_DB` | OpenCode/Kilo CLI sqlite DB path | `$XDG_DATA_HOME/opencode/opencode.db` |
233
+ | `XDG_DATA_HOME` | Base for the OpenCode DB default above | `~/.local/share` |
234
+ | `TOKEN_FINOPS_AIDER_DIRS` | Aider chat-history search roots | `~` |
235
+ | `TOKEN_FINOPS_AIDER_ANALYTICS` | Aider opt-in analytics JSONL path | `~/.aider/analytics.jsonl` |
236
+ | `CONTINUE_GLOBAL_DIR` | Continue.dev sessions root | `~/.continue` |
237
+ | `TOKEN_FINOPS_HARDWARE_JSON` | Override the `savings` hardware-profiles JSON | packaged `hardware_profiles.json` |
238
+ | `TOKEN_FINOPS_ENERGY_JSON` | Override the `savings` energy/tariff JSON | packaged `energy.json` |
239
+
240
+ ## Documentation and design rules
241
+
242
+ This package is one component of the `token-finops` repository. For the
243
+ full guide, the read-only/fractions-not-dollars design rules, the
244
+ per-assistant Architecture Decision Records, and the 50 executable use
245
+ cases, see the repository root:
246
+
247
+ - [`../README.md`](../README.md) — project overview, design rules, related tools
248
+ - [`../docs/PLAN.md`](../docs/PLAN.md) — structured plan and status
249
+ - [`../docs/ADAPTERS.md`](../docs/ADAPTERS.md) — how to add an adapter
250
+ - [`../docs/adr/`](../docs/adr/) — one ADR per coding assistant
251
+ - [`../docs/SYNTH.md`](../docs/SYNTH.md) — synthetic telemetry generator
252
+ - [`../docs/USECASES.md`](../docs/USECASES.md) — every executable use case
253
+ - [`../docs/TMUX.md`](../docs/TMUX.md) — `status --format …` for tmux, starship, waybar, polybar, i3, SwiftBar
254
+ - [`../docs/INTEGRATIONS.md`](../docs/INTEGRATIONS.md) — editor / agent-native / desktop integrations (design doc)
255
+ - [`../CHANGELOG.md`](../CHANGELOG.md), [`../CONTRIBUTING.md`](../CONTRIBUTING.md)
256
+
257
+ ## License
258
+
259
+ AGPL-3.0-or-later.
260
+
261
+ ## Credit
262
+
263
+ The Copilot CLI budget/runway/session-break logic in this package is a
264
+ direct port of the original **token-finops-cli** by **tronicum**
265
+ ([oh-my-agent-code/token-finops-cli](https://github.com/oh-my-agent-code/token-finops-cli)),
266
+ kept verbatim where possible. Everything else in this repository builds on
267
+ that foundation.
@@ -0,0 +1,244 @@
1
+ # token-finops-cli
2
+
3
+ Read-only, local-first token usage and budget-runway tracker for AI coding
4
+ agents. **v0.3.0** grew this out of the original 0.2.x line — a single
5
+ 600-line script that read GitHub Copilot CLI's local telemetry — into a
6
+ modular tool with **9 adapters** (Copilot, Claude Code, Codex CLI, Gemini
7
+ CLI, Hermes Agent, OpenCode/Kilo, Cline/Roo/Kilo, Aider, Continue.dev), a
8
+ shared runway engine, a self-audit for Claude Code sessions, and a
9
+ local-vs-cloud savings estimator. The philosophy hasn't changed: standard
10
+ library only, no runtime dependencies, and it never writes to any tool's
11
+ own data store — SQLite is opened read-only/immutable where applicable.
12
+
13
+ Everything below that talks about the original Copilot budget/cycle/session
14
+ reports still works exactly as it did in 0.2.x.
15
+
16
+ ## Install
17
+
18
+ ```bash
19
+ uv tool install token-finops-cli # recommended
20
+ pipx install token-finops-cli
21
+ pip install token-finops-cli
22
+ ```
23
+
24
+ ## 60-second tour
25
+
26
+ ```bash
27
+ $ token-finops adapters
28
+ tool found root
29
+ copilot yes ~/.copilot
30
+ claude_code yes ~/.claude
31
+ codex no ~/.codex
32
+ gemini_cli no ~/.gemini
33
+ hermes no ~/.hermes
34
+ opencode no ~/.local/share/opencode
35
+ cline no ~/.config/Code/User/globalStorage
36
+ aider no ~
37
+ continue no ~/.continue
38
+ ```
39
+
40
+ ```bash
41
+ $ token-finops report --compact
42
+ GitHub Copilot CLI [######--------------] 29.4% runway 41.2d OK
43
+ Claude Code [############--------] 61.0% runway 1.9h WARN
44
+
45
+ binding constraint: Claude Code (5h) — runway 1.9h -> WARN
46
+ ```
47
+
48
+ ```bash
49
+ $ token-finops sessions --tool claude_code --since 30d
50
+ Claude Code sessions (last 30d):
51
+ session_id started calls tokens API-eq $
52
+ 0bbc8283-… 2026-09-05 09:12 428 73.1M 87.28
53
+
54
+ ```
55
+
56
+ ```bash
57
+ $ token-finops self-audit
58
+ Self-audit: Claude Code session 0bbc8283-…
59
+ API calls (deduplicated): 428 = main loop 211 + sub-agents 217
60
+ total tokens: 73.1M (95 % of them cache reads)
61
+ API-equivalent: $87.28
62
+
63
+ By model:
64
+ claude-fable-5-1 255 calls $73.25 84%
65
+ claude-sonnet-5 172 calls $14.03 16%
66
+
67
+ Sub-agents = 30 % of API-equivalent cost
68
+ ```
69
+
70
+ ```bash
71
+ $ token-finops savings --hardware mac-studio-m4-max-128gb --model qwen3-32b --utilization 0.2
72
+ local total: $8.86 / 1M tok (capex-dominated at 20 % utilisation)
73
+ cloud (sonnet): $3.20 / 1M tok -> CLOUD CHEAPER
74
+ break-even utilisation: 60 % of 24/7
75
+ ```
76
+
77
+ ```bash
78
+ $ token-finops break-even --hardware mac-studio-m4-max-128gb
79
+ replaceable cloud usage (haiku/sonnet-class): 35.5M tokens = $14.94 API-equivalent
80
+ local alternative: energy $14.29 + capex $116.87 = $131.15
81
+ -> cloud still cheaper by $116.21; at the current pace the box never pays off
82
+ ```
83
+
84
+ ```bash
85
+ $ token-finops synth --out /tmp/demo-home --print-env && \
86
+ eval "$(token-finops synth --out /tmp/demo-home --print-env)" && \
87
+ token-finops report --compact
88
+ ```
89
+
90
+ runs the whole CLI against a fully synthetic, throwaway fake-home tree — no
91
+ real telemetry anywhere. See `docs/SYNTH.md` in the repo root for scenarios
92
+ (`steady`, `burst`, `exhausted`, `weekend`, `fresh`, `quiet`,
93
+ `subagent-heavy`).
94
+
95
+ ## GitHub Copilot CLI (the original tool)
96
+
97
+ `token-finops-cli` still reads `~/.copilot/session-store.db`
98
+ (`assistant_usage_events`) directly — no external API calls, nothing
99
+ leaves your machine, the DB is opened read-only.
100
+
101
+ ```bash
102
+ token-finops report --tool copilot # 7-day summary + runway (default window)
103
+ token-finops report --tool copilot --budget 1500 --cycle-day 1 # your plan's monthly AI units
104
+ token-finops report --tool copilot --compact # 2-line minimal output
105
+ token-finops report --tool copilot --watch 5 # live-refreshing view every 5s
106
+ ```
107
+
108
+ Session history and break/gap reports (per-session archival view, distinct
109
+ from the runway report):
110
+
111
+ ```bash
112
+ token-finops sessions # list all past Copilot sessions
113
+ token-finops sessions --since 30d --limit 10
114
+ token-finops sessions --session <id> # detailed break/gap report
115
+ token-finops sessions --session <id> --gap-minutes 60
116
+ token-finops sessions --totals # ONE combined report across ALL sessions
117
+ ```
118
+
119
+ The detailed per-session view reports "breaks" — gaps between requests
120
+ longer than `--gap-minutes` (default 30) — and active vs. idle wall-clock
121
+ time. The "AI unit" is GitHub's own cost/usage unit (`total_nano_aiu / 1e9`)
122
+ — the same number shown on the Copilot billing page, not raw token counts.
123
+
124
+ ## Claude Code: status-line hook
125
+
126
+ Anthropic subscriptions expose only a percentage of a rolling 5h/7d window,
127
+ and only through the status line. Wire the collector as your status line
128
+ command; every redraw stores a snapshot, and two snapshots in the same
129
+ window are enough for a burn estimate:
130
+
131
+ ```jsonc
132
+ // ~/.claude/settings.json
133
+ { "statusLine": { "type": "command", "command": "token-finops collect-statusline" } }
134
+ ```
135
+
136
+ ```bash
137
+ token-finops report --tool claude_code --json
138
+ ```
139
+
140
+ ```
141
+ Claude Code — window: 5h (resets 2026-09-05 12:29 UTC)
142
+ budget: [############------------------] 40.0%
143
+ window: [############------------------] 40.0% elapsed
144
+ pace: 1.00 (on pace)
145
+ burn: 20.0% per hour (from snapshots)
146
+ runway: 3.0h vs 3.0h left -> OK
147
+ ```
148
+
149
+ ## Status bars: tmux, starship, waybar, SwiftBar
150
+
151
+ ```bash
152
+ token-finops status # CC 61% 2h! | CX 17% 3h | CP 29% 41d | binds: CC
153
+ token-finops status --format tmux # for status-right (contrib/tmux/token-finops.tmux)
154
+ token-finops status --format starship # binding constraint only
155
+ token-finops status --format waybar # JSON; also polybar, i3, xbar, json
156
+ ```
157
+
158
+ `status` serves a cache (`~/.token-finops/last.json`) and rescans only with `--fresh` or when
159
+ the cache is older than `--max-age` seconds. Refresh it from one timer (`contrib/refresh/`) and let
160
+ every widget poll freely. Details: `../docs/TMUX.md`.
161
+
162
+ ## Self-audit
163
+
164
+ Claude Code writes one line per content block, so naive counting
165
+ over-reports by roughly 1.9x. `self-audit` deduplicates on
166
+ `(message.id, requestId)`, includes sub-agent transcripts, and breaks the
167
+ bill down by model — the model, not the raw token count, is what decides
168
+ the cost.
169
+
170
+ ```bash
171
+ token-finops self-audit # latest session
172
+ token-finops self-audit --session <id-prefix>
173
+ token-finops self-audit --json # machine-readable, for CI/PR checklists
174
+ ```
175
+
176
+ ## Local vs. cloud: savings and break-even
177
+
178
+ ```bash
179
+ token-finops savings --list # available hardware/model/tariff keys
180
+ token-finops savings --hardware rtx-4090-workstation --model llama-3.3-70b
181
+ token-finops savings --power solar-de-feed-in --utilization 0.8
182
+ token-finops break-even --since 90d --replaceable-tiers haiku,sonnet
183
+ ```
184
+
185
+ Solar is priced at the feed-in tariff you forgo, not at zero. Capex is
186
+ amortised per hour of *actual* inference. Only Haiku/Sonnet-class work
187
+ counts as replaceable by a local model — a 32B model does not do
188
+ Opus/Fable-class work. Hardware and energy numbers live in editable JSON
189
+ (`savings/hardware_profiles.json`, `savings/energy.json`) with sources and
190
+ review dates — measure your own box and overwrite them.
191
+
192
+ ## Environment variables
193
+
194
+ Every adapter reads its data root from an override variable before falling
195
+ back to the tool's real default location. None of these are required —
196
+ they exist so you can point the CLI at a different install, a synthetic
197
+ fixture tree, or a non-default `$HOME`.
198
+
199
+ | Variable | Overrides | Default |
200
+ |---|---|---|
201
+ | `TOKEN_FINOPS_COPILOT_DB` | Copilot session-store DB path | `~/.copilot/session-store.db` |
202
+ | `TOKEN_FINOPS_DB` | (legacy alias, `sessions`/original CLI) Copilot DB path | `~/.copilot/session-store.db` |
203
+ | `CLAUDE_CONFIG_DIR` | Claude Code config root (`self-audit --config-dir` also sets this) | `~/.claude` |
204
+ | `CODEX_HOME` | OpenAI Codex CLI home | `~/.codex` |
205
+ | `GEMINI_CLI_HOME` | Gemini CLI home | `~/.gemini` |
206
+ | `HERMES_HOME` | Hermes Agent home | `~/.hermes` |
207
+ | `CLINE_CLI_HOME` | Cline CLI (non-VS Code) home | `~/.cline` |
208
+ | `TOKEN_FINOPS_CLINE_DIRS` | Cline/Roo/Kilo VS Code `globalStorage` roots | per-OS VS Code data dir |
209
+ | `TOKEN_FINOPS_OPENCODE_DB` | OpenCode/Kilo CLI sqlite DB path | `$XDG_DATA_HOME/opencode/opencode.db` |
210
+ | `XDG_DATA_HOME` | Base for the OpenCode DB default above | `~/.local/share` |
211
+ | `TOKEN_FINOPS_AIDER_DIRS` | Aider chat-history search roots | `~` |
212
+ | `TOKEN_FINOPS_AIDER_ANALYTICS` | Aider opt-in analytics JSONL path | `~/.aider/analytics.jsonl` |
213
+ | `CONTINUE_GLOBAL_DIR` | Continue.dev sessions root | `~/.continue` |
214
+ | `TOKEN_FINOPS_HARDWARE_JSON` | Override the `savings` hardware-profiles JSON | packaged `hardware_profiles.json` |
215
+ | `TOKEN_FINOPS_ENERGY_JSON` | Override the `savings` energy/tariff JSON | packaged `energy.json` |
216
+
217
+ ## Documentation and design rules
218
+
219
+ This package is one component of the `token-finops` repository. For the
220
+ full guide, the read-only/fractions-not-dollars design rules, the
221
+ per-assistant Architecture Decision Records, and the 50 executable use
222
+ cases, see the repository root:
223
+
224
+ - [`../README.md`](../README.md) — project overview, design rules, related tools
225
+ - [`../docs/PLAN.md`](../docs/PLAN.md) — structured plan and status
226
+ - [`../docs/ADAPTERS.md`](../docs/ADAPTERS.md) — how to add an adapter
227
+ - [`../docs/adr/`](../docs/adr/) — one ADR per coding assistant
228
+ - [`../docs/SYNTH.md`](../docs/SYNTH.md) — synthetic telemetry generator
229
+ - [`../docs/USECASES.md`](../docs/USECASES.md) — every executable use case
230
+ - [`../docs/TMUX.md`](../docs/TMUX.md) — `status --format …` for tmux, starship, waybar, polybar, i3, SwiftBar
231
+ - [`../docs/INTEGRATIONS.md`](../docs/INTEGRATIONS.md) — editor / agent-native / desktop integrations (design doc)
232
+ - [`../CHANGELOG.md`](../CHANGELOG.md), [`../CONTRIBUTING.md`](../CONTRIBUTING.md)
233
+
234
+ ## License
235
+
236
+ AGPL-3.0-or-later.
237
+
238
+ ## Credit
239
+
240
+ The Copilot CLI budget/runway/session-break logic in this package is a
241
+ direct port of the original **token-finops-cli** by **tronicum**
242
+ ([oh-my-agent-code/token-finops-cli](https://github.com/oh-my-agent-code/token-finops-cli)),
243
+ kept verbatim where possible. Everything else in this repository builds on
244
+ that foundation.
@@ -0,0 +1,40 @@
1
+ """Generate a synthetic fake-home tree for demos/tests.
2
+
3
+ Thin CLI wrapper around `token_finops_cli.synth.generate` -- see that module
4
+ (and `docs/SYNTH.md`) for what gets written per tool and what the scenarios
5
+ mean. Equivalent to `token-finops synth`.
6
+ """
7
+ import argparse
8
+
9
+ from token_finops_cli.synth import ALL_TOOLS, generate
10
+ from token_finops_cli.synth.env import env_for
11
+ from token_finops_cli.synth.scenarios import KNOWN as SCENARIOS
12
+
13
+
14
+ def main():
15
+ parser = argparse.ArgumentParser(description=__doc__,
16
+ formatter_class=argparse.RawDescriptionHelpFormatter)
17
+ parser.add_argument("out", help="Output directory for the synthetic fake-home tree")
18
+ parser.add_argument("--tools", action="append", default=None,
19
+ help=f"comma-separated tool(s) to generate (default: all). known: {', '.join(ALL_TOOLS)}")
20
+ parser.add_argument("--days", type=int, default=14)
21
+ parser.add_argument("--scenario", choices=SCENARIOS, default="steady")
22
+ parser.add_argument("--seed", type=int, default=42)
23
+ parser.add_argument("--print-env", action="store_true",
24
+ help="also print `export VAR=...` lines")
25
+ args = parser.parse_args()
26
+
27
+ tools = None
28
+ if args.tools:
29
+ tools = [t.strip() for chunk in args.tools for t in chunk.split(",") if t.strip()]
30
+
31
+ manifest = generate(args.out, tools=tools, days=args.days, scenario=args.scenario, seed=args.seed)
32
+ for tool, info in manifest.items():
33
+ print(f"{tool}: {info['events']} events -> {info['path']}")
34
+ if args.print_env:
35
+ for var, val in env_for(args.out).items():
36
+ print(f"export {var}={val}")
37
+
38
+
39
+ if __name__ == "__main__":
40
+ main()
@@ -0,0 +1,53 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "token-finops-cli"
7
+ version = "0.3.0"
8
+ description = "Read-only, local-first token usage and budget-runway tracker for AI coding agents (Copilot CLI, Claude Code, Codex CLI, Gemini CLI, Hermes Agent, ...) plus a local-vs-cloud savings estimator"
9
+ readme = "README.md"
10
+ license = "AGPL-3.0-or-later"
11
+ requires-python = ">=3.10"
12
+ authors = [
13
+ { name = "tronicum" },
14
+ ]
15
+ keywords = ["github-copilot", "copilot-cli", "claude-code", "codex", "gemini-cli", "hermes-agent", "finops", "token-usage", "cli", "local-llm"]
16
+ classifiers = [
17
+ "Environment :: Console",
18
+ "Intended Audience :: Developers",
19
+ "License :: OSI Approved :: GNU Affero General Public License v3 or later (AGPLv3+)",
20
+ "Operating System :: OS Independent",
21
+ "Programming Language :: Python :: 3",
22
+ "Topic :: Utilities",
23
+ ]
24
+ dependencies = []
25
+
26
+ [project.optional-dependencies]
27
+ dev = [
28
+ "pytest>=8.0",
29
+ "ruff>=0.6",
30
+ ]
31
+
32
+ [project.urls]
33
+ Homepage = "https://github.com/tronicum/burn-token-burn"
34
+ Origin = "https://github.com/oh-my-agent-code/token-finops-cli"
35
+ Issues = "https://github.com/tronicum/burn-token-burn/issues"
36
+
37
+ [project.scripts]
38
+ token-finops = "token_finops_cli:main"
39
+
40
+ [tool.hatch.build.targets.wheel]
41
+ packages = ["src/token_finops_cli"]
42
+
43
+ [tool.ruff]
44
+ line-length = 110
45
+ target-version = "py310"
46
+
47
+ [tool.ruff.lint]
48
+ # pin the rule set explicitly: ruff 0.16 widened its defaults and broke CI
49
+ select = ["E4", "E7", "E9", "F"]
50
+
51
+ [tool.pytest.ini_options]
52
+ pythonpath = ["src"]
53
+ testpaths = ["tests"]
@@ -0,0 +1,7 @@
1
+ """token-finops-cli: read-only, local-first token usage and budget-runway tracker for
2
+ AI coding agents (Copilot CLI, Claude Code, Codex CLI, Gemini CLI, Hermes Agent, ...)."""
3
+
4
+ from .cli import main
5
+
6
+ __version__ = "0.3.0"
7
+ __all__ = ["main"]
@@ -0,0 +1,3 @@
1
+ from .cli import main
2
+
3
+ main()
@@ -0,0 +1,22 @@
1
+ """Per-tool adapters. Each adapter is read-only and exposes:
2
+
3
+ class XAdapter(BaseAdapter):
4
+ tool = "x"
5
+ def available(self) -> bool
6
+ def scan(self, since=None) -> Iterable[UsageEvent]
7
+ def quota(self) -> Optional[QuotaSnapshot]
8
+ def default_policy(self, allowance=None) -> BudgetPolicy
9
+
10
+ Adapters must never write to the tool's own data store. SQLite sources are
11
+ opened with `immutable=1` (no WAL/lock interaction with the live app).
12
+ """
13
+ from __future__ import annotations
14
+
15
+ from .base import BaseAdapter, registry, sqlite_readonly # noqa: F401
16
+
17
+
18
+ def all_adapters() -> list[BaseAdapter]:
19
+ # Import side effects register the adapters.
20
+ from . import aider, claude_code, cline, codex, continue_dev, copilot, gemini_cli, hermes, opencode # noqa: F401
21
+
22
+ return [cls() for cls in registry.values()]