claude-finops 0.1.0
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.
- package/LICENSE +21 -0
- package/README.md +361 -0
- package/bin/claude-finops.js +74 -0
- package/config/free_models.json +49 -0
- package/config/model_compare.json +13 -0
- package/config/pricing.json +232 -0
- package/config/settings.json +57 -0
- package/finops/__init__.py +0 -0
- package/finops/actions.py +671 -0
- package/finops/agents.py +296 -0
- package/finops/analytics.py +1579 -0
- package/finops/api.py +426 -0
- package/finops/classify.py +48 -0
- package/finops/cloud.py +336 -0
- package/finops/diagnose.py +1002 -0
- package/finops/etl.py +533 -0
- package/finops/paths.py +111 -0
- package/finops/playbook.py +229 -0
- package/finops/pricing.py +74 -0
- package/finops/procs.py +499 -0
- package/finops/report.py +169 -0
- package/package.json +52 -0
- package/run.cmd +4 -0
- package/run.py +210 -0
- package/run.sh +3 -0
- package/web/app.js +2923 -0
- package/web/charts.js +340 -0
- package/web/index.html +14 -0
- package/web/styles.css +387 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Mohit Raj Purohit
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
# Claude FinOps Command Center
|
|
2
|
+
|
|
3
|
+
A local, executive-and-developer FinOps dashboard for your own Claude usage, built
|
|
4
|
+
from the Claude Code transcripts already on this machine.
|
|
5
|
+
|
|
6
|
+
It answers, in a few clicks:
|
|
7
|
+
|
|
8
|
+
> **What I used → what it cost → why it cost that much → whether it was efficient →
|
|
9
|
+
> what is likely to happen next → and what I should change.**
|
|
10
|
+
|
|
11
|
+
Everything runs on `127.0.0.1` with the Python standard library. No dependencies, no
|
|
12
|
+
network calls, no data leaves the machine.
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npx claude-finops
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
That is the whole setup. It finds your transcripts, builds a local warehouse, and
|
|
19
|
+
opens the dashboard at <http://127.0.0.1:8787>. No account, no API key, no config
|
|
20
|
+
file to write first.
|
|
21
|
+
|
|
22
|
+

|
|
23
|
+
|
|
24
|
+
<sub>Screenshots are real output from a real warehouse; project names, session titles
|
|
25
|
+
and prompt text have been replaced with placeholders.</sub>
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## Try it in 60 seconds
|
|
30
|
+
|
|
31
|
+
**1. Run it.** Nothing to install first — `npx` fetches and runs it.
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
npx claude-finops
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
First run reads `~/.claude/projects` and builds the warehouse (roughly a minute for a
|
|
38
|
+
few hundred transcripts). Every run after that starts in about a second.
|
|
39
|
+
|
|
40
|
+
**2. Open <http://127.0.0.1:8787>.** You land on the executive overview above: spend,
|
|
41
|
+
tokens, burn rate, forecast, and a ranked list of what to fix first.
|
|
42
|
+
|
|
43
|
+
**3. Ask it what to do.** "Why so many tokens?" explains where your tokens actually
|
|
44
|
+
went and gives you a prompt you can paste straight into Claude Code to fix it.
|
|
45
|
+
|
|
46
|
+

|
|
47
|
+
|
|
48
|
+
**4. Stop when you are done.**
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
claude-finops --stop
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Want it permanently available?
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
npm install -g claude-finops # then `claude-finops` from anywhere
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## What you actually get
|
|
63
|
+
|
|
64
|
+
**Where the money goes, per project.** Every project ranked by cost, drilling down
|
|
65
|
+
Project → Session → Prompt.
|
|
66
|
+
|
|
67
|
+

|
|
68
|
+
|
|
69
|
+
**A grade, not just numbers.** A 0–100 FinOps scorecard across five dimensions, each
|
|
70
|
+
with the reasoning behind the score, so you know whether your usage is healthy.
|
|
71
|
+
|
|
72
|
+

|
|
73
|
+
|
|
74
|
+
**Waste you can act on.** Repeated prompts, abandoned sessions, context carried for
|
|
75
|
+
no reason — each with the estimated money attached.
|
|
76
|
+
|
|
77
|
+

|
|
78
|
+
|
|
79
|
+
**Whether the model you are on is the right one.** Per-model cost and efficiency,
|
|
80
|
+
plus a switch analysis that prices the same workload on a cheaper model.
|
|
81
|
+
|
|
82
|
+

