repospend 0.1.0 → 0.1.2
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/CHANGELOG.md +20 -0
- package/README.md +21 -7
- package/dist/cli.js +99 -36
- package/docs/data-sources.md +7 -0
- package/docs/llms.txt +1 -0
- package/docs/pricing.md +27 -1
- package/docs/screenshots/agent-friction.png +0 -0
- package/docs/screenshots/dashboard-overview.png +0 -0
- package/docs/screenshots/models-view.png +0 -0
- package/docs/screenshots/repo-detail.png +0 -0
- package/docs/screenshots/repos-view.png +0 -0
- package/docs/screenshots/session-detail.png +0 -0
- package/docs/screenshots/sessions-view.png +0 -0
- package/docs/token-accounting.md +34 -0
- package/package.json +7 -2
- package/web-dist/assets/index-BgcURWGy.js +308 -0
- package/web-dist/assets/index-Bzdfbi5w.css +1 -0
- package/web-dist/index.html +2 -2
- package/web-dist/assets/index-CdTTbCdO.css +0 -1
- package/web-dist/assets/index-DxlpLT8B.js +0 -308
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,26 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to RepoSpend will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## 0.1.2
|
|
6
|
+
|
|
7
|
+
- Add bundled GPT-5.6 API-equivalent pricing for the Sol, Terra, and Luna tiers, including cache-write and cached-input rates.
|
|
8
|
+
- Add bundled Claude Sonnet 5 introductory pricing through August 31, 2026, with dated model ID family pricing and a note for the standard September 1, 2026 rate.
|
|
9
|
+
- Add Claude Opus 4.8 fast mode pricing and keep special fast-mode rate cards separate from regular Opus family fallback.
|
|
10
|
+
- Add GitHub Copilot pricing coverage for MAI-Code-1-Flash and Kimi K2.7 Code, including suffix variant matching for local Copilot model IDs.
|
|
11
|
+
- Add the OpenAI GPT-5.6 preview pricing source to the bundled pricing metadata shown in Settings.
|
|
12
|
+
- Ignore local generated `marketing/` assets so release diffs stay focused on source, docs, and packaged files.
|
|
13
|
+
|
|
14
|
+
## 0.1.1
|
|
15
|
+
|
|
16
|
+
- Add bundled Claude Fable 5 and Claude Mythos 5 API-equivalent pricing, including dated model ID family pricing and future Fable/Mythos version fallback.
|
|
17
|
+
- Price Claude cache writes from the local 5-minute vs 1-hour TTL split when transcripts expose it, with the 1-hour rate kept as the fallback for unsplit cache creation.
|
|
18
|
+
- Improve the dashboard filter bar by replacing always-expanded filter groups with compact dropdowns that close when another filter opens, Escape is pressed, or the user clicks outside.
|
|
19
|
+
- Move the Overview timeline and chart controls directly under the KPI row, keeping diagnostics such as Token Counting in Settings instead of the daily-glance flow.
|
|
20
|
+
- Replace Settings save and local-cache actions that forced full page reloads with React refetches that preserve page context.
|
|
21
|
+
- Improve chart readability with stable repo colors, positive-only minimum bar size, and a model distribution share panel for skewed model usage.
|
|
22
|
+
- Reduce badge and copy noise by quieting provider badges, preserving outcome color, simplifying empty filter copy, and showing a compact filtered-view notice on pages without the filter bar.
|
|
23
|
+
- Improve responsive and accessibility polish with a horizontal narrow-width nav, safer filter popover alignment, system font fallback, tabular numeric values, and reduced-motion CSS.
|
|
24
|
+
|
|
5
25
|
## 0.1.0
|
|
6
26
|
|
|
7
27
|
- Add GitHub Copilot support for local OTEL exports, Copilot session-state files, and VS Code Copilot Chat transcript/debug files, leaving cost unknown when local data lacks full token splits.
|
package/README.md
CHANGED
|
@@ -35,11 +35,11 @@ Best for developers who want to:
|
|
|
35
35
|
|
|
36
36
|
## Preview
|
|
37
37
|
|
|
38
|
-

