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 +165 -128
- package/bin/llm-cost.mjs +645 -2
- package/package.json +3 -2
- package/src/calibrate.mjs +310 -0
- package/src/correlate.mjs +203 -0
- package/src/cost-feature-join.mjs +393 -0
- package/src/forecast.mjs +425 -0
- package/src/git-diff-source.mjs +278 -0
- package/src/index.mjs +160 -0
- package/src/project-forecast.mjs +329 -0
- package/src/quantiles.mjs +39 -0
- package/src/synthetic.mjs +153 -0
package/README.md
CHANGED
|
@@ -1,150 +1,197 @@
|
|
|
1
1
|
# llm-cost-attribution
|
|
2
2
|
|
|
3
|
-
Per-issue
|
|
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
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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
|
-
|
|
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
|
-
|
|
24
|
+
Requires Node 20+. Zero runtime dependencies.
|
|
36
25
|
|
|
37
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
46
|
-
|
|
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
|
-
|
|
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
|
-
##
|
|
51
|
-
|
|
52
|
-
Both CLIs persist every session they run as JSONL:
|
|
39
|
+
## Install
|
|
53
40
|
|
|
54
|
-
|
|
55
|
-
-
|
|
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
|
-
|
|
46
|
+
## CLI
|
|
58
47
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
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
|
-
|
|
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
|
-
##
|
|
70
|
+
## Delete transcripts, keep cost history
|
|
68
71
|
|
|
69
|
-
|
|
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
|
-
|
|
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
|
-
|
|
80
|
+
| | Before | After |
|
|
81
|
+
|---|---:|---:|
|
|
82
|
+
| Disk | 5.0 GB | 125 MB (40× smaller) |
|
|
83
|
+
| Query time | ~3 min | ~0.3 s |
|
|
76
84
|
|
|
77
|
-
|
|
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
|
-
|
|
82
|
-
|
|
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
|
-
|
|
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
|
-
##
|
|
97
|
+
## What drives your cost? (`cost-drivers`)
|
|
88
98
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
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
|
-
|
|
94
|
-
|
|
95
|
-
llm-cost
|
|
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
|
-
|
|
109
|
+
Example readout (synthetic numbers — for illustration only):
|
|
99
110
|
|
|
100
|
-
|
|
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
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
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
|
-
|
|
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
|
-
|
|
165
|
+
If none of the built-in strategies fit, emit the two streams and join them yourself:
|
|
126
166
|
|
|
127
167
|
```bash
|
|
128
|
-
#
|
|
129
|
-
llm-cost
|
|
168
|
+
# 1. dump the cost stream
|
|
169
|
+
llm-cost dump-usage > usage.jsonl
|
|
130
170
|
|
|
131
|
-
#
|
|
132
|
-
llm-cost
|
|
171
|
+
# 2. dump the diff stream
|
|
172
|
+
llm-cost dump-diffs --repo ~/code/my-project > diffs.jsonl
|
|
133
173
|
|
|
134
|
-
#
|
|
135
|
-
|
|
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
|
-
|
|
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
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
211
|
+
Pass `{ cwdPattern, claudeProjectsDir, codexSessionsDir }` to override defaults.
|
|
176
212
|
|
|
177
|
-
|
|
213
|
+
### Diff-size feature records
|
|
178
214
|
|
|
179
|
-
|
|
180
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
**
|
|
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
|
|
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
|
|