repospend 0.1.2 → 0.1.3
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 +7 -0
- package/README.md +1 -1
- package/dist/cli.js +103 -31
- package/docs/pricing.md +4 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to RepoSpend will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## 0.1.3
|
|
6
|
+
|
|
7
|
+
- Correct GPT-5.6 Terra and Luna API-equivalent pricing to the reduced July 30, 2026 OpenAI rates, including cached-input and cache-write rates.
|
|
8
|
+
- Migrate legacy full pricing files so the new Terra and Luna defaults take effect while preserving sparse user overrides and custom model rows.
|
|
9
|
+
- Improve unverified repo and folder grouping by using the outermost project marker below a safe shared-folder boundary, keeping nested non-Git workspace paths together without collapsing them into broad folders such as `Desktop`.
|
|
10
|
+
- Show paths relative to the home directory for unverified workspace labels so similarly named folders such as `src` remain distinguishable.
|
|
11
|
+
|
|
5
12
|
## 0.1.2
|
|
6
13
|
|
|
7
14
|
- Add bundled GPT-5.6 API-equivalent pricing for the Sol, Terra, and Luna tiers, including cache-write and cached-input rates.
|
package/README.md
CHANGED
|
@@ -125,7 +125,7 @@ RepoSpend helps you break down local AI coding usage by:
|
|
|
125
125
|
- input, cached input, output, and reasoning token shape
|
|
126
126
|
- estimated API-equivalent cost
|
|
127
127
|
|
|
128
|
-
Nested paths are grouped by Git root so usage rolls up to the repository.
|
|
128
|
+
Nested paths are grouped by Git root so usage rolls up to the repository. When no Git root is available, RepoSpend uses the outermost project marker below a safe shared-folder boundary, such as `package.json`, and keeps the grouping visibly unverified.
|
|
129
129
|
|
|
130
130
|
## Screenshots
|
|
131
131
|
|
package/dist/cli.js
CHANGED
|
@@ -324,18 +324,19 @@ var pricingInfo = {
|
|
|
324
324
|
sourceUrls: [
|
|
325
325
|
{ label: "OpenAI pricing reference", url: "https://developers.openai.com/api/docs/pricing" },
|
|
326
326
|
{ label: "OpenAI GPT-5.6 preview pricing", url: "https://openai.com/index/previewing-gpt-5-6-sol/" },
|
|
327
|
+
{ label: "OpenAI GPT-5.6 price update", url: "https://openai.com/index/advancing-the-price-performance-frontier-with-gpt-5-6/" },
|
|
327
328
|
{ label: "Claude pricing reference", url: "https://platform.claude.com/docs/en/about-claude/pricing" },
|
|
328
329
|
{ label: "GitHub Copilot model pricing reference", url: "https://docs.github.com/en/copilot/reference/copilot-billing/models-and-pricing" }
|
|
329
330
|
],
|
|
330
331
|
unit: "USD per 1M tokens",
|
|
331
|
-
updatedAt: "2026-07-
|
|
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."
|
|
332
|
+
updatedAt: "2026-07-30",
|
|
333
|
+
note: "RepoSpend estimates API-equivalent cost from local token counts and public API-style Standard pricing. GPT-5.6 Terra and Luna use the reduced OpenAI API rates effective July 30, 2026. This is not your actual bill; subscriptions, credits, provider terms, cache behavior, regional processing, or other billing factors can make your real cost different."
|
|
333
334
|
};
|
|
334
335
|
var defaultPricing = {
|
|
335
336
|
"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
337
|
"gpt-5.6-sol": { inputPerMillion: 5, cachedInputPerMillion: 0.5, cacheCreationInputPerMillion: 6.25, outputPerMillion: 30, reasoningOutputPerMillion: 30 },
|
|
337
|
-
"gpt-5.6-terra": { inputPerMillion: 2
|
|
338
|
-
"gpt-5.6-luna": { inputPerMillion:
|
|
338
|
+
"gpt-5.6-terra": { inputPerMillion: 2, cachedInputPerMillion: 0.2, cacheCreationInputPerMillion: 2.5, outputPerMillion: 12, reasoningOutputPerMillion: 12, note: "GPT-5.6 Terra reduced API pricing effective July 30, 2026. Cache reads are 90% below input and cache writes are 1.25x input." },
|
|
339
|
+
"gpt-5.6-luna": { inputPerMillion: 0.2, cachedInputPerMillion: 0.02, cacheCreationInputPerMillion: 0.25, outputPerMillion: 1.2, reasoningOutputPerMillion: 1.2, note: "GPT-5.6 Luna reduced API pricing effective July 30, 2026. Cache reads are 90% below input and cache writes are 1.25x input." },
|
|
339
340
|
"gpt-5.5": { inputPerMillion: 5, cachedInputPerMillion: 0.5, outputPerMillion: 30, reasoningOutputPerMillion: 30 },
|
|
340
341
|
"gpt-5.5-pro": { inputPerMillion: 30, outputPerMillion: 180, reasoningOutputPerMillion: 180 },
|
|
341
342
|
"gpt-5.4": { inputPerMillion: 2.5, cachedInputPerMillion: 0.25, outputPerMillion: 15, reasoningOutputPerMillion: 15 },
|
|
@@ -386,17 +387,44 @@ var defaultPricing = {
|
|
|
386
387
|
"claude-haiku-4-5": { inputPerMillion: 1, cacheCreationInput5mPerMillion: 1.25, cacheCreationInput1hPerMillion: 2, cacheCreationInputPerMillion: 2, cachedInputPerMillion: 0.1, outputPerMillion: 5 },
|
|
387
388
|
"claude-3-5-haiku": { inputPerMillion: 0.8, cacheCreationInput5mPerMillion: 1, cacheCreationInput1hPerMillion: 1.6, cacheCreationInputPerMillion: 1.6, cachedInputPerMillion: 0.08, outputPerMillion: 4 }
|
|
388
389
|
};
|
|
390
|
+
var legacyBundledPricing = {
|
|
391
|
+
"gpt-5.6-terra": { inputPerMillion: 2.5, cachedInputPerMillion: 0.25, cacheCreationInputPerMillion: 3.125, outputPerMillion: 15, reasoningOutputPerMillion: 15 },
|
|
392
|
+
"gpt-5.6-luna": { inputPerMillion: 1, cachedInputPerMillion: 0.1, cacheCreationInputPerMillion: 1.25, outputPerMillion: 6, reasoningOutputPerMillion: 6 }
|
|
393
|
+
};
|
|
389
394
|
function loadPricingTable(pricingPath) {
|
|
390
395
|
if (!pricingPath) {
|
|
391
396
|
return defaultPricing;
|
|
392
397
|
}
|
|
393
398
|
try {
|
|
394
399
|
const resolved = path.resolve(pricingPath);
|
|
395
|
-
|
|
400
|
+
const stored = JSON.parse(fs.readFileSync(resolved, "utf8"));
|
|
401
|
+
return { ...defaultPricing, ...migrateLegacyBundledPricing(stored) };
|
|
396
402
|
} catch {
|
|
397
403
|
return defaultPricing;
|
|
398
404
|
}
|
|
399
405
|
}
|
|
406
|
+
function pricingOverrides(pricing) {
|
|
407
|
+
return Object.fromEntries(
|
|
408
|
+
Object.entries(pricing).filter(([model, modelPricing]) => {
|
|
409
|
+
const bundled = defaultPricing[model];
|
|
410
|
+
return !bundled || !pricingEntriesEqual(modelPricing, bundled);
|
|
411
|
+
})
|
|
412
|
+
);
|
|
413
|
+
}
|
|
414
|
+
function migrateLegacyBundledPricing(stored) {
|
|
415
|
+
const isLegacyFullTable = Object.keys(defaultPricing).every((model) => Object.hasOwn(stored, model));
|
|
416
|
+
if (!isLegacyFullTable) return stored;
|
|
417
|
+
const migrated = { ...stored };
|
|
418
|
+
for (const [model, legacyPricing] of Object.entries(legacyBundledPricing)) {
|
|
419
|
+
if (pricingEntriesEqual(migrated[model], legacyPricing)) delete migrated[model];
|
|
420
|
+
}
|
|
421
|
+
return pricingOverrides(migrated);
|
|
422
|
+
}
|
|
423
|
+
function pricingEntriesEqual(left, right) {
|
|
424
|
+
if (!left || !right) return left === right;
|
|
425
|
+
const keys = /* @__PURE__ */ new Set([...Object.keys(left), ...Object.keys(right)]);
|
|
426
|
+
return [...keys].every((key) => left[key] === right[key]);
|
|
427
|
+
}
|
|
400
428
|
function resolvePricingPath(config = {}) {
|
|
401
429
|
return config.pricingPath ? path.resolve(repospendHome(), config.pricingPath) : path.join(repospendHome(), "pricing.json");
|
|
402
430
|
}
|
|
@@ -1235,6 +1263,7 @@ function isDashboardVisibleSession(session) {
|
|
|
1235
1263
|
|
|
1236
1264
|
// packages/core/src/repo.ts
|
|
1237
1265
|
import fs4 from "node:fs";
|
|
1266
|
+
import os2 from "node:os";
|
|
1238
1267
|
import path4 from "node:path";
|
|
1239
1268
|
function normalizeRoot(p) {
|
|
1240
1269
|
const stripped = p.startsWith("\\\\?\\") ? p.slice(4) : p;
|
|
@@ -1264,9 +1293,9 @@ function findGitRoot(cwd, fileSystem = fs4) {
|
|
|
1264
1293
|
function resolveRepoInfo(cwd, config = {}, fileSystem = fs4) {
|
|
1265
1294
|
const warnings = [];
|
|
1266
1295
|
const gitRoot = safeFindGitRoot(cwd, fileSystem);
|
|
1267
|
-
const repoRoot = gitRoot ?? fallbackWorkspaceRoot(cwd);
|
|
1296
|
+
const repoRoot = gitRoot ?? fallbackWorkspaceRoot(cwd, fileSystem);
|
|
1268
1297
|
const alias = aliasForPath(repoRoot, config);
|
|
1269
|
-
const repoName = alias ?? (path4.basename(repoRoot)
|
|
1298
|
+
const repoName = alias ?? (gitRoot ? path4.basename(repoRoot) : fallbackWorkspaceLabel(repoRoot));
|
|
1270
1299
|
const gitMeta = gitRoot ? readGitMetadata(gitRoot, fileSystem) : { remote: void 0, branch: void 0 };
|
|
1271
1300
|
if (!gitRoot) {
|
|
1272
1301
|
warnings.push("repo_unverified_no_git_root");
|
|
@@ -1287,16 +1316,59 @@ function safeFindGitRoot(cwd, fileSystem) {
|
|
|
1287
1316
|
return void 0;
|
|
1288
1317
|
}
|
|
1289
1318
|
}
|
|
1290
|
-
|
|
1319
|
+
var workspaceMarkers = [
|
|
1320
|
+
"package.json",
|
|
1321
|
+
"pyproject.toml",
|
|
1322
|
+
"Cargo.toml",
|
|
1323
|
+
"go.mod",
|
|
1324
|
+
"mix.exs",
|
|
1325
|
+
"composer.json",
|
|
1326
|
+
"Gemfile",
|
|
1327
|
+
"pom.xml",
|
|
1328
|
+
"build.gradle",
|
|
1329
|
+
"Package.swift",
|
|
1330
|
+
"CMakeLists.txt"
|
|
1331
|
+
];
|
|
1332
|
+
var fallbackWorkspaceCache = /* @__PURE__ */ new Map();
|
|
1333
|
+
function fallbackWorkspaceRoot(cwd, fileSystem = fs4) {
|
|
1291
1334
|
const resolved = normalizeRoot(path4.resolve(cwd || process.cwd()));
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1335
|
+
if (fileSystem === fs4) {
|
|
1336
|
+
const cached = fallbackWorkspaceCache.get(resolved);
|
|
1337
|
+
if (cached) return cached;
|
|
1338
|
+
}
|
|
1339
|
+
const root = findWorkspaceMarkerRoot(resolved, fileSystem) ?? resolved;
|
|
1340
|
+
if (fileSystem === fs4) fallbackWorkspaceCache.set(resolved, root);
|
|
1341
|
+
return root;
|
|
1342
|
+
}
|
|
1343
|
+
function findWorkspaceMarkerRoot(start, fileSystem) {
|
|
1344
|
+
const home = normalizeRoot(path4.resolve(os2.homedir()));
|
|
1345
|
+
const boundary = isWithin(start, home) ? home : path4.parse(start).root;
|
|
1346
|
+
let candidate;
|
|
1347
|
+
let current = start;
|
|
1348
|
+
while (true) {
|
|
1349
|
+
if (current !== boundary && isAllowedMarkerRoot(current, boundary) && workspaceMarkers.some((marker) => fileSystem.existsSync(path4.join(current, marker)))) {
|
|
1350
|
+
candidate = current;
|
|
1351
|
+
}
|
|
1352
|
+
if (current === boundary) return candidate;
|
|
1353
|
+
const parent = path4.dirname(current);
|
|
1354
|
+
if (parent === current) return candidate;
|
|
1355
|
+
current = parent;
|
|
1297
1356
|
}
|
|
1298
|
-
|
|
1299
|
-
|
|
1357
|
+
}
|
|
1358
|
+
function isAllowedMarkerRoot(candidate, boundary) {
|
|
1359
|
+
const relative = path4.relative(boundary, candidate).split(path4.sep).filter(Boolean);
|
|
1360
|
+
return relative.length > 1;
|
|
1361
|
+
}
|
|
1362
|
+
function isWithin(candidate, parent) {
|
|
1363
|
+
return candidate === parent || candidate.startsWith(`${parent}${path4.sep}`);
|
|
1364
|
+
}
|
|
1365
|
+
function fallbackWorkspaceLabel(repoRoot) {
|
|
1366
|
+
const home = normalizeRoot(path4.resolve(os2.homedir()));
|
|
1367
|
+
const relativeToHome = path4.relative(home, repoRoot);
|
|
1368
|
+
if (relativeToHome && !relativeToHome.startsWith("..") && !path4.isAbsolute(relativeToHome)) {
|
|
1369
|
+
return relativeToHome.split(path4.sep).filter(Boolean).join("/");
|
|
1370
|
+
}
|
|
1371
|
+
return path4.basename(repoRoot) || "unknown-workspace";
|
|
1300
1372
|
}
|
|
1301
1373
|
function readGitMetadata(repoRoot, fileSystem) {
|
|
1302
1374
|
const gitPath = path4.join(repoRoot, ".git");
|
|
@@ -1331,7 +1403,7 @@ function parseBranch(head) {
|
|
|
1331
1403
|
// packages/core/src/sources/codex.ts
|
|
1332
1404
|
import { spawnSync } from "node:child_process";
|
|
1333
1405
|
import fs5 from "node:fs";
|
|
1334
|
-
import
|
|
1406
|
+
import os3 from "node:os";
|
|
1335
1407
|
import path5 from "node:path";
|
|
1336
1408
|
import { fileURLToPath } from "node:url";
|
|
1337
1409
|
import Database from "better-sqlite3";
|
|
@@ -1856,7 +1928,7 @@ function numberFromKeys(object, keys) {
|
|
|
1856
1928
|
var codexSessionCacheVersion = "codex-session-v1";
|
|
1857
1929
|
var nativeSqliteRebuildAttempted = false;
|
|
1858
1930
|
function scanCodex(options) {
|
|
1859
|
-
const codexHome = options.codexHome ?? path5.join(
|
|
1931
|
+
const codexHome = options.codexHome ?? path5.join(os3.homedir(), ".codex");
|
|
1860
1932
|
const statePath = path5.join(codexHome, "state_5.sqlite");
|
|
1861
1933
|
const sessionsPath = path5.join(codexHome, "sessions");
|
|
1862
1934
|
const configPath2 = path5.join(codexHome, "config.toml");
|
|
@@ -1938,7 +2010,7 @@ function readThreads(statePath, sessionFiles, options, warnings) {
|
|
|
1938
2010
|
warnings.push("Codex threads table has no recognized columns.");
|
|
1939
2011
|
return [];
|
|
1940
2012
|
}
|
|
1941
|
-
const importedModels = readImportedModelMap(options.codexHome ?? path5.join(
|
|
2013
|
+
const importedModels = readImportedModelMap(options.codexHome ?? path5.join(os3.homedir(), ".codex"), warnings);
|
|
1942
2014
|
const rows = db.prepare(`SELECT ${selectedColumns.map((column) => `"${column}"`).join(", ")} FROM threads`).all().filter((row) => rowMatchesScanWindow(row, options.scanWindow));
|
|
1943
2015
|
const sessions = rows.map((row, index) => threadToUsage(row, index, sessionFiles, options, importedModels));
|
|
1944
2016
|
return attributeSubagentsToParentSurfaces(sessions, rows);
|
|
@@ -2095,7 +2167,7 @@ function threadToUsage(row, index, sessionFiles, options, importedModels = /* @_
|
|
|
2095
2167
|
messageCount: sessionBreakdown.messageCount,
|
|
2096
2168
|
rawTokenTotal: dbTokens || sessionBreakdown.totalTokens || void 0,
|
|
2097
2169
|
warnings,
|
|
2098
|
-
codexHome: options.codexHome ?? path5.join(
|
|
2170
|
+
codexHome: options.codexHome ?? path5.join(os3.homedir(), ".codex"),
|
|
2099
2171
|
durationMs: durationMs(startedAt, endedAt),
|
|
2100
2172
|
rawEventCount: sessionBreakdown.rawEventCount,
|
|
2101
2173
|
parseStatus: sessionBreakdown.parseStatus,
|
|
@@ -2169,7 +2241,7 @@ function readStandaloneSessions(sessionFiles, options) {
|
|
|
2169
2241
|
messageCount: breakdown.messageCount,
|
|
2170
2242
|
rawTokenTotal: breakdown.totalTokens || void 0,
|
|
2171
2243
|
warnings: [...repo.warnings, ...breakdown.warnings],
|
|
2172
|
-
codexHome: options.codexHome ?? path5.join(
|
|
2244
|
+
codexHome: options.codexHome ?? path5.join(os3.homedir(), ".codex"),
|
|
2173
2245
|
durationMs: durationMs(breakdown.startedAt, breakdown.endedAt),
|
|
2174
2246
|
rawEventCount: breakdown.rawEventCount,
|
|
2175
2247
|
parseStatus: breakdown.parseStatus,
|
|
@@ -2680,7 +2752,7 @@ function errorMessage(error) {
|
|
|
2680
2752
|
|
|
2681
2753
|
// packages/core/src/sources/claude.ts
|
|
2682
2754
|
import fs6 from "node:fs";
|
|
2683
|
-
import
|
|
2755
|
+
import os4 from "node:os";
|
|
2684
2756
|
import path6 from "node:path";
|
|
2685
2757
|
function scanClaude(options) {
|
|
2686
2758
|
const claudeHome = options.claudeHome ?? defaultClaudeHome();
|
|
@@ -2734,7 +2806,7 @@ function scanClaude(options) {
|
|
|
2734
2806
|
function discoverClaudeSessionFiles(claudeHome, warnings, explicitHome) {
|
|
2735
2807
|
const files = [];
|
|
2736
2808
|
const projectDirs = /* @__PURE__ */ new Set();
|
|
2737
|
-
const projectsPaths = explicitHome ? [path6.join(claudeHome, "projects")] : unique3([...configuredClaudeHomes(claudeHome).map((root) => path6.join(root, "projects")), path6.join(
|
|
2809
|
+
const projectsPaths = explicitHome ? [path6.join(claudeHome, "projects")] : unique3([...configuredClaudeHomes(claudeHome).map((root) => path6.join(root, "projects")), path6.join(os4.homedir(), ".config", "claude", "projects")]);
|
|
2738
2810
|
for (const projectsPath of projectsPaths) {
|
|
2739
2811
|
collectProjectFiles(projectsPath, files, projectDirs, warnings);
|
|
2740
2812
|
}
|
|
@@ -3248,10 +3320,10 @@ function claudeSourceAppLabel(entrypoint) {
|
|
|
3248
3320
|
return "Claude Code";
|
|
3249
3321
|
}
|
|
3250
3322
|
function defaultClaudeHome() {
|
|
3251
|
-
return process.env.CLAUDE_CONFIG_DIR?.split(",").map((item) => item.trim()).filter(Boolean)[0] ?? path6.join(
|
|
3323
|
+
return process.env.CLAUDE_CONFIG_DIR?.split(",").map((item) => item.trim()).filter(Boolean)[0] ?? path6.join(os4.homedir(), ".claude");
|
|
3252
3324
|
}
|
|
3253
3325
|
function defaultDesktopSessionRoots() {
|
|
3254
|
-
const home =
|
|
3326
|
+
const home = os4.homedir();
|
|
3255
3327
|
const xdg = process.env.XDG_CONFIG_HOME || path6.join(home, ".config");
|
|
3256
3328
|
return [
|
|
3257
3329
|
path6.join(home, "Library", "Application Support", "Claude", "local-agent-mode-sessions"),
|
|
@@ -3342,12 +3414,12 @@ function safeMtimeMs(filePath) {
|
|
|
3342
3414
|
|
|
3343
3415
|
// packages/core/src/sources/copilot.ts
|
|
3344
3416
|
import fs7 from "node:fs";
|
|
3345
|
-
import
|
|
3417
|
+
import os5 from "node:os";
|
|
3346
3418
|
import path7 from "node:path";
|
|
3347
3419
|
var maxJsonlBytes = 50 * 1024 * 1024;
|
|
3348
3420
|
var copilotOtelEnv = "COPILOT_OTEL_FILE_EXPORTER_PATH";
|
|
3349
3421
|
function scanCopilot(options) {
|
|
3350
|
-
const copilotHome = options.copilotHome ?? path7.join(
|
|
3422
|
+
const copilotHome = options.copilotHome ?? path7.join(os5.homedir(), ".copilot");
|
|
3351
3423
|
const discovery = discoverCopilotFiles(copilotHome, Boolean(options.copilotHome), options.codeUserRoots);
|
|
3352
3424
|
const files = discovery.files.filter((file) => fileMayOverlapScanWindow2(file.filePath, options.scanWindow));
|
|
3353
3425
|
const sessionMap = /* @__PURE__ */ new Map();
|
|
@@ -4016,7 +4088,7 @@ function timestampMs2(value) {
|
|
|
4016
4088
|
return iso ? Date.parse(iso) : void 0;
|
|
4017
4089
|
}
|
|
4018
4090
|
function defaultCodeUserRoots() {
|
|
4019
|
-
const home =
|
|
4091
|
+
const home = os5.homedir();
|
|
4020
4092
|
if (process.platform === "win32") {
|
|
4021
4093
|
const appData = process.env.APPDATA ?? path7.join(home, "AppData", "Roaming");
|
|
4022
4094
|
return ["Code", "Code - Insiders", "VSCodium"].map((name) => path7.join(appData, name, "User"));
|
|
@@ -4151,7 +4223,7 @@ function errorMessage3(error) {
|
|
|
4151
4223
|
|
|
4152
4224
|
// packages/core/src/sources/cursor.ts
|
|
4153
4225
|
import fs8 from "node:fs";
|
|
4154
|
-
import
|
|
4226
|
+
import os6 from "node:os";
|
|
4155
4227
|
import path8 from "node:path";
|
|
4156
4228
|
import Database2 from "better-sqlite3";
|
|
4157
4229
|
var parserVersion = "cursor-v1";
|
|
@@ -4160,7 +4232,7 @@ var maxDatabaseBytes = 100 * 1024 * 1024;
|
|
|
4160
4232
|
var cursorStoragePrefixes = ["cursor.", "anysphere."];
|
|
4161
4233
|
var interestingTerms = ["cursor", "composer", "aichat", "ai_chat", "aiservice", "agent-transcript", "prompttokenbreakdown"];
|
|
4162
4234
|
function scanCursor(options) {
|
|
4163
|
-
const cursorHome = options.cursorHome ?? path8.join(
|
|
4235
|
+
const cursorHome = options.cursorHome ?? path8.join(os6.homedir(), ".cursor");
|
|
4164
4236
|
const discovery = discoverCursorFiles(cursorHome, Boolean(options.cursorHome));
|
|
4165
4237
|
const jsonlFiles = discovery.jsonlFiles.filter((filePath) => fileMayOverlapScanWindow3(filePath, options.scanWindow));
|
|
4166
4238
|
const databaseFiles = discovery.databaseFiles.filter((filePath) => fileMayOverlapScanWindow3(filePath, options.scanWindow));
|
|
@@ -4276,7 +4348,7 @@ function cursorOwnedChildDirs(root) {
|
|
|
4276
4348
|
}
|
|
4277
4349
|
}
|
|
4278
4350
|
function defaultCursorAppPaths() {
|
|
4279
|
-
const home =
|
|
4351
|
+
const home = os6.homedir();
|
|
4280
4352
|
if (process.platform === "win32") {
|
|
4281
4353
|
const appData = process.env.APPDATA ?? path8.join(home, "AppData", "Roaming");
|
|
4282
4354
|
const globalStoragePath2 = path8.join(appData, "Cursor", "User", "globalStorage");
|
|
@@ -5116,7 +5188,7 @@ function writeConfigData(nextConfig) {
|
|
|
5116
5188
|
function writePricingData(models) {
|
|
5117
5189
|
const config = loadConfig();
|
|
5118
5190
|
const pricingPath = resolvePricingPath(config);
|
|
5119
|
-
savePricingTable(pricingPath, models);
|
|
5191
|
+
savePricingTable(pricingPath, pricingOverrides(models));
|
|
5120
5192
|
clearScanCache();
|
|
5121
5193
|
return {
|
|
5122
5194
|
path: pricingPath,
|
package/docs/pricing.md
CHANGED
|
@@ -6,6 +6,10 @@ The bundled table is seeded from public OpenAI, Anthropic, Google, and GitHub Co
|
|
|
6
6
|
|
|
7
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
8
|
|
|
9
|
+
OpenAI reduced GPT-5.6 pricing effective July 30, 2026. RepoSpend uses $2 input and $12 output per 1M tokens for Terra, and $0.20 input and $1.20 output for Luna. Cached input remains 90% below the uncached input rate, and cache writes are billed at 1.25x the uncached input rate, so Luna's bundled cache-write rate is $0.25 per 1M tokens.
|
|
10
|
+
|
|
11
|
+
The Settings page persists only custom or intentionally edited model rows. This lets future bundled rate updates flow through automatically. Older full pricing files are migrated for the GPT-5.6 Terra and Luna reductions when they still contain the previous bundled rates.
|
|
12
|
+
|
|
9
13
|
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
14
|
|
|
11
15
|
For Claude models, RepoSpend uses Anthropic's cache-write TTL split when local
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "repospend",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
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",
|