|
|
39
39
|
|
|
40
|
-
|
|
41
|
-
repo names, sessions, prompts, token counts, and costs are intentional; no
|
|
42
|
-
repository data is shown.
|
|
40
|
+
Preview media uses fictional Middle-earth demo data. The Lord of the Rings themed
|
|
41
|
+
repo names, sessions, prompts, token counts, models, and costs are intentional; no
|
|
42
|
+
private repository data is shown.
|
|
43
43
|
|
|
44
44
|
## What is RepoSpend?
|
|
45
45
|
|
|
@@ -196,9 +196,12 @@ RepoSpend uses normalized model-work totals:
|
|
|
196
196
|
totalTokens = inputTokens + outputTokens + reasoningTokens
|
|
197
197
|
```
|
|
198
198
|
|
|
199
|
-
Cache reads and cache writes are kept as input sub-buckets and priced once.
|
|
200
|
-
|
|
201
|
-
|
|
199
|
+
Cache reads and cache writes are kept as input sub-buckets and priced once. For
|
|
200
|
+
Claude, RepoSpend also uses the 5-minute vs 1-hour cache-write split recorded in
|
|
201
|
+
local transcripts when available. This means RepoSpend token totals may look
|
|
202
|
+
lower than tools that display cache reads/writes as separate addable token
|
|
203
|
+
columns, and Claude API-equivalent cost may differ from tools that collapse all
|
|
204
|
+
cache writes into one rate.
|
|
202
205
|
|
|
203
206
|
For the detailed accounting model and comparison with `ccusage` and Tokscale,
|
|
204
207
|
see [docs/token-accounting.md](docs/token-accounting.md).
|
|
@@ -275,6 +278,17 @@ view that is close to Claude Code's local usage files. Use RepoSpend when you
|
|
|
275
278
|
want a local dashboard that compares AI coding usage across repos, sessions,
|
|
276
279
|
models, and tools.
|
|
277
280
|
|
|
281
|
+
When comparing totals, expect some intentional differences:
|
|
282
|
+
|
|
283
|
+
- RepoSpend includes Claude Desktop/local-agent session files when they exist;
|
|
284
|
+
many terminal-first tools count only `~/.claude/projects`.
|
|
285
|
+
- RepoSpend keeps cached input and cache writes as input sub-buckets rather than
|
|
286
|
+
adding them again to headline token totals.
|
|
287
|
+
- RepoSpend prices Claude cache writes by the recorded 5-minute vs 1-hour TTL
|
|
288
|
+
split when local transcripts expose it.
|
|
289
|
+
- RepoSpend separates Codex visible output from reasoning output so reasoning is
|
|
290
|
+
priced once.
|
|
291
|
+
|
|
278
292
|
Generic Claude Code monitors usually focus on one source. RepoSpend is designed
|
|
279
293
|
as a repo-level AI coding cost tracker: it brings together local Codex, Claude
|
|
280
294
|
Code, and GitHub Copilot data, includes experimental Cursor imports when enabled,
|
package/dist/cli.js
CHANGED
|
@@ -9,11 +9,17 @@ function summarize(sessions) {
|
|
|
9
9
|
const cost = sumKnownCost(sessions);
|
|
10
10
|
const inputTokens = sum(sessions, "inputTokens");
|
|
11
11
|
const cachedInputTokens = sum(sessions, "cachedInputTokens");
|
|
12
|
+
const cacheCreationInputTokens = optionalSum(sessions, "cacheCreationInputTokens");
|
|
13
|
+
const cacheCreationInputTokens5m = optionalSum(sessions, "cacheCreationInputTokens5m");
|
|
14
|
+
const cacheCreationInputTokens1h = optionalSum(sessions, "cacheCreationInputTokens1h");
|
|
12
15
|
return {
|
|
13
16
|
estimatedCostUsd: cost.knownCount > 0 ? cost.value : void 0,
|
|
14
17
|
knownCostSessions: cost.knownCount,
|
|
15
18
|
totalTokens: sum(sessions, "totalTokens"),
|
|
16
19
|
cachedInputTokens,
|
|
20
|
+
cacheCreationInputTokens,
|
|
21
|
+
cacheCreationInputTokens5m,
|
|
22
|
+
cacheCreationInputTokens1h,
|
|
17
23
|
inputTokens,
|
|
18
24
|
outputTokens: sum(sessions, "outputTokens"),
|
|
19
25
|
reasoningTokens: sum(sessions, "reasoningTokens"),
|
|
@@ -80,6 +86,9 @@ function groupBy(sessions, keyFn, labelFn) {
|
|
|
80
86
|
unknownCostSessions: 0,
|
|
81
87
|
inputTokens: 0,
|
|
82
88
|
cachedInputTokens: 0,
|
|
89
|
+
cacheCreationInputTokens: 0,
|
|
90
|
+
cacheCreationInputTokens5m: 0,
|
|
91
|
+
cacheCreationInputTokens1h: 0,
|
|
83
92
|
outputTokens: 0,
|
|
84
93
|
reasoningTokens: 0,
|
|
85
94
|
totalTokens: 0,
|
|
@@ -89,6 +98,9 @@ function groupBy(sessions, keyFn, labelFn) {
|
|
|
89
98
|
};
|
|
90
99
|
group.inputTokens += session.inputTokens;
|
|
91
100
|
group.cachedInputTokens += session.cachedInputTokens;
|
|
101
|
+
group.cacheCreationInputTokens = (group.cacheCreationInputTokens ?? 0) + (session.cacheCreationInputTokens ?? 0);
|
|
102
|
+
group.cacheCreationInputTokens5m = (group.cacheCreationInputTokens5m ?? 0) + (session.cacheCreationInputTokens5m ?? 0);
|
|
103
|
+
group.cacheCreationInputTokens1h = (group.cacheCreationInputTokens1h ?? 0) + (session.cacheCreationInputTokens1h ?? 0);
|
|
92
104
|
group.outputTokens += session.outputTokens;
|
|
93
105
|
group.reasoningTokens += session.reasoningTokens;
|
|
94
106
|
group.totalTokens += session.totalTokens;
|
|
@@ -128,6 +140,8 @@ function toCsv(sessions) {
|
|
|
128
140
|
"inputTokens",
|
|
129
141
|
"cachedInputTokens",
|
|
130
142
|
"cacheCreationInputTokens",
|
|
143
|
+
"cacheCreationInputTokens5m",
|
|
144
|
+
"cacheCreationInputTokens1h",
|
|
131
145
|
"outputTokens",
|
|
132
146
|
"reasoningTokens",
|
|
133
147
|
"reasoningOutputTokens",
|
|
@@ -219,11 +233,15 @@ function normalizePricingModelId(model) {
|
|
|
219
233
|
}
|
|
220
234
|
function claudePricingFamilyModel(model, isUsable = () => true) {
|
|
221
235
|
const families = [
|
|
236
|
+
"claude-fable-5",
|
|
237
|
+
"claude-mythos-5",
|
|
238
|
+
"claude-opus-4-8-fast-mode",
|
|
222
239
|
"claude-opus-4-7",
|
|
223
240
|
"claude-opus-4-6",
|
|
224
241
|
"claude-opus-4-5",
|
|
225
242
|
"claude-opus-4-1",
|
|
226
243
|
"claude-opus-4",
|
|
244
|
+
"claude-sonnet-5",
|
|
227
245
|
"claude-sonnet-4-6",
|
|
228
246
|
"claude-sonnet-4-5",
|
|
229
247
|
"claude-sonnet-4",
|
|
@@ -246,11 +264,13 @@ function copilotPricingFamilyModel(model, isUsable = () => true) {
|
|
|
246
264
|
if ((normalized === "raptor-mini" || normalized.startsWith("raptor-mini-") || normalized.startsWith("oswe-vscode")) && isUsable("raptor-mini")) return "raptor-mini";
|
|
247
265
|
if ((normalized === "lark" || normalized.startsWith("lark-")) && isUsable("lark")) return "lark";
|
|
248
266
|
if ((normalized === "goldeneye" || normalized.startsWith("goldeneye-")) && isUsable("goldeneye")) return "goldeneye";
|
|
267
|
+
if ((normalized === "mai-code-1-flash" || normalized.startsWith("mai-code-1-flash-")) && isUsable("mai-code-1-flash")) return "mai-code-1-flash";
|
|
268
|
+
if ((normalized === "kimi-k2.7-code" || normalized.startsWith("kimi-k2.7-code-")) && isUsable("kimi-k2.7-code")) return "kimi-k2.7-code";
|
|
249
269
|
return void 0;
|
|
250
270
|
}
|
|
251
271
|
function parseVersionedModel(normalized) {
|
|
252
272
|
if (normalized.includes("fast-mode")) return void 0;
|
|
253
|
-
const claude = normalized.match(/^(claude-(?:opus|sonnet|haiku))-(\d+)(?:-(\d+))?/);
|
|
273
|
+
const claude = normalized.match(/^(claude-(?:fable|mythos|opus|sonnet|haiku))-(\d+)(?:-(\d+))?/);
|
|
254
274
|
if (claude) {
|
|
255
275
|
const minor = claude[3] ? Number(claude[3]) : 0;
|
|
256
276
|
return { tier: claude[1], version: Number(claude[2]) + minor / 100 };
|
|
@@ -276,7 +296,7 @@ function versionedFallbackModel(model, knownModels, isUsable = () => true) {
|
|
|
276
296
|
}
|
|
277
297
|
function isCopilotAliasModel(model) {
|
|
278
298
|
const normalized = normalizePricingModelId(model);
|
|
279
|
-
return normalized.startsWith("raptor-mini") || normalized.startsWith("oswe-vscode") || normalized === "lark" || normalized.startsWith("lark-") || normalized.startsWith("goldeneye");
|
|
299
|
+
return normalized.startsWith("raptor-mini") || normalized.startsWith("oswe-vscode") || normalized === "lark" || normalized.startsWith("lark-") || normalized.startsWith("goldeneye") || normalized.startsWith("mai-code-1-flash") || normalized.startsWith("kimi-k2.7-code");
|
|
280
300
|
}
|
|
281
301
|
function positiveRate(value) {
|
|
282
302
|
return typeof value === "number" && Number.isFinite(value) && value > 0;
|
|
@@ -303,14 +323,19 @@ var pricingInfo = {
|
|
|
303
323
|
sourceUrl: "https://developers.openai.com/api/docs/pricing",
|
|
304
324
|
sourceUrls: [
|
|
305
325
|
{ label: "OpenAI pricing reference", url: "https://developers.openai.com/api/docs/pricing" },
|
|
326
|
+
{ label: "OpenAI GPT-5.6 preview pricing", url: "https://openai.com/index/previewing-gpt-5-6-sol/" },
|
|
306
327
|
{ label: "Claude pricing reference", url: "https://platform.claude.com/docs/en/about-claude/pricing" },
|
|
307
328
|
{ label: "GitHub Copilot model pricing reference", url: "https://docs.github.com/en/copilot/reference/copilot-billing/models-and-pricing" }
|
|
308
329
|
],
|
|
309
330
|
unit: "USD per 1M tokens",
|
|
310
|
-
updatedAt: "2026-
|
|
331
|
+
updatedAt: "2026-07-02",
|
|
311
332
|
note: "RepoSpend estimates API-equivalent cost from local token counts and public API-style Standard pricing. This is not your actual bill; subscriptions, credits, provider terms, cache behavior, regional processing, or other billing factors can make your real cost different."
|
|
312
333
|
};
|
|
313
334
|
var defaultPricing = {
|
|
335
|
+
"gpt-5.6": { inputPerMillion: 5, cachedInputPerMillion: 0.5, cacheCreationInputPerMillion: 6.25, outputPerMillion: 30, reasoningOutputPerMillion: 30, note: "GPT-5.6 Sol flagship tier. OpenAI preview pricing lists Sol at $5 input / $30 output per 1M tokens, cache writes at 1.25x input, and cache reads at a 90% discount." },
|
|
336
|
+
"gpt-5.6-sol": { inputPerMillion: 5, cachedInputPerMillion: 0.5, cacheCreationInputPerMillion: 6.25, outputPerMillion: 30, reasoningOutputPerMillion: 30 },
|
|
337
|
+
"gpt-5.6-terra": { inputPerMillion: 2.5, cachedInputPerMillion: 0.25, cacheCreationInputPerMillion: 3.125, outputPerMillion: 15, reasoningOutputPerMillion: 15 },
|
|
338
|
+
"gpt-5.6-luna": { inputPerMillion: 1, cachedInputPerMillion: 0.1, cacheCreationInputPerMillion: 1.25, outputPerMillion: 6, reasoningOutputPerMillion: 6 },
|
|
314
339
|
"gpt-5.5": { inputPerMillion: 5, cachedInputPerMillion: 0.5, outputPerMillion: 30, reasoningOutputPerMillion: 30 },
|
|
315
340
|
"gpt-5.5-pro": { inputPerMillion: 30, outputPerMillion: 180, reasoningOutputPerMillion: 180 },
|
|
316
341
|
"gpt-5.4": { inputPerMillion: 2.5, cachedInputPerMillion: 0.25, outputPerMillion: 15, reasoningOutputPerMillion: 15 },
|
|
@@ -342,18 +367,24 @@ var defaultPricing = {
|
|
|
342
367
|
"oswe-vscode-prime": { inputPerMillion: 0.25, cachedInputPerMillion: 0.025, outputPerMillion: 2, note: "GitHub Copilot Raptor mini internal model id." },
|
|
343
368
|
"lark": { inputPerMillion: 0.25, cachedInputPerMillion: 0.025, outputPerMillion: 2, note: "GitHub Copilot preview model; estimated with lightweight Copilot pricing." },
|
|
344
369
|
"goldeneye": { inputPerMillion: 1.25, cachedInputPerMillion: 0.125, outputPerMillion: 10, note: "GitHub Copilot fine-tuned model using GPT-5.1-Codex pricing." },
|
|
345
|
-
"
|
|
346
|
-
"
|
|
347
|
-
"claude-
|
|
348
|
-
"claude-
|
|
349
|
-
"claude-opus-4-
|
|
350
|
-
"claude-opus-4-
|
|
351
|
-
"claude-opus-4": { inputPerMillion:
|
|
352
|
-
"claude-
|
|
353
|
-
"claude-
|
|
354
|
-
"claude-
|
|
355
|
-
"claude-
|
|
356
|
-
"claude-
|
|
370
|
+
"mai-code-1-flash": { inputPerMillion: 0.75, cachedInputPerMillion: 0.075, outputPerMillion: 4.5, note: "GitHub Copilot supported Microsoft model; API-equivalent estimate, not a Copilot bill." },
|
|
371
|
+
"kimi-k2.7-code": { inputPerMillion: 0.95, cachedInputPerMillion: 0.19, outputPerMillion: 4, note: "GitHub Copilot supported Moonshot AI model; API-equivalent estimate, not a Copilot bill." },
|
|
372
|
+
"claude-fable-5": { inputPerMillion: 10, cacheCreationInput5mPerMillion: 12.5, cacheCreationInput1hPerMillion: 20, cacheCreationInputPerMillion: 20, cachedInputPerMillion: 1, outputPerMillion: 50 },
|
|
373
|
+
"claude-mythos-5": { inputPerMillion: 10, cacheCreationInput5mPerMillion: 12.5, cacheCreationInput1hPerMillion: 20, cacheCreationInputPerMillion: 20, cachedInputPerMillion: 1, outputPerMillion: 50, note: "Limited availability Anthropic model; same public API-equivalent pricing as Claude Fable 5." },
|
|
374
|
+
"claude-opus-4-8": { inputPerMillion: 5, cacheCreationInput5mPerMillion: 6.25, cacheCreationInput1hPerMillion: 10, cacheCreationInputPerMillion: 10, cachedInputPerMillion: 0.5, outputPerMillion: 25 },
|
|
375
|
+
"claude-opus-4-8-fast-mode": { inputPerMillion: 10, cacheCreationInput5mPerMillion: 12.5, cacheCreationInput1hPerMillion: 20, cacheCreationInputPerMillion: 12.5, cachedInputPerMillion: 1, outputPerMillion: 50, note: "Claude Opus 4.8 fast mode research preview pricing. Prompt caching multipliers apply on top of fast mode pricing." },
|
|
376
|
+
"claude-opus-4-7": { inputPerMillion: 5, cacheCreationInput5mPerMillion: 6.25, cacheCreationInput1hPerMillion: 10, cacheCreationInputPerMillion: 10, cachedInputPerMillion: 0.5, outputPerMillion: 25 },
|
|
377
|
+
"claude-opus-4-6": { inputPerMillion: 5, cacheCreationInput5mPerMillion: 6.25, cacheCreationInput1hPerMillion: 10, cacheCreationInputPerMillion: 10, cachedInputPerMillion: 0.5, outputPerMillion: 25 },
|
|
378
|
+
"claude-opus-4-6-fast-mode": { inputPerMillion: 0, cacheCreationInput5mPerMillion: 0, cacheCreationInput1hPerMillion: 0, cacheCreationInputPerMillion: 0, cachedInputPerMillion: 0, outputPerMillion: 0, note: "GitHub Copilot supported model, but public per-token pricing is not listed separately. RepoSpend leaves API-equivalent pricing unset." },
|
|
379
|
+
"claude-opus-4-5": { inputPerMillion: 5, cacheCreationInput5mPerMillion: 6.25, cacheCreationInput1hPerMillion: 10, cacheCreationInputPerMillion: 10, cachedInputPerMillion: 0.5, outputPerMillion: 25 },
|
|
380
|
+
"claude-opus-4-1": { inputPerMillion: 15, cacheCreationInput5mPerMillion: 18.75, cacheCreationInput1hPerMillion: 30, cacheCreationInputPerMillion: 30, cachedInputPerMillion: 1.5, outputPerMillion: 75 },
|
|
381
|
+
"claude-opus-4": { inputPerMillion: 15, cacheCreationInput5mPerMillion: 18.75, cacheCreationInput1hPerMillion: 30, cacheCreationInputPerMillion: 30, cachedInputPerMillion: 1.5, outputPerMillion: 75 },
|
|
382
|
+
"claude-sonnet-5": { inputPerMillion: 2, cacheCreationInput5mPerMillion: 2.5, cacheCreationInput1hPerMillion: 4, cacheCreationInputPerMillion: 4, cachedInputPerMillion: 0.2, outputPerMillion: 10, note: "Anthropic introductory pricing through August 31, 2026. Standard pricing from September 1, 2026 is $3 input / $15 output per 1M tokens." },
|
|
383
|
+
"claude-sonnet-4-6": { inputPerMillion: 3, cacheCreationInput5mPerMillion: 3.75, cacheCreationInput1hPerMillion: 6, cacheCreationInputPerMillion: 6, cachedInputPerMillion: 0.3, outputPerMillion: 15 },
|
|
384
|
+
"claude-sonnet-4-5": { inputPerMillion: 3, cacheCreationInput5mPerMillion: 3.75, cacheCreationInput1hPerMillion: 6, cacheCreationInputPerMillion: 6, cachedInputPerMillion: 0.3, outputPerMillion: 15 },
|
|
385
|
+
"claude-sonnet-4": { inputPerMillion: 3, cacheCreationInput5mPerMillion: 3.75, cacheCreationInput1hPerMillion: 6, cacheCreationInputPerMillion: 6, cachedInputPerMillion: 0.3, outputPerMillion: 15 },
|
|
386
|
+
"claude-haiku-4-5": { inputPerMillion: 1, cacheCreationInput5mPerMillion: 1.25, cacheCreationInput1hPerMillion: 2, cacheCreationInputPerMillion: 2, cachedInputPerMillion: 0.1, outputPerMillion: 5 },
|
|
387
|
+
"claude-3-5-haiku": { inputPerMillion: 0.8, cacheCreationInput5mPerMillion: 1, cacheCreationInput1hPerMillion: 1.6, cacheCreationInputPerMillion: 1.6, cachedInputPerMillion: 0.08, outputPerMillion: 4 }
|
|
357
388
|
};
|
|
358
389
|
function loadPricingTable(pricingPath) {
|
|
359
390
|
if (!pricingPath) {
|
|
@@ -385,14 +416,23 @@ function calculateCostUsd(usage, pricing) {
|
|
|
385
416
|
if (!modelPricing) {
|
|
386
417
|
return void 0;
|
|
387
418
|
}
|
|
388
|
-
const
|
|
419
|
+
const cacheCreationInput5m = usage.cacheCreationInputTokens5m ?? 0;
|
|
420
|
+
const cacheCreationInput1h = usage.cacheCreationInputTokens1h ?? 0;
|
|
421
|
+
const splitCacheCreationInput = cacheCreationInput5m + cacheCreationInput1h;
|
|
422
|
+
const cacheCreationInput = usage.cacheCreationInputTokens ?? splitCacheCreationInput;
|
|
423
|
+
const unclassifiedCacheCreationInput = Math.max(cacheCreationInput - splitCacheCreationInput, 0);
|
|
389
424
|
const billableInput = Math.max(usage.inputTokens - usage.cachedInputTokens - cacheCreationInput, 0);
|
|
390
425
|
const cachedRate = modelPricing.cachedInputPerMillion ?? modelPricing.inputPerMillion;
|
|
391
|
-
const cacheCreationRate = modelPricing.cacheCreationInputPerMillion
|
|
426
|
+
const cacheCreationRate = firstPositiveRate(modelPricing.cacheCreationInputPerMillion, modelPricing.cacheCreationInput1hPerMillion, modelPricing.cacheCreationInput5mPerMillion, modelPricing.inputPerMillion);
|
|
427
|
+
const cacheCreation5mRate = firstPositiveRate(modelPricing.cacheCreationInput5mPerMillion, modelPricing.cacheCreationInputPerMillion, modelPricing.inputPerMillion);
|
|
428
|
+
const cacheCreation1hRate = firstPositiveRate(modelPricing.cacheCreationInput1hPerMillion, modelPricing.cacheCreationInputPerMillion, modelPricing.inputPerMillion);
|
|
392
429
|
const reasoningRate = modelPricing.reasoningOutputPerMillion ?? modelPricing.outputPerMillion;
|
|
393
|
-
const cost = billableInput / 1e6 * modelPricing.inputPerMillion + usage.cachedInputTokens / 1e6 * cachedRate +
|
|
430
|
+
const cost = billableInput / 1e6 * modelPricing.inputPerMillion + usage.cachedInputTokens / 1e6 * cachedRate + cacheCreationInput5m / 1e6 * cacheCreation5mRate + cacheCreationInput1h / 1e6 * cacheCreation1hRate + unclassifiedCacheCreationInput / 1e6 * cacheCreationRate + usage.outputTokens / 1e6 * modelPricing.outputPerMillion + usage.reasoningTokens / 1e6 * reasoningRate;
|
|
394
431
|
return Number(cost.toFixed(6));
|
|
395
432
|
}
|
|
433
|
+
function firstPositiveRate(...rates) {
|
|
434
|
+
return rates.find((rate) => typeof rate === "number" && Number.isFinite(rate) && rate > 0) ?? 0;
|
|
435
|
+
}
|
|
396
436
|
|
|
397
437
|
// packages/core/src/cache.ts
|
|
398
438
|
var activeCacheVersion = "parse-v1";
|
|
@@ -2795,6 +2835,8 @@ function claudeBreakdownToUsage(file, index, options, breakdown, idSuffix = "")
|
|
|
2795
2835
|
inputTokens: breakdown.inputTokens,
|
|
2796
2836
|
cachedInputTokens: breakdown.cachedInputTokens,
|
|
2797
2837
|
cacheCreationInputTokens: breakdown.cacheCreationInputTokens,
|
|
2838
|
+
cacheCreationInputTokens5m: breakdown.cacheCreationInputTokens5m,
|
|
2839
|
+
cacheCreationInputTokens1h: breakdown.cacheCreationInputTokens1h,
|
|
2798
2840
|
outputTokens: breakdown.outputTokens,
|
|
2799
2841
|
reasoningTokens: 0,
|
|
2800
2842
|
reasoningOutputTokens: 0,
|
|
@@ -2817,12 +2859,14 @@ function claudeBreakdownToUsage(file, index, options, breakdown, idSuffix = "")
|
|
|
2817
2859
|
serviceTierSource: serviceTierSummary.serviceTier ? "session_usage" : void 0,
|
|
2818
2860
|
serviceTierConfidence: serviceTierSummary.serviceTier ? "high" : void 0,
|
|
2819
2861
|
serviceTierDetail: serviceTierSummary.serviceTierDetail,
|
|
2820
|
-
sourceMetadata: breakdown.serviceTiers.length || breakdown.serviceSpeeds.length ? {
|
|
2862
|
+
sourceMetadata: breakdown.serviceTiers.length || breakdown.serviceSpeeds.length || breakdown.cacheCreationInputTokens5m > 0 || breakdown.cacheCreationInputTokens1h > 0 ? {
|
|
2821
2863
|
claude: {
|
|
2822
2864
|
serviceTiers: breakdown.serviceTiers,
|
|
2823
2865
|
speeds: breakdown.serviceSpeeds,
|
|
2824
2866
|
serviceTier: serviceTierSummary.serviceTier,
|
|
2825
|
-
speed: serviceSpeedSummary.serviceTier
|
|
2867
|
+
speed: serviceSpeedSummary.serviceTier,
|
|
2868
|
+
cacheCreationInputTokens5m: breakdown.cacheCreationInputTokens5m,
|
|
2869
|
+
cacheCreationInputTokens1h: breakdown.cacheCreationInputTokens1h
|
|
2826
2870
|
}
|
|
2827
2871
|
} : void 0,
|
|
2828
2872
|
userPromptCount: breakdown.userPromptCount,
|
|
@@ -2951,19 +2995,30 @@ function occurrenceKey(filePath, recordIndex) {
|
|
|
2951
2995
|
return `${path6.resolve(filePath)}:${recordIndex}`;
|
|
2952
2996
|
}
|
|
2953
2997
|
function cloneUsage(usage) {
|
|
2998
|
+
const cacheCreation = firstObject4(usage.cache_creation);
|
|
2954
2999
|
return {
|
|
2955
3000
|
input_tokens: numberValue(usage.input_tokens),
|
|
2956
3001
|
cache_read_input_tokens: numberValue(usage.cache_read_input_tokens),
|
|
2957
3002
|
cache_creation_input_tokens: numberValue(usage.cache_creation_input_tokens),
|
|
3003
|
+
cache_creation: cacheCreation ? {
|
|
3004
|
+
ephemeral_5m_input_tokens: numberValue(cacheCreation.ephemeral_5m_input_tokens),
|
|
3005
|
+
ephemeral_1h_input_tokens: numberValue(cacheCreation.ephemeral_1h_input_tokens)
|
|
3006
|
+
} : void 0,
|
|
2958
3007
|
output_tokens: numberValue(usage.output_tokens),
|
|
2959
3008
|
service_tier: stringValue3(usage.service_tier),
|
|
2960
3009
|
speed: stringValue3(usage.speed)
|
|
2961
3010
|
};
|
|
2962
3011
|
}
|
|
2963
3012
|
function mergeUsageMax(target, usage) {
|
|
3013
|
+
const targetCacheCreation = firstObject4(target.cache_creation) ?? {};
|
|
3014
|
+
const usageCacheCreation = firstObject4(usage.cache_creation) ?? {};
|
|
2964
3015
|
target.input_tokens = Math.max(numberValue(target.input_tokens), numberValue(usage.input_tokens));
|
|
2965
3016
|
target.cache_read_input_tokens = Math.max(numberValue(target.cache_read_input_tokens), numberValue(usage.cache_read_input_tokens));
|
|
2966
3017
|
target.cache_creation_input_tokens = Math.max(numberValue(target.cache_creation_input_tokens), numberValue(usage.cache_creation_input_tokens));
|
|
3018
|
+
target.cache_creation = {
|
|
3019
|
+
ephemeral_5m_input_tokens: Math.max(numberValue(targetCacheCreation.ephemeral_5m_input_tokens), numberValue(usageCacheCreation.ephemeral_5m_input_tokens)),
|
|
3020
|
+
ephemeral_1h_input_tokens: Math.max(numberValue(targetCacheCreation.ephemeral_1h_input_tokens), numberValue(usageCacheCreation.ephemeral_1h_input_tokens))
|
|
3021
|
+
};
|
|
2967
3022
|
target.output_tokens = Math.max(numberValue(target.output_tokens), numberValue(usage.output_tokens));
|
|
2968
3023
|
target.service_tier ??= stringValue3(usage.service_tier);
|
|
2969
3024
|
target.speed ??= stringValue3(usage.speed);
|
|
@@ -2971,11 +3026,17 @@ function mergeUsageMax(target, usage) {
|
|
|
2971
3026
|
function applyUsage(breakdown, usage) {
|
|
2972
3027
|
const input = numberValue(usage.input_tokens);
|
|
2973
3028
|
const cacheRead = numberValue(usage.cache_read_input_tokens);
|
|
2974
|
-
const
|
|
3029
|
+
const cacheCreationBreakdown = firstObject4(usage.cache_creation);
|
|
3030
|
+
const cacheCreation5m = numberValue(cacheCreationBreakdown?.ephemeral_5m_input_tokens);
|
|
3031
|
+
const cacheCreation1h = numberValue(cacheCreationBreakdown?.ephemeral_1h_input_tokens);
|
|
3032
|
+
const splitCacheCreation = cacheCreation5m + cacheCreation1h;
|
|
3033
|
+
const cacheCreation = Math.max(numberValue(usage.cache_creation_input_tokens), splitCacheCreation);
|
|
2975
3034
|
const output = numberValue(usage.output_tokens);
|
|
2976
3035
|
breakdown.inputTokens += input + cacheRead + cacheCreation;
|
|
2977
3036
|
breakdown.cachedInputTokens += cacheRead;
|
|
2978
3037
|
breakdown.cacheCreationInputTokens += cacheCreation;
|
|
3038
|
+
breakdown.cacheCreationInputTokens5m += cacheCreation5m;
|
|
3039
|
+
breakdown.cacheCreationInputTokens1h += cacheCreation1h;
|
|
2979
3040
|
breakdown.outputTokens += output;
|
|
2980
3041
|
breakdown.usageCount += 1;
|
|
2981
3042
|
pushUnique(breakdown.serviceTiers, normalizedServiceTier(usage.service_tier));
|
|
@@ -3030,7 +3091,8 @@ function dateKey2(timestamp3) {
|
|
|
3030
3091
|
return timestamp3?.slice(0, 10) ?? "unknown-date";
|
|
3031
3092
|
}
|
|
3032
3093
|
function usageTotal(usage) {
|
|
3033
|
-
|
|
3094
|
+
const cacheCreation = firstObject4(usage.cache_creation);
|
|
3095
|
+
return numberValue(usage.input_tokens) + numberValue(usage.cache_read_input_tokens) + Math.max(numberValue(usage.cache_creation_input_tokens), numberValue(cacheCreation?.ephemeral_5m_input_tokens) + numberValue(cacheCreation?.ephemeral_1h_input_tokens)) + numberValue(usage.output_tokens);
|
|
3034
3096
|
}
|
|
3035
3097
|
function applyContentSignals(breakdown, content) {
|
|
3036
3098
|
if (!Array.isArray(content)) return;
|
|
@@ -3073,6 +3135,8 @@ function emptyBreakdown2() {
|
|
|
3073
3135
|
inputTokens: 0,
|
|
3074
3136
|
cachedInputTokens: 0,
|
|
3075
3137
|
cacheCreationInputTokens: 0,
|
|
3138
|
+
cacheCreationInputTokens5m: 0,
|
|
3139
|
+
cacheCreationInputTokens1h: 0,
|
|
3076
3140
|
outputTokens: 0,
|
|
3077
3141
|
totalTokens: 0,
|
|
3078
3142
|
usageCount: 0,
|
|
@@ -5212,12 +5276,7 @@ import Fastify from "fastify";
|
|
|
5212
5276
|
|
|
5213
5277
|
// apps/server/src/demo/fixtures.ts
|
|
5214
5278
|
var baseDate = "2026-05-18";
|
|
5215
|
-
var demoPricing =
|
|
5216
|
-
...defaultPricing,
|
|
5217
|
-
"gpt-5.3-codex": { inputPerMillion: 1.75, cachedInputPerMillion: 0.175, outputPerMillion: 14, reasoningOutputPerMillion: 14 },
|
|
5218
|
-
"gpt-5.3-codex-spark": { inputPerMillion: 1.75, cachedInputPerMillion: 0.175, outputPerMillion: 14, reasoningOutputPerMillion: 14 },
|
|
5219
|
-
"claude-haiku-4-5-20251001": { inputPerMillion: 1, cacheCreationInputPerMillion: 1.25, cachedInputPerMillion: 0.1, outputPerMillion: 5 }
|
|
5220
|
-
};
|
|
5279
|
+
var demoPricing = defaultPricing;
|
|
5221
5280
|
function issue(command2, category, severity, impact, reason) {
|
|
5222
5281
|
return {
|
|
5223
5282
|
command: command2,
|
|
@@ -5375,11 +5434,11 @@ var demoInputs = [
|
|
|
5375
5434
|
{
|
|
5376
5435
|
id: "lotr-session-003",
|
|
5377
5436
|
repo: "shire-mobile",
|
|
5378
|
-
title: "Investigate token spike after second breakfast",
|
|
5437
|
+
title: "Investigate Opus 4.8 token spike after second breakfast",
|
|
5379
5438
|
sourceClient: "claude",
|
|
5380
5439
|
sourceApp: "Claude Code",
|
|
5381
5440
|
sourceAppRaw: "claude-code",
|
|
5382
|
-
model: "claude-opus-4-
|
|
5441
|
+
model: "claude-opus-4-8",
|
|
5383
5442
|
provider: "anthropic",
|
|
5384
5443
|
hour: 11,
|
|
5385
5444
|
minute: 5,
|
|
@@ -5458,11 +5517,11 @@ var demoInputs = [
|
|
|
5458
5517
|
{
|
|
5459
5518
|
id: "lotr-session-006",
|
|
5460
5519
|
repo: "rivendell-dashboard",
|
|
5461
|
-
title: "Refactor the council-of-elrond planner",
|
|
5520
|
+
title: "Refactor the council-of-elrond planner with Fable 5",
|
|
5462
5521
|
sourceClient: "claude",
|
|
5463
5522
|
sourceApp: "Claude Desktop App",
|
|
5464
5523
|
sourceAppRaw: "claude-desktop-local-agent",
|
|
5465
|
-
model: "claude-
|
|
5524
|
+
model: "claude-fable-5",
|
|
5466
5525
|
provider: "anthropic",
|
|
5467
5526
|
hour: 14,
|
|
5468
5527
|
minute: 15,
|
|
@@ -5594,11 +5653,11 @@ var demoInputs = [
|
|
|
5594
5653
|
{
|
|
5595
5654
|
id: "lotr-session-009",
|
|
5596
5655
|
repo: "minas-tirith-admin",
|
|
5597
|
-
title: "Tighten city-gate roles before the beacons launch",
|
|
5656
|
+
title: "Tighten city-gate roles with Mythos 5 before the beacons launch",
|
|
5598
5657
|
sourceClient: "claude",
|
|
5599
5658
|
sourceApp: "Claude Code",
|
|
5600
5659
|
sourceAppRaw: "claude-code",
|
|
5601
|
-
model: "claude-
|
|
5660
|
+
model: "claude-mythos-5",
|
|
5602
5661
|
provider: "anthropic",
|
|
5603
5662
|
hour: 17,
|
|
5604
5663
|
minute: 5,
|
|
@@ -5750,11 +5809,11 @@ var demoInputs = [
|
|
|
5750
5809
|
{
|
|
5751
5810
|
id: "lotr-session-014",
|
|
5752
5811
|
repo: "shire-mobile",
|
|
5753
|
-
title: "Make pantry sync work offline in Bag End",
|
|
5812
|
+
title: "Make pantry sync work offline in Bag End with Sonnet 4.6",
|
|
5754
5813
|
sourceClient: "claude",
|
|
5755
5814
|
sourceApp: "Claude Code",
|
|
5756
5815
|
sourceAppRaw: "claude-code",
|
|
5757
|
-
model: "claude-
|
|
5816
|
+
model: "claude-sonnet-4-6-20260601",
|
|
5758
5817
|
provider: "anthropic",
|
|
5759
5818
|
hour: 20,
|
|
5760
5819
|
minute: 45,
|
|
@@ -6297,9 +6356,13 @@ function validatePricingBody(body) {
|
|
|
6297
6356
|
outputPerMillion
|
|
6298
6357
|
};
|
|
6299
6358
|
const cachedInputPerMillion = finiteNumber(row.cachedInputPerMillion);
|
|
6359
|
+
const cacheCreationInput5mPerMillion = finiteNumber(row.cacheCreationInput5mPerMillion);
|
|
6360
|
+
const cacheCreationInput1hPerMillion = finiteNumber(row.cacheCreationInput1hPerMillion);
|
|
6300
6361
|
const cacheCreationInputPerMillion = finiteNumber(row.cacheCreationInputPerMillion);
|
|
6301
6362
|
const reasoningOutputPerMillion = finiteNumber(row.reasoningOutputPerMillion);
|
|
6302
6363
|
if (cachedInputPerMillion !== void 0) modelPricing.cachedInputPerMillion = cachedInputPerMillion;
|
|
6364
|
+
if (cacheCreationInput5mPerMillion !== void 0) modelPricing.cacheCreationInput5mPerMillion = cacheCreationInput5mPerMillion;
|
|
6365
|
+
if (cacheCreationInput1hPerMillion !== void 0) modelPricing.cacheCreationInput1hPerMillion = cacheCreationInput1hPerMillion;
|
|
6303
6366
|
if (cacheCreationInputPerMillion !== void 0) modelPricing.cacheCreationInputPerMillion = cacheCreationInputPerMillion;
|
|
6304
6367
|
if (reasoningOutputPerMillion !== void 0) modelPricing.reasoningOutputPerMillion = reasoningOutputPerMillion;
|
|
6305
6368
|
if (typeof row.note === "string") modelPricing.note = row.note;
|
package/docs/data-sources.md
CHANGED
|
@@ -30,6 +30,13 @@ RepoSpend scans:
|
|
|
30
30
|
|
|
31
31
|
Claude Code session transcripts are parsed from JSONL files. Useful fields include `sessionId`, `cwd`, `gitBranch`, `timestamp`, `entrypoint`, `message.model`, and `message.usage`. When `message.usage` is present, RepoSpend sums Claude assistant usage records directly. When token counts or model names are absent in a transcript, sessions remain visible with unknown cost.
|
|
32
32
|
|
|
33
|
+
RepoSpend intentionally includes Claude Desktop/local-agent session roots when
|
|
34
|
+
they exist. Tools such as `ccusage` and Tokscale commonly focus on
|
|
35
|
+
`~/.claude/projects`, so RepoSpend's Claude totals can be higher when local-agent
|
|
36
|
+
sessions are present. In one all-time local audit, RepoSpend's
|
|
37
|
+
`~/.claude/projects` slice matched `ccusage`, while the difference came from
|
|
38
|
+
`~/Library/Application Support/Claude/local-agent-mode-sessions`.
|
|
39
|
+
|
|
33
40
|
RepoSpend checks `~/.claude/history.jsonl` only for source status/counting. History-only entries are not imported into usage analytics because they do not include reliable token, model, or transcript data.
|
|
34
41
|
|
|
35
42
|
## GitHub Copilot
|
package/docs/llms.txt
CHANGED
|
@@ -53,6 +53,7 @@ Positioning:
|
|
|
53
53
|
- RepoSpend complements CLI tools such as ccusage.
|
|
54
54
|
- ccusage is useful for terminal-first Claude Code totals and daily breakdowns.
|
|
55
55
|
- RepoSpend is useful for visual repo-level analytics, session inspection, source/tool comparison, model mix, token shape, cache reuse, exports, and command/agent friction.
|
|
56
|
+
- RepoSpend totals can intentionally differ from ccusage or Tokscale because it includes Claude Desktop/local-agent session roots when present, treats cache reads/writes as input sub-buckets, prices Claude cache writes by recorded 5-minute vs 1-hour TTL, and separates Codex visible output from reasoning output.
|
|
56
57
|
|
|
57
58
|
Cost language:
|
|
58
59
|
- RepoSpend shows estimated API-equivalent cost.
|
package/docs/pricing.md
CHANGED
|
@@ -4,10 +4,24 @@ RepoSpend estimates API-equivalent cost from a local pricing table. The bundled
|
|
|
4
4
|
|
|
5
5
|
The bundled table is seeded from public OpenAI, Anthropic, Google, and GitHub Copilot model references and is expressed as USD per 1M tokens. Pricing changes over time, so treat RepoSpend costs as API-equivalent estimates rather than invoice-grade accounting.
|
|
6
6
|
|
|
7
|
+
The bundled defaults use currently published standard API prices unless a provider has an active introductory API rate. For example, Claude Sonnet 5 uses Anthropic's $2 input and $10 output introductory rate through August 31, 2026, with a pricing-table note for the $3 input and $15 output standard rate that starts September 1, 2026.
|
|
8
|
+
|
|
9
|
+
Some GitHub Copilot model rows are hosted or fine-tuned vendor models such as Raptor mini, MAI-Code-1-Flash, and Kimi K2.7 Code. RepoSpend prices those rows from GitHub's public per-token table as API-equivalent estimates, not as a Copilot invoice.
|
|
10
|
+
|
|
11
|
+
For Claude models, RepoSpend uses Anthropic's cache-write TTL split when local
|
|
12
|
+
Claude Code usage reports expose it. Tokens recorded under
|
|
13
|
+
`cache_creation.ephemeral_5m_input_tokens` use the 5-minute cache-write rate, and
|
|
14
|
+
tokens recorded under `cache_creation.ephemeral_1h_input_tokens` use the 1-hour
|
|
15
|
+
cache-write rate. If a source only exposes the older aggregate
|
|
16
|
+
`cache_creation_input_tokens` field, RepoSpend falls back to the model's generic
|
|
17
|
+
cache-write rate.
|
|
18
|
+
|
|
7
19
|
Each model can define:
|
|
8
20
|
|
|
9
21
|
- input tokens
|
|
10
|
-
- cache write input tokens
|
|
22
|
+
- 5-minute cache write input tokens
|
|
23
|
+
- 1-hour cache write input tokens
|
|
24
|
+
- generic cache write input tokens for sources without a TTL split
|
|
11
25
|
- cached input tokens
|
|
12
26
|
- output tokens
|
|
13
27
|
- reasoning output tokens
|
|
@@ -25,8 +39,20 @@ from visible output before storing and pricing both buckets. If a comparison too
|
|
|
25
39
|
shows output inclusive of reasoning and also prices reasoning separately, its
|
|
26
40
|
cost will be higher because reasoning is counted twice.
|
|
27
41
|
|
|
42
|
+
This is a known reason RepoSpend Codex API-equivalent cost can be lower than a
|
|
43
|
+
tool whose output bucket remains inclusive of reasoning while also exposing a
|
|
44
|
+
separate reasoning bucket. Compare visible output and reasoning separately before
|
|
45
|
+
treating a cost delta as a pricing-table problem.
|
|
46
|
+
|
|
28
47
|
See [token-accounting.md](token-accounting.md) for the detailed comparison model.
|
|
29
48
|
|
|
49
|
+
This can differ from tools or older `ccusage` versions that price all Claude
|
|
50
|
+
cache creation tokens with one cache-write rate. A single-rate calculation can
|
|
51
|
+
understate sessions that mostly used Anthropic's 1-hour cache writes or overstate
|
|
52
|
+
sessions that mostly used 5-minute cache writes. RepoSpend prices the buckets
|
|
53
|
+
recorded in the local Claude transcript instead of forcing every cache write into
|
|
54
|
+
one column.
|
|
55
|
+
|
|
30
56
|
When an exact model id is not present in the pricing table, RepoSpend first tries conservative family matching for known provider naming patterns. For example, a nearby newer Claude Opus 4.x or GPT 5.x variant can inherit the closest older bundled rate so the dashboard stays useful while public rate cards catch up. Inherited rates are labeled in Settings.
|
|
31
57
|
|
|
32
58
|
If RepoSpend cannot resolve a usable rate, it still displays token totals and marks cost as unknown. Unknown pricing does not stop scans, dashboard responses, CLI output, or exports.
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/docs/token-accounting.md
CHANGED
|
@@ -5,6 +5,7 @@ RepoSpend uses one normalized token shape across local clients:
|
|
|
5
5
|
- `inputTokens` is total input for the request/session. When a source reports cache reads or cache writes separately, RepoSpend includes them in `inputTokens` and also stores them in sub-buckets.
|
|
6
6
|
- `cachedInputTokens` is the cache-read portion of input.
|
|
7
7
|
- `cacheCreationInputTokens` is the cache-write portion of input, when the source exposes it.
|
|
8
|
+
- `cacheCreationInputTokens5m` and `cacheCreationInputTokens1h` preserve Anthropic's Claude cache-write TTL split when local transcripts expose `usage.cache_creation`.
|
|
8
9
|
- `outputTokens` is visible/non-reasoning output.
|
|
9
10
|
- `reasoningTokens` is reasoning output, when the source exposes it.
|
|
10
11
|
- `totalTokens = inputTokens + outputTokens + reasoningTokens`.
|
|
@@ -37,6 +38,39 @@ cache write input = cacheCreationInputTokens
|
|
|
37
38
|
|
|
38
39
|
Adding cached tokens again would inflate token totals and double-charge cached input in API-equivalent cost.
|
|
39
40
|
|
|
41
|
+
## Claude Source Scope
|
|
42
|
+
|
|
43
|
+
RepoSpend scans both Claude project transcripts and Claude Desktop/local-agent
|
|
44
|
+
session roots:
|
|
45
|
+
|
|
46
|
+
```text
|
|
47
|
+
~/.claude/projects
|
|
48
|
+
~/.config/claude/projects
|
|
49
|
+
~/Library/Application Support/Claude/local-agent-mode-sessions
|
|
50
|
+
~/.config/Claude/local-agent-mode-sessions
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Many terminal-first usage tools focus on `~/.claude/projects`. If local-agent
|
|
54
|
+
session files exist, RepoSpend can show higher Claude totals than `ccusage` or
|
|
55
|
+
Tokscale without a parser bug. When comparing tools, first check whether the
|
|
56
|
+
same Claude roots are included.
|
|
57
|
+
|
|
58
|
+
For Claude, RepoSpend also preserves the cache-write TTL split when it is present
|
|
59
|
+
in local logs:
|
|
60
|
+
|
|
61
|
+
```text
|
|
62
|
+
5-minute cache write = cacheCreationInputTokens5m
|
|
63
|
+
1-hour cache write = cacheCreationInputTokens1h
|
|
64
|
+
unclassified cache write = cacheCreationInputTokens - cacheCreationInputTokens5m - cacheCreationInputTokens1h
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
This is one reason RepoSpend API-equivalent cost can differ from `ccusage`.
|
|
68
|
+
Anthropic prices 5-minute cache writes at a different rate than 1-hour cache
|
|
69
|
+
writes. RepoSpend applies the recorded TTL-specific rate for each bucket; tools
|
|
70
|
+
that collapse all Claude cache creation tokens into a single cache-write column
|
|
71
|
+
can drift when a session mixes TTLs or when the chosen single rate does not match
|
|
72
|
+
the session's actual cache usage.
|
|
73
|
+
|
|
40
74
|
## Reasoning Tokens
|
|
41
75
|
|
|
42
76
|
RepoSpend keeps reasoning tokens separate when the local source exposes them. The accurate treatment depends on the source's raw usage shape:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "repospend",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Local-first dashboard for tracking AI coding token usage and API-equivalent spend by repository, session, model, and tool.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "Mehmet Mustafa Demir",
|
|
@@ -45,7 +45,9 @@
|
|
|
45
45
|
"typecheck": "pnpm --filter @repospend/types --filter @repospend/core build && pnpm --stream --filter @repospend/types --filter @repospend/core --filter @repospend/server --filter @repospend/web typecheck",
|
|
46
46
|
"clean": "pnpm -r clean && rm -rf dist web-dist",
|
|
47
47
|
"rebuild:native": "pnpm rebuild better-sqlite3",
|
|
48
|
-
"doctor:native": "node scripts/check-native.mjs"
|
|
48
|
+
"doctor:native": "node scripts/check-native.mjs",
|
|
49
|
+
"media:capture": "node scripts/capture-release-media.mjs",
|
|
50
|
+
"media:producthunt": "node scripts/generate-producthunt-media.mjs"
|
|
49
51
|
},
|
|
50
52
|
"keywords": [
|
|
51
53
|
"codex",
|
|
@@ -81,6 +83,9 @@
|
|
|
81
83
|
"@types/node": "^22.19.1",
|
|
82
84
|
"esbuild": "^0.28.0",
|
|
83
85
|
"eslint": "^9.39.1",
|
|
86
|
+
"gifenc": "^1.0.3",
|
|
87
|
+
"playwright": "^1.60.0",
|
|
88
|
+
"sharp": "^0.35.1",
|
|
84
89
|
"typescript": "^5.9.3",
|
|
85
90
|
"typescript-eslint": "^8.46.4",
|
|
86
91
|
"vitest": "^3.2.4"
|