|
|
83
|
+
|
|
84
|
+
**What next month looks like.** Forecast from your own history, against budgets you
|
|
85
|
+
set.
|
|
86
|
+
|
|
87
|
+

|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## Why it is easy to use
|
|
92
|
+
|
|
93
|
+
- **One command, zero configuration.** `npx claude-finops`. No API key, no sign-up, no
|
|
94
|
+
config file — it reads transcripts Claude Code already wrote.
|
|
95
|
+
- **No dependencies.** Pure Python standard library. Nothing to `pip install`, nothing
|
|
96
|
+
to build, no lockfile to resolve.
|
|
97
|
+
- **Nothing to learn.** Every screen states its own conclusion in plain English before
|
|
98
|
+
it shows you a chart, and every number carries a badge saying whether it is measured
|
|
99
|
+
or estimated.
|
|
100
|
+
- **Your data stays put.** It binds to `127.0.0.1` and makes no outbound calls. The
|
|
101
|
+
warehouse lives in `~/.claude-finops`, so upgrading or deleting the package never
|
|
102
|
+
touches it.
|
|
103
|
+
- **It tells you what to change**, not just what happened — usually with a prompt you
|
|
104
|
+
can paste into Claude Code.
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
## The accuracy contract
|
|
109
|
+
|
|
110
|
+
This is the part that matters most, so it is stated first. Every figure in the UI
|
|
111
|
+
carries one of four badges, and nothing is invented.
|
|
112
|
+
|
|
113
|
+
| Badge | Meaning |
|
|
114
|
+
|---|---|
|
|
115
|
+
| **Actual** | Read straight out of your transcripts: token counts, timestamps, models, effort, tool calls, file paths, session and project identity. |
|
|
116
|
+
| **Estimated** | Derived. **Every dollar figure is estimated**, because Claude Code transcripts contain token counts but no billed amount. Cost = tokens × the price table in `config/pricing.json`. |
|
|
117
|
+
| **Forecast** | Projected from your history. Assumes the recent pattern continues. |
|
|
118
|
+
| **Recommendation** | A modelled opportunity. Savings estimates hold token usage constant on the alternative and do **not** model output quality. |
|
|
119
|
+
|
|
120
|
+
### Deliberately not fabricated
|
|
121
|
+
|
|
122
|
+
These are simply not present in Claude Code transcripts, so the dashboard says so
|
|
123
|
+
rather than guessing:
|
|
124
|
+
|
|
125
|
+
- Plan tier, allowance, and remaining credits
|
|
126
|
+
- Message / request allowances
|
|
127
|
+
- Billed invoice amounts
|
|
128
|
+
- Assistant response text (only usage metadata is extracted)
|
|
129
|
+
- Lines changed, commits, pull requests, bugs fixed
|
|
130
|
+
|
|
131
|
+
Wherever one of these would appear, you get **"Unavailable from connected Claude
|
|
132
|
+
data"**. Several of them can be *declared by you* in `config/settings.json` — do that
|
|
133
|
+
and the usage-vs-limit, days-until-limit, and limit-date projections light up, clearly
|
|
134
|
+
labelled as your own configured figures.
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
## What is in it
|
|
139
|
+
|
|
140
|
+
**Command center** — Executive overview (spend, tokens, usage %, remaining, forecast),
|
|
141
|
+
an AI FinOps Advisor that answers "what should I do today?" from live data, and a
|
|
142
|
+
0–100 FinOps scorecard with per-dimension reasoning.
|
|
143
|
+
|
|
144
|
+
**Usage** — Interactive timeline across 10 metrics and 6 time ranges with day
|
|
145
|
+
drill-down; burn rate and limits with a gauge, days-until-limit and projected overage;
|
|
146
|
+
per-model FinOps table with superlatives (most expensive, most used, most
|
|
147
|
+
token-efficient, best cost-per-output); context-size distribution and cache
|
|
148
|
+
with-vs-without analysis.
|
|
149
|
+
|
|
150
|
+
**Drill-down** — Project → Session → Prompt, everywhere. A prompt explorer over every
|
|
151
|
+
prompt with full text, category, tokens, cache split, tool calls, files touched,
|
|
152
|
+
latency and efficiency; five leaderboards; prompt intelligence (spend by activity);
|
|
153
|
+
and a Claude Code view (tools, files, branches, cost per repository).
|
|
154
|
+
|
|
155
|
+
**Optimize** — A waste detector with seven rules, each showing the exact prompts or
|
|
156
|
+
sessions it flagged; a recommendation engine that stays quiet without evidence; and
|
|
157
|
+
anomaly detection you can click through to the underlying sessions.
|
|
158
|
+
|
|
159
|
+
**Plan** — Forecast with conservative/expected/high scenario bands, and budgets with
|
|
160
|
+
Budget → Actual → Forecast → Variance and configurable alert thresholds.
|
|
161
|
+
|
|
162
|
+
Global search spans prompt text, sessions, projects, models, tools and dates. Global
|
|
163
|
+
filters (date, model, project, category, cost/token thresholds, sandbox toggle) update
|
|
164
|
+
every chart and KPI. Everything exports to CSV/JSON, plus a printable PDF report.
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
## Configuration
|
|
169
|
+
|
|
170
|
+
Two files, both editable without touching code. The server picks up changes to
|
|
171
|
+
`settings.json` immediately; `pricing.json` needs a restart.
|
|
172
|
+
|
|
173
|
+
### `config/pricing.json`
|
|
174
|
+
|
|
175
|
+
Model prices per million tokens, kept strictly separate from usage data so the table
|
|
176
|
+
can be updated as prices change. A model with no entry falls back to
|
|
177
|
+
`default_model_pricing` and is flagged as such in the Model analysis view.
|
|
178
|
+
|
|
179
|
+
### `config/settings.json`
|
|
180
|
+
|
|
181
|
+
```jsonc
|
|
182
|
+
{
|
|
183
|
+
"billing_period": { "mode": "calendar_month", "anchor_day": 1 },
|
|
184
|
+
"limits": { // null => reported as unavailable, never guessed
|
|
185
|
+
"monthly_cost_allowance_usd": null,
|
|
186
|
+
"monthly_token_allowance": null,
|
|
187
|
+
"monthly_request_allowance": null,
|
|
188
|
+
"remaining_credits_usd": null
|
|
189
|
+
},
|
|
190
|
+
"budgets": { "monthly_usd": null, "daily_usd": null, "per_project_usd": {} },
|
|
191
|
+
"alert_thresholds_pct": [50, 75, 90, 100],
|
|
192
|
+
"waste_rules": { /* thresholds for each detector */ },
|
|
193
|
+
"anomaly": { /* z-score and ratio triggers */ },
|
|
194
|
+
"scorecard": { /* the reference points each dimension is graded against */ }
|
|
195
|
+
}
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
The **Budgets** view edits limits, budgets and thresholds from the browser and writes
|
|
199
|
+
them back to this file.
|
|
200
|
+
|
|
201
|
+
Note on `waste_rules.low_output_ratio_vs_median`: sessions are flagged relative to
|
|
202
|
+
*your own* median output ratio rather than an absolute number, because a healthy ratio
|
|
203
|
+
depends entirely on how agentic your workload is.
|
|
204
|
+
|
|
205
|
+
---
|
|
206
|
+
|
|
207
|
+
## How it works
|
|
208
|
+
|
|
209
|
+
```
|
|
210
|
+
~/.claude/projects/**/*.jsonl
|
|
211
|
+
│
|
|
212
|
+
▼ finops/etl.py stream-parse, normalize, roll up
|
|
213
|
+
~/.claude-finops/data/finops.db SQLite warehouse
|
|
214
|
+
│
|
|
215
|
+
▼ finops/analytics.py KPIs · burn · forecast · waste · anomalies · scorecard
|
|
216
|
+
finops/api.py stdlib HTTP: JSON API + static files (127.0.0.1 only)
|
|
217
|
+
│
|
|
218
|
+
▼ web/ vanilla JS, inline-SVG charts, no build step
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
### Data model
|
|
222
|
+
|
|
223
|
+
`projects` → `sessions` → `prompts` → `requests` (the usage event) → `tool_calls`,
|
|
224
|
+
plus `files_touched`. A request carries the full token split (input, output, thinking,
|
|
225
|
+
cache read, cache write 5m/1h), derived `billable_tokens` and `context_tokens`,
|
|
226
|
+
estimated cost, a no-cache counterfactual cost, and measured latency.
|
|
227
|
+
|
|
228
|
+
### Definitions worth knowing
|
|
229
|
+
|
|
230
|
+
- **billable_tokens** = input + output + cache read + cache write. Agentic coding is
|
|
231
|
+
dominated by cache reads, so this number is large by nature.
|
|
232
|
+
- **context_tokens** = input + cache read + cache write — the prompt side of one
|
|
233
|
+
request, used as the context-size proxy.
|
|
234
|
+
- **latency** = gap to the preceding message, discarded above 15 minutes since that is
|
|
235
|
+
idle time rather than model latency.
|
|
236
|
+
- **Cache savings** price every cached token at the plain input rate as a
|
|
237
|
+
counterfactual. It is a model, not a bill you avoided.
|
|
238
|
+
- **Prompt categories** are transparent keyword rules (`finops/classify.py`); every
|
|
239
|
+
prompt records the matched terms and a confidence, both visible in its detail view.
|
|
240
|
+
|
|
241
|
+
### Rebuilding
|
|
242
|
+
|
|
243
|
+
```bash
|
|
244
|
+
python3 -m finops.etl # default ~/.claude/projects
|
|
245
|
+
python3 -m finops.etl /path/to/transcripts # or a specific directory
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
The build is destructive and idempotent — it drops and recreates `~/.claude-finops/data/finops.db`.
|
|
249
|
+
|
|
250
|
+
---
|
|
251
|
+
|
|
252
|
+
## Commands
|
|
253
|
+
|
|
254
|
+
```
|
|
255
|
+
claude-finops start the dashboard
|
|
256
|
+
claude-finops --rebuild re-read transcripts, then start
|
|
257
|
+
claude-finops --stop stop it
|
|
258
|
+
claude-finops --where where your data, settings and keys live
|
|
259
|
+
claude-finops --set-key store a provider API key (hidden prompt, 0600)
|
|
260
|
+
claude-finops --keys which provider keys are configured
|
|
261
|
+
claude-finops --help everything
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
Installed globally (`npm i -g claude-finops`) or run ad hoc (`npx claude-finops`),
|
|
265
|
+
these work from any directory. From a source checkout, `./run.sh` takes the same flags.
|
|
266
|
+
|
|
267
|
+
---
|
|
268
|
+
|
|
269
|
+
## Where your data lives
|
|
270
|
+
|
|
271
|
+
Everything the app writes lives outside the install folder, in one state directory:
|
|
272
|
+
|
|
273
|
+
```
|
|
274
|
+
~/.claude-finops/
|
|
275
|
+
data/finops.db SQLite warehouse (your prompt text)
|
|
276
|
+
data/cloud_cache.json cached billing figures
|
|
277
|
+
data/server.pid|.log running server
|
|
278
|
+
settings.local.json budgets and limits set in the UI
|
|
279
|
+
secrets.local.json provider API keys (0600)
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
Set `CLAUDE_FINOPS_HOME=/some/path` to put it elsewhere. The install folder holds
|
|
283
|
+
only code and the shared defaults in `config/`, so it can be replaced on upgrade —
|
|
284
|
+
or shipped as a package — without touching your data. An older in-tree `data/`
|
|
285
|
+
layout is copied across automatically on first run; the originals are left in
|
|
286
|
+
place for you to delete once you are happy.
|
|
287
|
+
|
|
288
|
+
---
|
|
289
|
+
|
|
290
|
+
## Privacy
|
|
291
|
+
|
|
292
|
+
`~/.claude-finops/data/finops.db` and the prompt/CSV exports contain **your full prompt text**. The
|
|
293
|
+
server binds to `127.0.0.1` only and makes no outbound requests, but treat the
|
|
294
|
+
database and any export you generate as sensitive.
|
|
295
|
+
|
|
296
|
+
---
|
|
297
|
+
|
|
298
|
+
## Sharing it
|
|
299
|
+
|
|
300
|
+
The app has nothing tied to one person. Whoever runs it sees **their own** Claude usage:
|
|
301
|
+
|
|
302
|
+
- It reads `~/.claude/projects` on the machine it runs on (override with `CLAUDE_PROJECTS=/path`).
|
|
303
|
+
- The account label comes from that machine's `~/.claude.json`.
|
|
304
|
+
- Budgets and limits you set in the UI are saved to `~/.claude-finops/settings.local.json`. The committed `config/settings.json` holds only shared defaults.
|
|
305
|
+
|
|
306
|
+
**Requirements:** macOS, Windows or Linux; Python 3.9+; Claude Code used at least once.
|
|
307
|
+
Nothing else to install.
|
|
308
|
+
|
|
309
|
+
| OS | Start | Stop | Package to share |
|
|
310
|
+
|---|---|---|---|
|
|
311
|
+
| macOS / Linux | `./run.sh` | `./run.sh --stop` | `./share.sh` |
|
|
312
|
+
| Windows | `run.cmd` (or `py run.py`) | Ctrl-C | `py run.py --share` |
|
|
313
|
+
|
|
314
|
+
**To share:** run `./share.sh`, which writes `../claude-finops.zip` containing code and defaults
|
|
315
|
+
only. Your data never lives in the folder, so there is nothing to strip. **Never send
|
|
316
|
+
`~/.claude-finops/`**, because `finops.db` holds your prompt text.
|
|
317
|
+
|
|
318
|
+
**Recipient:** unzip, then `./run.sh` (macOS/Linux) or double-click `run.cmd` (Windows), and open
|
|
319
|
+
<http://127.0.0.1:8787>.
|
|
320
|
+
|
|
321
|
+
Free-model setup per OS: macOS installs Ollama with Homebrew, Windows with winget. Linux needs
|
|
322
|
+
sudo, so the app shows the one command to run yourself. Launchers go to `~/.local/bin`, as `.cmd`
|
|
323
|
+
files on Windows.
|
|
324
|
+
|
|
325
|
+
---
|
|
326
|
+
|
|
327
|
+
## Actions (these change your machine, and only when you click)
|
|
328
|
+
|
|
329
|
+
- **⟳ Sync** (top bar) re-reads `~/.claude` and swaps in fresh data without restarting.
|
|
330
|
+
- **Free models** adds a launcher such as `claude-qwen` in `~/.local/bin` that runs Claude Code
|
|
331
|
+
on a free model (Ollama locally, or OpenRouter). It lists every step first, and asks before it
|
|
332
|
+
installs Ollama or downloads a model. Your normal `claude` is unchanged. Edit the list in
|
|
333
|
+
`config/free_models.json`.
|
|
334
|
+
- **Skills & MCP** suggests MCP servers and skills from work you repeat, with the evidence.
|
|
335
|
+
"Add" runs `claude mcp add -s user …`; "Create skill" writes `~/.claude/skills/<name>/SKILL.md`.
|
|
336
|
+
|
|
337
|
+
---
|
|
338
|
+
|
|
339
|
+
## Multi-agent
|
|
340
|
+
|
|
341
|
+
Besides Claude Code, the warehouse loads every other coding agent it finds on the machine:
|
|
342
|
+
|
|
343
|
+
| Agent | Read from | What it gives |
|
|
344
|
+
|---|---|---|
|
|
345
|
+
| Codex | `~/.codex/sessions` | Tokens + model per turn; cost at OpenAI list price |
|
|
346
|
+
| Gemini CLI | `~/.gemini/tmp/*/chats` | Tokens + model per reply; cost at Gemini list price |
|
|
347
|
+
| Cursor | `~/.cursor/projects/*/agent-transcripts`, Cursor IDE `state.vscdb` (read-only) | Prompts + tool calls; tokens only where Cursor stored them; no model, not priced |
|
|
348
|
+
|
|
349
|
+
Pick agents with the chips at the top: click for one, Cmd/Ctrl-click to combine, **All** for
|
|
350
|
+
everything. Every page follows the selection, and **Agents** shows them side by side.
|
|
351
|
+
Prices for the other providers live in `config/pricing.json` with their source URLs.
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
---
|
|
355
|
+
|
|
356
|
+
## License and trademarks
|
|
357
|
+
|
|
358
|
+
MIT — see [LICENSE](LICENSE).
|
|
359
|
+
|
|
360
|
+
Not affiliated with, endorsed by, or sponsored by Anthropic. "Claude" and "Claude Code"
|
|
361
|
+
are trademarks of Anthropic, PBC, used here only to describe what this tool reads.
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
/*
|
|
4
|
+
* npm entry point. Node does no work here beyond finding a Python 3.9+ and
|
|
5
|
+
* handing control to run.py, which stays the single source of truth.
|
|
6
|
+
*
|
|
7
|
+
* Interpreter search order: $CLAUDE_FINOPS_PYTHON, then the platform default.
|
|
8
|
+
* The app writes only to ~/.claude-finops, so it runs fine from a read-only
|
|
9
|
+
* npx cache.
|
|
10
|
+
*/
|
|
11
|
+
const { spawn, spawnSync } = require("child_process");
|
|
12
|
+
const path = require("path");
|
|
13
|
+
|
|
14
|
+
const RUN_PY = path.join(__dirname, "..", "run.py");
|
|
15
|
+
const MIN = [3, 9];
|
|
16
|
+
|
|
17
|
+
function candidates() {
|
|
18
|
+
const explicit = process.env.CLAUDE_FINOPS_PYTHON;
|
|
19
|
+
if (explicit) return [{ cmd: explicit, pre: [] }];
|
|
20
|
+
return process.platform === "win32"
|
|
21
|
+
? [{ cmd: "py", pre: ["-3"] }, { cmd: "python", pre: [] }, { cmd: "python3", pre: [] }]
|
|
22
|
+
: [{ cmd: "python3", pre: [] }, { cmd: "python", pre: [] }];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Ask the interpreter its own version rather than parsing `--version` output,
|
|
26
|
+
// which differs across builds.
|
|
27
|
+
function versionOf(c) {
|
|
28
|
+
const probe = spawnSync(c.cmd, [...c.pre, "-c", "import sys;print('%d.%d' % sys.version_info[:2])"],
|
|
29
|
+
{ encoding: "utf8" });
|
|
30
|
+
if (probe.error || probe.status !== 0) return null;
|
|
31
|
+
const parts = String(probe.stdout).trim().split(".").map(Number);
|
|
32
|
+
return parts.length === 2 && parts.every(Number.isFinite) ? parts : null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function resolve() {
|
|
36
|
+
let best = null;
|
|
37
|
+
for (const c of candidates()) {
|
|
38
|
+
const v = versionOf(c);
|
|
39
|
+
if (!v) continue;
|
|
40
|
+
if (v[0] > MIN[0] || (v[0] === MIN[0] && v[1] >= MIN[1])) return { ...c, v };
|
|
41
|
+
best = best || { ...c, v };
|
|
42
|
+
}
|
|
43
|
+
return { tooOld: best };
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const py = resolve();
|
|
47
|
+
|
|
48
|
+
if (py.tooOld) {
|
|
49
|
+
console.error(`Claude FinOps needs Python ${MIN.join(".")}+, but found ${py.tooOld.v.join(".")}.`);
|
|
50
|
+
console.error("Install a newer Python: https://www.python.org/downloads/");
|
|
51
|
+
process.exit(1);
|
|
52
|
+
}
|
|
53
|
+
if (!py.cmd) {
|
|
54
|
+
console.error("Claude FinOps needs Python 3.9+, which was not found on your PATH.");
|
|
55
|
+
console.error(process.platform === "darwin"
|
|
56
|
+
? "Install it with: brew install python3 (or https://www.python.org/downloads/)"
|
|
57
|
+
: "Install it from: https://www.python.org/downloads/");
|
|
58
|
+
console.error("Already have one elsewhere? Set CLAUDE_FINOPS_PYTHON=/path/to/python3");
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const child = spawn(py.cmd, [...py.pre, "-X", "utf8", RUN_PY, ...process.argv.slice(2)],
|
|
63
|
+
{ stdio: "inherit" });
|
|
64
|
+
|
|
65
|
+
// Forward the signals a user actually sends, so Ctrl-C stops the dashboard
|
|
66
|
+
// rather than orphaning it.
|
|
67
|
+
for (const sig of ["SIGINT", "SIGTERM", "SIGHUP"]) {
|
|
68
|
+
process.on(sig, () => { try { child.kill(sig); } catch { /* already gone */ } });
|
|
69
|
+
}
|
|
70
|
+
child.on("error", (err) => {
|
|
71
|
+
console.error(`Could not start ${py.cmd}: ${err.message}`);
|
|
72
|
+
process.exit(1);
|
|
73
|
+
});
|
|
74
|
+
child.on("exit", (code, signal) => process.exit(signal ? 1 : code === null ? 1 : code));
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"_comment": "Free models Claude Code can talk to. 'Adding' one creates a launcher command (e.g. claude-qwen) that starts Claude Code pointed at that model. Your normal `claude` is never changed. Edit this list to add more.",
|
|
3
|
+
"models": [
|
|
4
|
+
{
|
|
5
|
+
"id": "qwen-local",
|
|
6
|
+
"name": "Qwen 3.5 9B (local, Ollama)",
|
|
7
|
+
"provider": "ollama",
|
|
8
|
+
"model": "qwen3.5:9b",
|
|
9
|
+
"command": "claude-qwen",
|
|
10
|
+
"download_gb": 6.6,
|
|
11
|
+
"min_ram_gb": 16,
|
|
12
|
+
"good_for": "Best local pick for 16 GB Macs. Tools + thinking, 256K context. Routine edits, tests, docs.",
|
|
13
|
+
"limits": "Slower than Claude and weaker on large multi-file changes. Runs on your CPU/GPU."
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"id": "qwen38-local",
|
|
17
|
+
"name": "Qwen 3.8 27B (local, Ollama)",
|
|
18
|
+
"provider": "ollama",
|
|
19
|
+
"model": "qwen3.8:27b",
|
|
20
|
+
"command": "claude-qwen38",
|
|
21
|
+
"download_gb": 18,
|
|
22
|
+
"min_ram_gb": 32,
|
|
23
|
+
"good_for": "Newest Qwen. Strongest local option for multi-file work, 256K context.",
|
|
24
|
+
"limits": "Needs a 32 GB+ Mac (18 GB model). On 16 GB it will be unusably slow or fail to load."
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"id": "qwen-coder-small",
|
|
28
|
+
"name": "Qwen 2.5 Coder 7B (local, Ollama)",
|
|
29
|
+
"provider": "ollama",
|
|
30
|
+
"model": "qwen2.5-coder:7b",
|
|
31
|
+
"command": "claude-qwen-coder",
|
|
32
|
+
"download_gb": 4.7,
|
|
33
|
+
"min_ram_gb": 8,
|
|
34
|
+
"good_for": "Lightweight laptops. Small code questions and single-file edits.",
|
|
35
|
+
"limits": "Older (2024) and weakest at tool use. Prefer Qwen 3.5 9B if you have 16 GB RAM."
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"id": "qwen-openrouter",
|
|
39
|
+
"name": "Qwen3 Coder (free, OpenRouter cloud)",
|
|
40
|
+
"provider": "openrouter",
|
|
41
|
+
"model": "qwen/qwen3-coder:free",
|
|
42
|
+
"command": "claude-qwen-cloud",
|
|
43
|
+
"download_gb": 0,
|
|
44
|
+
"min_ram_gb": 0,
|
|
45
|
+
"good_for": "Big free coding model with nothing to download.",
|
|
46
|
+
"limits": "Needs a free OpenRouter API key. Free tier is rate-limited, and prompts are sent to OpenRouter."
|
|
47
|
+
}
|
|
48
|
+
]
|
|
49
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"_comment": "Qualitative 1-5 ratings for the Compare view. Judgement from model size, age and known behaviour in agentic coding. Not benchmark scores; edit freely. Keys match model ids in pricing.json or ids in free_models.json.",
|
|
3
|
+
"ratings": {
|
|
4
|
+
"claude-opus-5": {"tools": 5, "reasoning": 5, "multifile": 5, "speed": "Fast", "best_for": "Hard bugs, architecture, reviews"},
|
|
5
|
+
"claude-sonnet-5": {"tools": 5, "reasoning": 4, "multifile": 5, "speed": "Fast", "best_for": "Everyday coding: best value"},
|
|
6
|
+
"claude-fable-5-1": {"tools": 5, "reasoning": 4, "multifile": 4, "speed": "Fast", "best_for": "Same price tier as Sonnet"},
|
|
7
|
+
"claude-haiku-4-5-20251001": {"tools": 4, "reasoning": 3, "multifile": 3, "speed": "Fastest", "best_for": "Lookups, Explore subagents, small edits"},
|
|
8
|
+
"qwen-openrouter": {"tools": 3, "reasoning": 3, "multifile": 3, "speed": "Medium, throttled on free tier", "best_for": "Free general coding, if sending code to OpenRouter is OK"},
|
|
9
|
+
"qwen38-local": {"tools": 3, "reasoning": 3, "multifile": 3, "speed": "Needs 32 GB+ RAM", "best_for": "Strongest local option on a big machine"},
|
|
10
|
+
"qwen-local": {"tools": 2, "reasoning": 2, "multifile": 2, "speed": "Slow; slower as context grows", "best_for": "Offline or private small tasks"},
|
|
11
|
+
"qwen-coder-small": {"tools": 1.5, "reasoning": 1, "multifile": 1, "speed": "Slow", "best_for": "Snippets and explanations only"}
|
|
12
|
+
}
|
|
13
|
+
}
|