llm-cost-attribution 0.1.1 → 0.3.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/README.md CHANGED
@@ -1,150 +1,197 @@
1
1
  # llm-cost-attribution
2
2
 
3
- Per-issue token, turn, and quota analytics for [Claude Code](https://docs.anthropic.com/en/docs/claude-code) and [Codex CLI](https://github.com/openai/codex) sessions. Reads the CLIs' own session JSONLs — **no telemetry pipeline, no database, no API keys**.
3
+ Per-issue cost analytics for [Claude Code](https://docs.anthropic.com/en/docs/claude-code) and [Codex CLI](https://github.com/openai/codex) sessions — how many **tokens** an issue burned, how many **turns** it took (one agent request → response is a turn), and how much of your Codex/Claude plan's rate-limit **quota** it ate. It reads the CLIs' own session logs (JSONL = one JSON record per line) — **no telemetry pipeline, no database, no API keys**.
4
4
 
5
5
  ```bash
6
6
  npx llm-cost-attribution EPAC-1940
7
7
  ```
8
8
 
9
9
  ```
10
- ════════════════════════════════════════════════════════════════════════
11
- LLM COST — EPAC-1940
12
- ════════════════════════════════════════════════════════════════════════
13
- Sessions found: 5
14
- Total turns: 414
15
- Total tokens: 61,357,012
16
-
17
- ────────────────────────────────────────────────────────────────────────
18
- CODEX (4 sessions)
19
- ────────────────────────────────────────────────────────────────────────
20
- Models: gpt-5-codex
21
- Turns: 340
22
- Tokens:
23
- input uncached 1,517,206
24
- cache read 51,024,768
25
- output (visible) 44,683
26
- output (reasoning) 18,649
27
- grand total 52,605,306
28
- Quota (plan_type=pro, 345 samples):
29
- 5h window 58% → 64% used (peak 64%)
30
- 7d window 56% → 57% used (peak 57%)
10
+ LLM COST — EPAC-1940
11
+ Sessions: 5 Turns: 414 Tokens: 61,357,012
12
+
13
+ CODEX (4 sessions) Models: gpt-5-codex Turns: 340
14
+ input uncached 1,517,206
15
+ cache read 51,024,768
16
+ output (visible) 44,683
17
+ output (reasoning) 18,649
18
+ grand total 52,605,306
19
+ Quota (pro, 345 samples): 5h 58%→64% (peak 64%) 7d 56%→57% (peak 57%)
31
20
  ```
32
21
 
33
- ## Designed for Symphony workflows
22
+ Reading that block: **cache read** is tokens the provider served from its prompt cache (cheap, and usually most of the total); **output (reasoning)** is the model's hidden thinking tokens, billed separately from the **visible** answer; **Quota** is how much of your Codex plan's two rolling rate-limit windows — a 5-hour and a 7-day one — these sessions used.
34
23
 
35
- [OpenAI Symphony's specification](https://github.com/openai/symphony/blob/main/SPEC.md) requires that each issue gets its own filesystem workspace, and that the coding agent's `cwd` equals that workspace path:
24
+ Requires Node 20+. Zero runtime dependencies.
36
25
 
37
- - **§4.1.4 Workspace** — "Filesystem workspace assigned to one issue identifier."
38
- - **Workspace path formula** — `<workspace.root>/<sanitized_issue_identifier>`.
39
- - **Invariant 1** — "Run the coding agent only in the per-issue workspace path... validate: `cwd == workspace_path`."
26
+ ## How it works
40
27
 
41
- Because of those requirements, the working directory of every Claude Code or Codex CLI session that Symphony (or any Symphony-spec-conformant orchestrator) launches always carries the issue identifier as its last path component. The CLI agents in turn record that `cwd` in every session JSONL they create. So the issue identifier is already in the transcript no custom telemetry pipeline needed to join.
28
+ Both CLIs persist every run as JSONL — Claude Code in `~/.claude/projects/<encoded-cwd>/<sessionId>.jsonl` (`<encoded-cwd>` is just the run's working directory with `/` and `.` rewritten to `-`), Codex in `~/.codex/sessions/YYYY/MM/DD/rollout-*.jsonl` and each file records, per turn, the provider-reported token counts (the same numbers your account is billed against) plus, for Codex, its rate-limit usage. This package walks both directories, keeps the sessions whose **working directory** matches the issue ID you ask for, and adds them up.
42
29
 
43
- This package's default `--cwd-pattern` matches the two most common `workspace.root` configurations:
30
+ How does a session get matched to an issue? By its **working directory** (`cwd`). Under [Symphony](https://github.com/openai/symphony/blob/main/SPEC.md)'s spec — Symphony being an orchestrator that runs coding agents one issue at a time — each agent runs in a directory dedicated to its issue (`<workspace.root>/<ISSUE-ID>`), so the issue ID is already baked into every transcript's path; no custom pipeline needed. The default `--cwd-pattern` (the regex that pulls the issue ID out of that path) matches both the spec default (`<tmp>/symphony_workspaces/<ID>`) and the common in-repo layout (`<repo>/.symphony/workspaces/<ID>`). For any other layout, pass your own regex with one capture group around the ID:
44
31
 
45
- 1. The Symphony spec default: `<system-temp>/symphony_workspaces/<ISSUE-ID>` (e.g. `/tmp/symphony_workspaces/EPAC-1940`).
46
- 2. A common in-repo override: `<repo>/.symphony/workspaces/<ISSUE-ID>` (used by Autopilot and the Riddim factory's Symphony config).
32
+ ```bash
33
+ llm-cost FOO-12 --cwd-pattern '-([A-Z]+-\d+)$' # ../repo-worktrees/<ID>
34
+ llm-cost 1234 --cwd-pattern '/issues/(\d+)$' # ~/issues/<id>/
35
+ ```
47
36
 
48
- For any other `workspace.root` setting, pass `--cwd-pattern '<regex>'` with one capture group for the issue identifier — see "[The convention](#the-convention)" below.
37
+ If your workflow doesn't give each issue its own directory, this package can't disambiguate sessions — see "What it doesn't do."
49
38
 
50
- ## How it works
51
-
52
- Both CLIs persist every session they run as JSONL:
39
+ ## Install
53
40
 
54
- - **Claude Code** writes `~/.claude/projects/<encoded-cwd>/<sessionId>.jsonl` for every interactive and non-interactive run (encoded-cwd is the absolute working directory with `/` and `.` replaced by `-`).
55
- - **Codex CLI** writes `~/.codex/sessions/YYYY/MM/DD/rollout-<timestamp>-<id>.jsonl` for every run, with the working directory recorded in the first `session_meta` event.
41
+ ```bash
42
+ npx llm-cost-attribution EPAC-1940 # one-shot
43
+ npm install -g llm-cost-attribution # then: llm-cost EPAC-1940
44
+ ```
56
45
 
57
- Each file carries provider-reported token usage per turn — the same numbers your Anthropic / OpenAI account is billed against:
46
+ ## CLI
58
47
 
59
- | Provider | Tokens captured |
60
- |---|---|
61
- | Claude | `input_tokens`, `cache_read_input_tokens`, `cache_creation.{ephemeral_5m,1h}_input_tokens`, `output_tokens` |
62
- | Codex | `input_tokens`, `cached_input_tokens`, `output_tokens`, `reasoning_output_tokens` (deltaed from cumulative) |
63
- | Codex (additionally) | `rate_limits.{primary,secondary}.used_percent` per turn |
48
+ ```
49
+ llm-cost <ISSUE-ID> [options]
50
+ llm-cost <ISSUE-ID> --from-usage <usage.jsonl-or-dir>
51
+ llm-cost list
52
+ llm-cost backfill --out <usage.jsonl-path>
53
+ llm-cost calibrate <usage.jsonl-or-dir> [--seed N] [--holdout F]
54
+ llm-cost --help
64
55
 
65
- This package walks both directories, filters sessions whose working directory matches an issue identifier you ask for, and aggregates.
56
+ Options:
57
+ --cwd-pattern <regex> JS regex matching the cwd; one capture group = issue ID.
58
+ --claude-dir <path> Override ~/.claude/projects.
59
+ --codex-dir <path> Override ~/.codex/sessions.
60
+ --from-usage <path> Read a baked usage.jsonl file/dir instead of transcripts.
61
+ --out <path> (backfill) Destination usage.jsonl. Appended.
62
+ --seed <int> (calibrate) Held-out split seed. Default 1.
63
+ --holdout <0..1> (calibrate) Fraction held out per cell. Default 0.2.
64
+ --quantile <0..1> (calibrate) Band to test. Default 0.8.
65
+ --threshold <0..1> (calibrate) Flag coverage drift beyond this. Default 0.1.
66
+ --json Emit JSON instead of a table.
67
+ --no-pricing Suppress the dollar block.
68
+ ```
66
69
 
67
- ## The convention
70
+ ## Delete transcripts, keep cost history
68
71
 
69
- You map sessions to issues via the **working directory at session start**. By default this package matches the Symphony-spec convention:
72
+ Transcripts are large (MBs per session, GBs across a factory) and mostly conversation content the cost tool doesn't need. `backfill` bakes every transcript into a small append-only JSONL (~1 KB/turn, no prompt/response content); queries then read that file, and the transcripts are safe to delete:
70
73
 
71
- ```
72
- <repo>/.symphony/workspaces/<ISSUE-ID>
74
+ ```bash
75
+ llm-cost backfill --out ~/llm-cost-history.jsonl
76
+ llm-cost EPAC-1940 --from-usage ~/llm-cost-history.jsonl
77
+ rm -rf ~/.claude/projects ~/.codex/sessions # once numbers verified
73
78
  ```
74
79
 
75
- A regex extracts `<ISSUE-ID>`. If your workflow uses a different layout, pass `--cwd-pattern '<regex>'` with one capture group:
80
+ | | Before | After |
81
+ |---|---:|---:|
82
+ | Disk | 5.0 GB | 125 MB (40× smaller) |
83
+ | Query time | ~3 min | ~0.3 s |
76
84
 
77
- ```bash
78
- # Your workflow uses ../repo-worktrees/<ID>
79
- llm-cost FOO-12 --cwd-pattern '-([A-Z]+-\d+)$'
85
+ The bake is lossless for everything the analysis uses (quota windows, Claude cache tiers, Codex reasoning/visible split, totals, models, timestamps, workspace provenance). The format follows the [Symphony Cost Telemetry Extension spec](https://github.com/RiddimSoftware/groove/blob/main/specs/symphony-cost-telemetry-extension/SPEC.md), so a conformant orchestrator can emit `usage.jsonl` directly and skip the bake — optional interop, not required.
80
86
 
81
- # Your workflow uses ~/issues/<id>/
82
- llm-cost 1234 --cwd-pattern '/issues/(\d+)$'
87
+ ## Is the forecast trustworthy? (`calibrate`)
88
+
89
+ A **P80** is the 80th-percentile cost — the number 80% of comparable issues come in at or below. Claiming "P80 = 12K tokens" is only honest if, on issues the forecaster never saw, the real cost actually lands under 12K about 80% of the time; otherwise it's a horoscope. `calibrate` checks exactly that against a local `usage.jsonl` whose records are **estimate-tagged** (each one carries the issue's size estimate). It sorts the records into **cells** — groups of past issues sharing the same `{ size, model }` — holds out a reproducible slice of each cell (`--seed` makes the split repeatable), forecasts from what's left, and measures how often the held-out actuals really fell at or below the predicted P80. Any cell whose hit-rate drifts from 80% by more than `--threshold` is flagged ⚠. On a small dataset the coverage figures are themselves noisy — a cell with only a few held-out issues can read 0% or 100% by luck — so treat per-cell flags as directional until cells are well-populated.
90
+
91
+ ```bash
92
+ llm-cost calibrate ~/backfill.out --seed 1 --holdout 0.2
83
93
  ```
84
94
 
85
- If your workflow doesn't give each issue its own working directory (e.g. you switch branches in a single checkout), this package can't disambiguate sessions for you — see "[What it doesn't (and can't) do](#what-it-doesnt-and-cant-do)" below.
95
+ Read-only and local the input is never written back or committed (point it at a gitignored file). Committed tests use only synthetic fixtures (`test/forecast-recovers-known-dist.test.mjs`).
86
96
 
87
- ## Install
97
+ ## What drives your cost? (`cost-drivers`)
88
98
 
89
- ```bash
90
- # One-shot via npx
91
- npx llm-cost-attribution EPAC-1940
99
+ `cost-drivers` runs an end-to-end correlation analysis: it reads your LLM cost records, reads diff statistics from a local git repo, joins them by issue key, and prints Spearman rank correlation, linear Pearson, log-log Pearson, and a decile table. The goal is to understand which attributes of an issue predict how much it costs — using your own data, not anyone else's benchmarks.
100
+
101
+ **Minimal inputs:** a local git repo whose commit subjects include issue keys, and transcripts (or a `usage.jsonl`) for the same issues.
92
102
 
93
- # Install globally
94
- npm install -g llm-cost-attribution
95
- llm-cost EPAC-1940
103
+ ```bash
104
+ llm-cost cost-drivers --repo ~/code/my-project
105
+ llm-cost cost-drivers --repo ~/code/my-project --metric turns
106
+ llm-cost cost-drivers --repo ~/code/my-project --from-usage ~/llm-cost-history.jsonl
96
107
  ```
97
108
 
98
- Requires Node 20+. Zero runtime dependencies.
109
+ Example readout (synthetic numbers for illustration only):
99
110
 
100
- ## CLI
111
+ ```
112
+ ════════════════════════════════════════════════════════════════════════
113
+ COST DRIVERS — diff churn vs tokens
114
+ ════════════════════════════════════════════════════════════════════════
115
+ Join strategy: issue
116
+ Source: ~/code/my-project
117
+ n = 42 pairs unjoined: 3 usage, 5 diffs unmatched commits: 11
118
+
119
+ Correlations:
120
+ Spearman 0.34
121
+ Pearson(linear) 0.21
122
+ Pearson(log-log) 0.40
101
123
 
124
+ Decile table:
125
+ Decile Feature range n Median cost
126
+ ────────────────────────────────────────────────────────────────────────
127
+ 1 14 – 87 4 58.3K
128
+ 2 91 – 210 4 72.1K
129
+ 3 215 – 380 4 91.4K
130
+ 4 384 – 510 4 103.2K
131
+ 5 512 – 740 5 128.7K
132
+ 6 744 – 1.1K 4 145.3K
133
+ 7 1.1K – 1.6K 4 189.6K
134
+ 8 1.6K – 2.4K 4 224.1K
135
+ 9 2.5K – 4.1K 5 301.8K
136
+ 10 4.2K – 9.3K 4 512.4K
102
137
  ```
103
- llm-cost <ISSUE-ID> [options]
104
- llm-cost <ISSUE-ID> --from-usage <usage.jsonl-or-dir>
105
- llm-cost list
106
- llm-cost backfill --out <usage.jsonl-path>
107
- llm-cost --help
108
138
 
109
- Options:
110
- --cwd-pattern <regex> JS regex matching the cwd; one capture group is the issue ID.
111
- Default matches both `<system-temp>/symphony_workspaces/<ID>`
112
- and `<repo>/.symphony/workspaces/<ID>` (raw or Claude-encoded).
113
- --claude-dir <path> Override ~/.claude/projects.
114
- --codex-dir <path> Override ~/.codex/sessions.
115
- --from-usage <path> Read from a usage.jsonl file or directory of `usage*.jsonl`
116
- files instead of the CLI transcripts. See "Delete transcripts,
117
- keep cost history" below.
118
- --out <path> (backfill only) Destination usage.jsonl path. Appended.
119
- --json Emit JSON instead of a table.
120
- -h, --help Print help.
139
+ Reading that block: **Feature range** is diff churn (additions + deletions) in lines; **Median cost** is the median token count for issues in that churn decile. The three correlation coefficients tell the same story from different angles — see "Reading the output" below.
140
+
141
+ ### Join model
142
+
143
+ `cost-drivers` needs to know which cost record belongs to which diff. The `--join-by` flag selects the strategy:
144
+
145
+ | Strategy | How it joins | When to use |
146
+ |---|---|---|
147
+ | `issue` (default) | Extracts issue keys (e.g. `ABC-123`) from commit subjects and from each cost record's `issueIdentifier` / workspace path | Works out of the box with Symphony's per-issue worktree convention and squash-merge commit messages |
148
+ | `worktree` | Joins on the cost record's workspace path vs. the diff record's key | Useful when your diff records carry workspace paths instead of issue keys |
149
+ | `time` | Attributes each cost record to the next commit within `--window` (e.g. `30m`, `2h`, `1d`) | Label-free fallback when commit subjects don't contain keys; inherently approximate |
150
+
151
+ ```bash
152
+ # explicit strategies
153
+ llm-cost cost-drivers --repo ~/code/my-project --join-by issue # default
154
+ llm-cost cost-drivers --repo ~/code/my-project --join-by worktree
155
+ llm-cost cost-drivers --repo ~/code/my-project --join-by time --window 2h
156
+
157
+ # override the key-extraction regex if your project uses a different format
158
+ llm-cost cost-drivers --repo ~/code/my-project --key-pattern 'TICKET-\d+'
121
159
  ```
122
160
 
123
- ## Delete transcripts, keep cost history (optional)
161
+ The `keyOfUsage`, `keyOfDiff`, and `join` overrides are available via the library API (`joinCostWithFeature`) for cases the CLI flags don't cover — for example joining on a custom field, or implementing a fully custom reconciliation.
162
+
163
+ #### Escape hatch: join externally with `dump-* → correlate`
124
164
 
125
- Transcripts are large — a few MB per session, growing to gigabytes across an active factory — and most of the bytes are conversation content the cost tool doesn't need. So `llm-cost` can **bake** every transcript into a small append-only JSONL file (~1 KB per turn, no prompt or response content), then read cost queries from that file instead. After the bake, transcripts are safe to delete.
165
+ If none of the built-in strategies fit, emit the two streams and join them yourself:
126
166
 
127
167
  ```bash
128
- # Bake every transcript on this machine into one file.
129
- llm-cost backfill --out ~/llm-cost-history.jsonl
168
+ # 1. dump the cost stream
169
+ llm-cost dump-usage > usage.jsonl
130
170
 
131
- # Cost queries now run against the much smaller file:
132
- llm-cost EPAC-1940 --from-usage ~/llm-cost-history.jsonl
171
+ # 2. dump the diff stream
172
+ llm-cost dump-diffs --repo ~/code/my-project > diffs.jsonl
133
173
 
134
- # Once you've verified the numbers match, transcripts are safe to delete:
135
- rm -rf ~/.claude/projects ~/.codex/sessions
174
+ # 3. join them however you like, then feed back a { feature, cost } CSV
175
+ llm-cost correlate --pairs my-pairs.csv # CSV: feature,cost[,key]
136
176
  ```
137
177
 
138
- Real-world numbers from a working factory:
178
+ `correlate --pairs` accepts `.csv` (header `feature,cost`) or `.json` (array of `{feature, cost}` objects) and produces the same readout as `cost-drivers`.
139
179
 
140
- | | Before backfill | After backfill |
141
- |---|---:|---:|
142
- | Disk footprint | 5.0 GB | 125 MB (40× smaller) |
143
- | `llm-cost EPAC-1940` query time | ~3 min (full Codex scan) | ~0.3 s |
180
+ ### Reading the output
181
+
182
+ **Three correlation views, not one.** LLM cost is heavy-tailed a handful of expensive issues can dominate a linear average. `cost-drivers` therefore reports:
183
+
184
+ - **Spearman** (rank correlation): captures monotonic relationships without being skewed by outliers. If big issues generally cost more than small ones, Spearman will pick that up even when the raw values vary wildly.
185
+ - **Pearson (linear)**: the standard linear correlation on raw values. On heavy-tailed data it can read near zero even when Spearman is meaningful; it is sensitive to a few extreme issues.
186
+ - **Pearson (log-log)**: Pearson on log₁₀-transformed values, the right view when both axes span orders of magnitude. If cost and diff size both grow geometrically, this is the coefficient that captures it.
144
187
 
145
- The backfill is lossless for everything the cost analysis cares about including the Codex per-window quota readout, the Claude cache-tier split (5m vs 1h), and the Codex reasoning-vs-visible output split. Token grand totals, turn counts, models, timestamps, and workspace-path provenance are preserved exactly. The bake file can also be checked into a private repo, shipped to a billing host, or queried from CI without access to the machine that produced the agent sessions.
188
+ A large gap between Spearman and linear Pearson is a signal that the relationship is real but nonlinear or that a few outliers are suppressing the linear view not that the relationship is absent.
146
189
 
147
- This whole flow is a built-in feature of the package you don't need to know anything about the file format to use it. As a side benefit: the format follows the [Symphony Coding-Agent Cost Telemetry Extension spec](https://github.com/RiddimSoftware/groove/blob/main/specs/symphony-cost-telemetry-extension/SPEC.md), so any other tool that conforms can read or write the same file (e.g. a Symphony-spec-conformant orchestrator can emit `usage.jsonl` directly during runs, skipping the bake step entirely). That interop is purely optional; the package works exactly the same whether you care about the spec or not.
190
+ **Always check `n`.** With a small sample (say n < 20) the coefficients are unreliable and the decile table will have very few rows per bucket. Treat the output as directional until you have more history.
191
+
192
+ **Diff size is output, not effort.** A feature that happens to touch many files will show high churn whether or not it was the most complex work. Churn is the most readily available proxy; other features (issue estimate, turn count) may or may not track cost better on your workload.
193
+
194
+ **Local-git limits.** `readGitDiffs` only sees commits already in your local checkout — run `git fetch` or `git pull` first if you want remote-only commits. For the default `issue` strategy, commits must also carry issue keys in their subjects (the default pattern matches `ABC-123`-style keys; override with `--key-pattern`).
148
195
 
149
196
  ## Library
150
197
 
@@ -156,48 +203,38 @@ import {
156
203
  listKnownIssues,
157
204
  } from 'llm-cost-attribution';
158
205
 
159
- // Read from transcripts directly:
160
- const rollup = await computeIssueCost('EPAC-1940');
161
- console.log(rollup.combinedTokens);
162
- console.log(rollup.providerTotals.codex.quotaSamples);
163
-
164
- // Or read from a backfilled usage.jsonl:
206
+ const rollup = await computeIssueCost('EPAC-1940');
165
207
  const rollup2 = await computeIssueCostFromUsage('EPAC-1940', '~/llm-cost-history.jsonl');
166
-
167
- // Backfill programmatically:
168
- const result = await backfillUsageFromTranscripts({
169
- outFile: '/tmp/usage.jsonl',
170
- onProgress: ({ phase, processed, total }) => console.log(`${phase}: ${processed}/${total}`),
171
- });
172
- console.log(`Wrote ${result.recordsWritten} records`);
208
+ const result = await backfillUsageFromTranscripts({ outFile: '/tmp/usage.jsonl' });
173
209
  ```
174
210
 
175
- Pass `{ cwdPattern, claudeProjectsDir, codexSessionsDir }` to override defaults on any of the above.
211
+ Pass `{ cwdPattern, claudeProjectsDir, codexSessionsDir }` to override defaults.
176
212
 
177
- ## What it doesn't (and can't) do
213
+ ### Diff-size feature records
178
214
 
179
- - **Story-point estimate axis.** Estimates live in your issue tracker (Linear / Jira / GitHub Projects), not in the CLI transcripts. To get cost-vs-estimate rollups you'd need to join issue-tracker data — out of scope for this package.
180
- - **Attempt counts.** The CLI doesn't record "this was attempt #N of M"; if you ran `claude` 5 times on the same issue, this package sees 5 sessions but can't tell you which one shipped.
181
- - **PR-merge state, CI status, reviewer verdicts.** These come from GitHub, not from the CLIs — and the Symphony spec explicitly out-of-scopes them (§2.2 Non-Goals, §11.5): ticket mutations and PR outcomes are delegated to the coding agent's tooling, not recorded by the orchestrator. This package stops at the same boundary: "what's in the CLI transcript."
182
- - **Anything in the Claude Desktop app, claude.ai, ChatGPT, or direct API SDK calls.** Only Claude Code CLI and Codex CLI sessions are stored in the directories this package reads.
215
+ `readGitDiffs(repoPath, { revRange, keyPattern })` reads local `git log --numstat`
216
+ output and yields one aggregated record per issue key found in commit subjects:
183
217
 
184
- ## Pricing
218
+ ```js
219
+ for await (const diff of readGitDiffs('/path/to/repo')) {
220
+ console.log(diff.key, diff.additions + diff.deletions, diff.changedFiles);
221
+ }
222
+ ```
185
223
 
186
- `llm-cost` shows API-equivalent dollar cost per bucket alongside the raw token counts, using a built-in rate table sourced from [anthropic.com/pricing](https://www.anthropic.com/pricing) and [platform.openai.com/docs/pricing](https://platform.openai.com/docs/pricing):
224
+ It is local-first: no GitHub token, network, or API calls. The tradeoff is that it
225
+ sees only history already present in the checkout, and commits must carry issue
226
+ keys in their subjects, as with squash-merge subjects like `[ABC-12]: add widget`.
187
227
 
188
- ```
189
- API-equivalent pricing (gpt-5.5 @ rates verified 2026-05-22):
190
- input uncached $7.59 (1.5M × $5.00/1M)
191
- cache read $25.51 (51.0M × $0.500/1M)
192
- output (visible) $1.34 (44.7K × $30.00/1M)
193
- output (reasoning) $0.56 (18.6K × $30.00/1M)
194
- ───────────────────────────────────────────
195
- total API cost $35.00 [hypothetical — your Codex Pro plan covers this]
196
- ```
228
+ ## What it doesn't (and can't) do
197
229
 
198
- **This is a counterfactual, not your actual spend.** If you're on a subscription plan (Claude Max, Codex Pro, etc.), the dollar number represents what the same token volume would have cost on pay-as-you-go API — useful for comparison, but the marginal cost of running it on your actual plan is captured by the Codex quota readout above (`5h primary 58% → 64% used`), not by the dollar total.
230
+ - **Story-point estimates** live in your tracker, not the transcripts (see the sibling `llm-cost-estimation`).
231
+ - **Attempt counts** — the CLI doesn't record "attempt #N"; 5 runs look like 5 sessions with no winner marked.
232
+ - **PR / CI / reviewer state** — comes from GitHub, not the CLIs; out of scope (matches Symphony §2.2/§11.5).
233
+ - **Claude Desktop, claude.ai, ChatGPT, raw API SDK** — only Claude Code CLI and Codex CLI sessions are read.
234
+
235
+ ## Pricing
199
236
 
200
- The CLI warns when the bundled rate table is more than 90 days old. Pass `--no-pricing` to suppress the block entirely.
237
+ `llm-cost` shows API-equivalent dollar cost per bucket from a built-in rate table ([Anthropic](https://www.anthropic.com/pricing), [OpenAI](https://platform.openai.com/docs/pricing)). **This is a counterfactual, not your actual spend:** on a subscription plan (Claude Max, Codex Pro) it's what the same tokens would cost pay-as-you-go — your real marginal cost is the quota readout, not the dollar total. The CLI warns when the table is >90 days old; `--no-pricing` suppresses the block.
201
238
 
202
239
  ## License
203
240