exe-watcher 0.2.66 → 0.2.67

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.
Files changed (3) hide show
  1. package/README.md +6 -4
  2. package/dist/cli.js +483 -295
  3. package/package.json +2 -2
package/dist/cli.js CHANGED
@@ -2125,12 +2125,12 @@ function getPricingWarnings() {
2125
2125
  }
2126
2126
  function getPricingMetadata() {
2127
2127
  return {
2128
- basis: "latest-rates",
2128
+ basis: "recorded-rates",
2129
2129
  currency: "USD",
2130
2130
  refreshedAt: _pricingLastUpdated ? new Date(_pricingLastUpdated).toISOString() : null,
2131
2131
  fallbackVerifiedOn: verified_pricing_default.verifiedOn,
2132
2132
  sources: OFFICIAL_PRICING_SOURCES,
2133
- note: "Historical and current usage are valued at the latest available API list rates. This is an API-equivalent estimate, not a reconstruction of past invoices."
2133
+ note: "Counted usage keeps its recorded API-equivalent cost. Latest available rates apply to newly observed usage; price refreshes do not revalue saved history. This is an estimate, not a reconstruction of invoices."
2134
2134
  };
2135
2135
  }
2136
2136
  function getCachePath() {
@@ -2690,12 +2690,186 @@ var init_discovery_cache = __esm({
2690
2690
  }
2691
2691
  });
2692
2692
 
2693
+ // src/sqlite.ts
2694
+ import { createRequire } from "module";
2695
+ function loadDriver() {
2696
+ if (loadAttempted) return DatabaseSync !== null;
2697
+ loadAttempted = true;
2698
+ const origEmit = process.emit.bind(process);
2699
+ let restored = false;
2700
+ const restore = () => {
2701
+ if (restored) return;
2702
+ restored = true;
2703
+ process.emit = origEmit;
2704
+ };
2705
+ process.emit = function patchedEmit(event, ...args) {
2706
+ if (event === "warning") {
2707
+ const warning = args[0];
2708
+ if (warning?.name === "ExperimentalWarning" && typeof warning.message === "string" && /SQLite/i.test(warning.message)) {
2709
+ return false;
2710
+ }
2711
+ }
2712
+ return origEmit.call(this, event, ...args);
2713
+ };
2714
+ try {
2715
+ const mod = requireForSqlite("node:sqlite");
2716
+ DatabaseSync = mod.DatabaseSync;
2717
+ return true;
2718
+ } catch (err) {
2719
+ const message = err instanceof Error ? err.message : String(err);
2720
+ loadError = `Watcher saved costs and SQLite-based providers need Node 22.13+ (Node 23 requires 23.4+) with node:sqlite.
2721
+ Current Node: ${process.version}.
2722
+ Upgrade Node (https://nodejs.org) and run exe-watcher again.
2723
+ (underlying error: ${message})`;
2724
+ return false;
2725
+ } finally {
2726
+ setTimeout(restore, 0);
2727
+ }
2728
+ }
2729
+ function isSqliteAvailable() {
2730
+ return loadDriver();
2731
+ }
2732
+ function getSqliteLoadError() {
2733
+ return loadError ?? "SQLite driver not available";
2734
+ }
2735
+ function openDatabase(path) {
2736
+ if (!loadDriver() || DatabaseSync === null) {
2737
+ throw new Error(getSqliteLoadError());
2738
+ }
2739
+ const db = new DatabaseSync(path, { readOnly: true });
2740
+ return {
2741
+ query(sql, params = []) {
2742
+ const rows2 = [];
2743
+ let bytes = 0;
2744
+ for (const row of db.prepare(sql).iterate(...params)) {
2745
+ const size = Buffer.byteLength(JSON.stringify(row));
2746
+ bytes += size;
2747
+ if (rows2.length >= 1e5 || bytes > 32 * 1024 * 1024) throw new ResourceBudgetError("SQLite result budget");
2748
+ chargeRead(size);
2749
+ rows2.push(row);
2750
+ }
2751
+ return rows2;
2752
+ },
2753
+ close() {
2754
+ db.close();
2755
+ }
2756
+ };
2757
+ }
2758
+ var requireForSqlite, DatabaseSync, loadAttempted, loadError;
2759
+ var init_sqlite = __esm({
2760
+ "src/sqlite.ts"() {
2761
+ "use strict";
2762
+ init_resource_budget();
2763
+ requireForSqlite = createRequire(import.meta.url);
2764
+ DatabaseSync = null;
2765
+ loadAttempted = false;
2766
+ loadError = null;
2767
+ }
2768
+ });
2769
+
2770
+ // src/recorded-costs.ts
2771
+ import { createHash as createHash2 } from "crypto";
2772
+ import { createRequire as createRequire2 } from "module";
2773
+ import { chmodSync, existsSync, lstatSync, mkdirSync } from "fs";
2774
+ import { join as join9 } from "path";
2775
+ function database() {
2776
+ const directory = getCacheDir();
2777
+ const path = join9(directory, "recorded-costs.sqlite");
2778
+ if (opened?.path === path && existsSync(path)) return opened.db;
2779
+ opened?.db.close();
2780
+ opened = void 0;
2781
+ if (!isSqliteAvailable()) throw new Error(getSqliteLoadError());
2782
+ mkdirSync(directory, { recursive: true, mode: 448 });
2783
+ if (existsSync(path) && !lstatSync(path).isFile()) throw new Error("Watcher pricing ledger must be a regular file");
2784
+ const { DatabaseSync: DatabaseSync2 } = require2("node:sqlite");
2785
+ const db = new DatabaseSync2(path);
2786
+ chmodSync(path, 384);
2787
+ db.exec("PRAGMA busy_timeout=1000; PRAGMA cache_size=-1024; PRAGMA journal_mode=WAL; PRAGMA synchronous=NORMAL;");
2788
+ const pageSize = Number(db.prepare("PRAGMA page_size").get().page_size);
2789
+ db.exec(`PRAGMA max_page_count=${Math.floor(512 * 1024 * 1024 / pageSize)}`);
2790
+ db.exec("CREATE TABLE IF NOT EXISTS costs (id BLOB PRIMARY KEY, usd REAL NOT NULL CHECK(usd >= 0)) WITHOUT ROWID");
2791
+ opened = { path, db };
2792
+ return db;
2793
+ }
2794
+ function costRow(value) {
2795
+ if (!value || typeof value !== "object") return null;
2796
+ const record = value;
2797
+ const { provider, model, deduplicationKey, costUSD } = record;
2798
+ if (typeof provider !== "string" || typeof model !== "string" || typeof deduplicationKey !== "string" || !deduplicationKey || typeof costUSD !== "number" || !Number.isFinite(costUSD) || costUSD < 0) return null;
2799
+ if (costUSD === 0 && !getModelCosts(model)) return "unpriced";
2800
+ const usage = record.usage ?? record;
2801
+ const fields2 = ["inputTokens", "outputTokens", "cacheCreationInputTokens", "cacheReadInputTokens", "cacheCreation1hInputTokens", "webSearchRequests"];
2802
+ const tokens = fields2.map((field) => usage[field] ?? 0);
2803
+ if (!tokens.every((value2) => typeof value2 === "number" && Number.isFinite(value2) && value2 >= 0)) return null;
2804
+ const id = createHash2("sha256").update(JSON.stringify([provider, deduplicationKey, model, record.speed ?? "standard", ...tokens])).digest();
2805
+ return { record, id };
2806
+ }
2807
+ function lockRecordedCosts(data) {
2808
+ let db;
2809
+ let batch = [];
2810
+ let hasUnpricedUsage = false;
2811
+ const flush = () => {
2812
+ if (!batch.length) return;
2813
+ chargeRead(0);
2814
+ db ??= database();
2815
+ const placeholders = batch.map(() => "?").join(",");
2816
+ const query = db.prepare(`SELECT id, usd FROM costs WHERE id IN (${placeholders})`);
2817
+ const read = () => new Map(query.all(...batch.map((row) => row.id)).map((row) => {
2818
+ const cost = Number(row.usd);
2819
+ if (!Number.isFinite(cost) || cost < 0) throw new Error("Watcher pricing ledger contains an invalid recorded cost");
2820
+ return [Buffer.from(row.id).toString("hex"), cost];
2821
+ }));
2822
+ let known = read();
2823
+ const missing = batch.filter((row) => !known.has(row.id.toString("hex")));
2824
+ if (missing.length) {
2825
+ db.exec("BEGIN IMMEDIATE");
2826
+ try {
2827
+ const insert = db.prepare("INSERT OR IGNORE INTO costs (id, usd) VALUES (?, ?)");
2828
+ for (const row of missing) insert.run(row.id, row.record.costUSD);
2829
+ db.exec("COMMIT");
2830
+ } catch (error) {
2831
+ db.exec("ROLLBACK");
2832
+ throw error;
2833
+ }
2834
+ known = read();
2835
+ }
2836
+ chargeRead(known.size * 40);
2837
+ for (const row of batch) row.record.costUSD = known.get(row.id.toString("hex"));
2838
+ batch = [];
2839
+ };
2840
+ const add2 = (value) => {
2841
+ const row = costRow(value);
2842
+ if (row === "unpriced") hasUnpricedUsage = true;
2843
+ else if (row) batch.push(row);
2844
+ if (batch.length === BATCH) flush();
2845
+ };
2846
+ for (const entry of data) {
2847
+ if (entry && typeof entry === "object" && Array.isArray(entry.assistantCalls)) {
2848
+ for (const call of entry.assistantCalls) add2(call);
2849
+ } else add2(entry);
2850
+ }
2851
+ flush();
2852
+ return hasUnpricedUsage;
2853
+ }
2854
+ var require2, BATCH, opened;
2855
+ var init_recorded_costs = __esm({
2856
+ "src/recorded-costs.ts"() {
2857
+ "use strict";
2858
+ init_cache_dir();
2859
+ init_models();
2860
+ init_resource_budget();
2861
+ init_sqlite();
2862
+ require2 = createRequire2(import.meta.url);
2863
+ BATCH = 128;
2864
+ }
2865
+ });
2866
+
2693
2867
  // src/parsed-file-cache.ts
2694
2868
  import { gzip, gunzip } from "zlib";
2695
2869
  import { promisify } from "util";
2696
- import { createHash as createHash2, randomUUID as randomUUID2 } from "crypto";
2870
+ import { createHash as createHash3, randomUUID as randomUUID2 } from "crypto";
2697
2871
  import { mkdir as mkdir8, readFile as readFile7, stat as stat5, writeFile as writeFile3, rename as rename6, unlink as unlink5, readdir as readdir3 } from "fs/promises";
2698
- import { join as join9 } from "path";
2872
+ import { join as join10 } from "path";
2699
2873
  async function fingerprint(path) {
2700
2874
  try {
2701
2875
  const s = await stat5(path);
@@ -2706,12 +2880,14 @@ async function fingerprint(path) {
2706
2880
  }
2707
2881
  }
2708
2882
  async function cachedFileParse(path, kind, parse, warnings, range) {
2709
- const dir = join9(getCacheDir(), "parsed-files");
2710
- const key2 = createHash2("sha256").update(`${VERSION2}:${kind}:${path}:${getPricingFingerprint()}`).digest("hex");
2711
- const cachePath = join9(dir, `v${VERSION2}-${key2}.json.gz`);
2883
+ const dir = join10(getCacheDir(), "parsed-files");
2884
+ const key2 = createHash3("sha256").update(`${VERSION2}:${kind}:${path}`).digest("hex");
2885
+ const cachePath = join10(dir, `v${VERSION2}-${key2}.json.gz`);
2886
+ const legacyKey = createHash3("sha256").update(`3:${kind}:${path}:${getPricingFingerprint()}`).digest("hex");
2887
+ const legacyPath = join10(dir, `v3-${legacyKey}.json.gz`);
2712
2888
  const fingerprintPath = kind === "opencode" ? path.slice(0, path.lastIndexOf(":")) : path;
2713
2889
  const before = await fingerprint(fingerprintPath);
2714
- const rangeKind = `usage-range:${VERSION2}:${kind}`;
2890
+ const rangeKind = `usage-range:3:${kind}`;
2715
2891
  const bounds = range && before ? await readDiscoveryFact(rangeKind, path, before) : void 0;
2716
2892
  if (range && bounds && (bounds.last < range.start.getTime() || bounds.first > range.end.getTime())) {
2717
2893
  if (Array.isArray(bounds.warnings)) warnings?.push(...bounds.warnings);
@@ -2729,32 +2905,49 @@ async function cachedFileParse(path, kind, parse, warnings, range) {
2729
2905
  }
2730
2906
  await rememberDiscoveryFact(rangeKind, path, before, { first: data2.length ? first : 0, last: data2.length ? last : -1, warnings });
2731
2907
  };
2908
+ let migrated;
2732
2909
  if (before) {
2733
- try {
2734
- const s = await stat5(cachePath);
2735
- if (s.size <= MAX_ENTRY) {
2736
- chargeRead(s.size);
2737
- const saved = JSON.parse((await decompress(await readFile7(cachePath), { maxOutputLength: MAX_ENTRY })).toString("utf8"));
2738
- if (saved.v === VERSION2 && saved.fingerprint === before && Array.isArray(saved.data)) {
2739
- if (Array.isArray(saved.warnings)) warnings?.push(...saved.warnings.filter((w) => typeof w === "string"));
2740
- await rememberRange(saved.data);
2741
- return saved.data;
2910
+ for (const candidate of [cachePath, legacyPath]) {
2911
+ try {
2912
+ const s = await stat5(candidate);
2913
+ if (s.size <= MAX_ENTRY) {
2914
+ chargeRead(s.size);
2915
+ const saved = JSON.parse((await decompress(await readFile7(candidate), { maxOutputLength: MAX_ENTRY })).toString("utf8"));
2916
+ if ((saved.v === VERSION2 || saved.v === 3) && saved.fingerprint === before && Array.isArray(saved.data)) {
2917
+ if (Array.isArray(saved.warnings)) warnings?.push(...saved.warnings.filter((w) => typeof w === "string"));
2918
+ await rememberRange(saved.data);
2919
+ if (saved.v === VERSION2 && saved.pricesLocked === true) {
2920
+ if (!saved.hasUnpricedUsage || saved.pricingFingerprint === getPricingFingerprint()) return saved.data;
2921
+ continue;
2922
+ }
2923
+ migrated = saved.data;
2924
+ break;
2925
+ }
2742
2926
  }
2927
+ } catch (err) {
2928
+ if (err instanceof ResourceBudgetError) throw err;
2743
2929
  }
2744
- } catch (err) {
2745
- if (err instanceof ResourceBudgetError) throw err;
2746
2930
  }
2747
2931
  }
2748
- const data = await parse();
2932
+ const data = migrated ?? await parse();
2933
+ const hasUnpricedUsage = lockRecordedCosts(data);
2749
2934
  if (!before || await fingerprint(fingerprintPath) !== before) return data;
2750
2935
  await rememberRange(data);
2751
- const payload = JSON.stringify({ v: VERSION2, fingerprint: before, data, warnings });
2936
+ const payload = JSON.stringify({
2937
+ v: VERSION2,
2938
+ fingerprint: before,
2939
+ data,
2940
+ warnings,
2941
+ pricesLocked: true,
2942
+ hasUnpricedUsage,
2943
+ pricingFingerprint: getPricingFingerprint()
2944
+ });
2752
2945
  if (Buffer.byteLength(payload) > MAX_ENTRY) return data;
2753
2946
  await mkdir8(dir, { recursive: true });
2754
2947
  const encoded = await compress(Buffer.from(payload));
2755
- const release = await acquireFileLock(join9(dir, "writes.lock"));
2948
+ const release = await acquireFileLock(join10(dir, "writes.lock"));
2756
2949
  try {
2757
- const quotaPath = join9(dir, "quota");
2950
+ const quotaPath = join10(dir, "quota");
2758
2951
  const quota = await readFile7(quotaPath, "utf8").catch(() => "");
2759
2952
  let total = /^\d+$/.test(quota) ? Number(quota) : NaN;
2760
2953
  const writeQuota = async (bytes) => {
@@ -2772,12 +2965,12 @@ async function cachedFileParse(path, kind, parse, warnings, range) {
2772
2965
  const rows2 = [];
2773
2966
  for (const name of files) {
2774
2967
  if (!name.endsWith(".json") && !name.endsWith(".json.gz") && !name.endsWith(".tmp")) continue;
2775
- if (!name.startsWith(`v${VERSION2}-`)) {
2776
- await unlink5(join9(dir, name)).catch(() => {
2968
+ if (!name.startsWith(`v${VERSION2}-`) && !name.startsWith("v3-")) {
2969
+ await unlink5(join10(dir, name)).catch(() => {
2777
2970
  });
2778
2971
  continue;
2779
2972
  }
2780
- const p = join9(dir, name), s = await stat5(p).catch(() => null);
2973
+ const p = join10(dir, name), s = await stat5(p).catch(() => null);
2781
2974
  if (s) rows2.push({ path: p, size: s.size, mtime: s.mtimeMs });
2782
2975
  }
2783
2976
  total = rows2.reduce((n, r) => n + r.size, 0);
@@ -2797,7 +2990,7 @@ async function cachedFileParse(path, kind, parse, warnings, range) {
2797
2990
  const rows2 = [];
2798
2991
  for (const name of await readdir3(dir)) {
2799
2992
  if (!name.endsWith(".json.gz")) continue;
2800
- const p = join9(dir, name);
2993
+ const p = join10(dir, name);
2801
2994
  if (p === cachePath) continue;
2802
2995
  const s = await stat5(p).catch(() => null);
2803
2996
  if (s) rows2.push({ path: p, size: s.size, mtime: s.mtimeMs });
@@ -2835,7 +3028,8 @@ var init_parsed_file_cache = __esm({
2835
3028
  init_resource_budget();
2836
3029
  init_file_lock();
2837
3030
  init_discovery_cache();
2838
- VERSION2 = 3;
3031
+ init_recorded_costs();
3032
+ VERSION2 = 4;
2839
3033
  compress = promisify(gzip);
2840
3034
  decompress = promisify(gunzip);
2841
3035
  MAX_ENTRY = 16 * 1024 * 1024;
@@ -3011,15 +3205,15 @@ var init_fs_utils = __esm({
3011
3205
 
3012
3206
  // src/providers/claude.ts
3013
3207
  import { readdir as readdir4, stat as stat6 } from "fs/promises";
3014
- import { basename as basename2, join as join10, resolve as resolve2 } from "path";
3208
+ import { basename as basename2, join as join11, resolve as resolve2 } from "path";
3015
3209
  import { homedir as homedir4 } from "os";
3016
3210
  function getClaudeDir() {
3017
- return process.env["CLAUDE_CONFIG_DIR"] || join10(homedir4(), ".claude");
3211
+ return process.env["CLAUDE_CONFIG_DIR"] || join11(homedir4(), ".claude");
3018
3212
  }
3019
3213
  function getDesktopSessionsDir() {
3020
- if (process.platform === "darwin") return join10(homedir4(), "Library", "Application Support", "Claude", "local-agent-mode-sessions");
3021
- if (process.platform === "win32") return join10(homedir4(), "AppData", "Roaming", "Claude", "local-agent-mode-sessions");
3022
- return join10(homedir4(), ".config", "Claude", "local-agent-mode-sessions");
3214
+ if (process.platform === "darwin") return join11(homedir4(), "Library", "Application Support", "Claude", "local-agent-mode-sessions");
3215
+ if (process.platform === "win32") return join11(homedir4(), "AppData", "Roaming", "Claude", "local-agent-mode-sessions");
3216
+ return join11(homedir4(), ".config", "Claude", "local-agent-mode-sessions");
3023
3217
  }
3024
3218
  async function findDesktopProjectDirs(base) {
3025
3219
  const results = [];
@@ -3028,13 +3222,13 @@ async function findDesktopProjectDirs(base) {
3028
3222
  const entries = await readdir4(dir).catch(() => []);
3029
3223
  for (const entry of entries) {
3030
3224
  if (entry === "node_modules" || entry === ".git") continue;
3031
- const full = join10(dir, entry);
3225
+ const full = join11(dir, entry);
3032
3226
  const s = await stat6(full).catch(() => null);
3033
3227
  if (!s?.isDirectory()) continue;
3034
3228
  if (entry === "projects") {
3035
3229
  const projectDirs = await readdir4(full).catch(() => []);
3036
3230
  for (const pd of projectDirs) {
3037
- const pdFull = join10(full, pd);
3231
+ const pdFull = join11(full, pd);
3038
3232
  const pdStat = await stat6(pdFull).catch(() => null);
3039
3233
  if (pdStat?.isDirectory()) results.push(pdFull);
3040
3234
  }
@@ -3088,11 +3282,11 @@ var init_claude = __esm({
3088
3282
  const config = await readConfig();
3089
3283
  const roots = /* @__PURE__ */ new Set([getClaudeDir(), ...config.claudeConfigDirs ?? []]);
3090
3284
  for (const root of roots) {
3091
- const projectsDir = join10(root.startsWith("~/") ? join10(homedir4(), root.slice(2)) : resolve2(root), "projects");
3285
+ const projectsDir = join11(root.startsWith("~/") ? join11(homedir4(), root.slice(2)) : resolve2(root), "projects");
3092
3286
  try {
3093
3287
  const entries = await readdir4(projectsDir);
3094
3288
  for (const dirName of entries) {
3095
- const dirPath = join10(projectsDir, dirName);
3289
+ const dirPath = join11(projectsDir, dirName);
3096
3290
  const dirStat = await stat6(dirPath).catch(() => null);
3097
3291
  if (dirStat?.isDirectory()) {
3098
3292
  sources.push({ path: dirPath, project: dirName, provider: "claude" });
@@ -3119,10 +3313,10 @@ var init_claude = __esm({
3119
3313
 
3120
3314
  // src/providers/codex.ts
3121
3315
  import { readdir as readdir5, stat as stat7 } from "fs/promises";
3122
- import { basename as basename3, join as join11 } from "path";
3316
+ import { basename as basename3, join as join12 } from "path";
3123
3317
  import { homedir as homedir5 } from "os";
3124
3318
  function getCodexDir(override) {
3125
- return override ?? process.env["CODEX_HOME"] ?? join11(homedir5(), ".codex");
3319
+ return override ?? process.env["CODEX_HOME"] ?? join12(homedir5(), ".codex");
3126
3320
  }
3127
3321
  function sanitizeProject(cwd) {
3128
3322
  return cwd.replace(/^\//, "").replace(/\//g, "-");
@@ -3143,12 +3337,12 @@ async function readCodexHeader(filePath) {
3143
3337
  return { project: sanitizeProject(entry.payload?.cwd ?? "unknown") };
3144
3338
  }
3145
3339
  async function discoverSessionsInDir(codexDir) {
3146
- const sessionsDir = join11(codexDir, "sessions");
3340
+ const sessionsDir = join12(codexDir, "sessions");
3147
3341
  const sources = [];
3148
- const archivedDir = join11(codexDir, "archived_sessions");
3342
+ const archivedDir = join12(codexDir, "archived_sessions");
3149
3343
  for (const file of await readdir5(archivedDir).catch(() => [])) {
3150
3344
  if (!file.startsWith("rollout-") || !file.endsWith(".jsonl")) continue;
3151
- const filePath = join11(archivedDir, file);
3345
+ const filePath = join12(archivedDir, file);
3152
3346
  const s = await stat7(filePath).catch(() => null);
3153
3347
  if (!s?.isFile()) continue;
3154
3348
  const header = await cachedDiscoveryHeader("codex", filePath, s, () => readCodexHeader(filePath));
@@ -3162,19 +3356,19 @@ async function discoverSessionsInDir(codexDir) {
3162
3356
  }
3163
3357
  for (const year of years) {
3164
3358
  if (!/^\d{4}$/.test(year)) continue;
3165
- const yearDir = join11(sessionsDir, year);
3359
+ const yearDir = join12(sessionsDir, year);
3166
3360
  const months = await readdir5(yearDir).catch(() => []);
3167
3361
  for (const month of months) {
3168
3362
  if (!/^\d{2}$/.test(month)) continue;
3169
- const monthDir = join11(yearDir, month);
3363
+ const monthDir = join12(yearDir, month);
3170
3364
  const days = await readdir5(monthDir).catch(() => []);
3171
3365
  for (const day of days) {
3172
3366
  if (!/^\d{2}$/.test(day)) continue;
3173
- const dayDir = join11(monthDir, day);
3367
+ const dayDir = join12(monthDir, day);
3174
3368
  const files = await readdir5(dayDir).catch(() => []);
3175
3369
  for (const file of files) {
3176
3370
  if (!file.startsWith("rollout-") || !file.endsWith(".jsonl")) continue;
3177
- const filePath = join11(dayDir, file);
3371
+ const filePath = join12(dayDir, file);
3178
3372
  const s = await stat7(filePath).catch(() => null);
3179
3373
  if (!s?.isFile()) continue;
3180
3374
  const header = await cachedDiscoveryHeader("codex", filePath, s, () => readCodexHeader(filePath));
@@ -3393,10 +3587,10 @@ var init_codex = __esm({
3393
3587
 
3394
3588
  // src/providers/copilot.ts
3395
3589
  import { readdir as readdir6, stat as stat8 } from "fs/promises";
3396
- import { basename as basename4, dirname as dirname3, join as join12 } from "path";
3590
+ import { basename as basename4, dirname as dirname3, join as join13 } from "path";
3397
3591
  import { homedir as homedir6 } from "os";
3398
3592
  function getCopilotSessionStateDir(override) {
3399
- return override ?? join12(homedir6(), ".copilot", "session-state");
3593
+ return override ?? join13(homedir6(), ".copilot", "session-state");
3400
3594
  }
3401
3595
  function parseCwd(yaml) {
3402
3596
  const match = yaml.match(/^cwd:\s*(.+)$/m);
@@ -3471,11 +3665,11 @@ async function discoverSessionsInDir2(sessionStateDir) {
3471
3665
  return sources;
3472
3666
  }
3473
3667
  for (const sessionId of sessionDirs) {
3474
- const eventsPath = join12(sessionStateDir, sessionId, "events.jsonl");
3668
+ const eventsPath = join13(sessionStateDir, sessionId, "events.jsonl");
3475
3669
  const s = await stat8(eventsPath).catch(() => null);
3476
3670
  if (!s?.isFile()) continue;
3477
3671
  let project = sessionId;
3478
- const yaml = await readSessionFile(join12(sessionStateDir, sessionId, "workspace.yaml"));
3672
+ const yaml = await readSessionFile(join13(sessionStateDir, sessionId, "workspace.yaml"));
3479
3673
  if (yaml !== null) {
3480
3674
  const cwd = parseCwd(yaml);
3481
3675
  if (cwd) project = basename4(cwd);
@@ -3589,13 +3783,13 @@ var init_bash_utils = __esm({
3589
3783
 
3590
3784
  // src/providers/pi.ts
3591
3785
  import { readdir as readdir7, stat as stat9 } from "fs/promises";
3592
- import { basename as basename6, join as join13 } from "path";
3786
+ import { basename as basename6, join as join14 } from "path";
3593
3787
  import { homedir as homedir7 } from "os";
3594
3788
  function getPiSessionsDir(override) {
3595
- return override ?? join13(homedir7(), ".pi", "agent", "sessions");
3789
+ return override ?? join14(homedir7(), ".pi", "agent", "sessions");
3596
3790
  }
3597
3791
  function getOmpSessionsDir(override) {
3598
- return override ?? join13(homedir7(), ".omp", "agent", "sessions");
3792
+ return override ?? join14(homedir7(), ".omp", "agent", "sessions");
3599
3793
  }
3600
3794
  async function readFirstEntry(filePath) {
3601
3795
  const line = await readSessionFirstLine(filePath);
@@ -3615,7 +3809,7 @@ async function discoverSessionsInDir3(sessionsDir, providerName) {
3615
3809
  return sources;
3616
3810
  }
3617
3811
  for (const dirName of projectDirs) {
3618
- const dirPath = join13(sessionsDir, dirName);
3812
+ const dirPath = join14(sessionsDir, dirName);
3619
3813
  const dirStat = await stat9(dirPath).catch(() => null);
3620
3814
  if (!dirStat?.isDirectory()) continue;
3621
3815
  let files;
@@ -3626,7 +3820,7 @@ async function discoverSessionsInDir3(sessionsDir, providerName) {
3626
3820
  }
3627
3821
  for (const file of files) {
3628
3822
  if (!file.endsWith(".jsonl")) continue;
3629
- const filePath = join13(dirPath, file);
3823
+ const filePath = join14(dirPath, file);
3630
3824
  const fileStat = await stat9(filePath).catch(() => null);
3631
3825
  if (!fileStat?.isFile()) continue;
3632
3826
  const first = await readFirstEntry(filePath);
@@ -3783,12 +3977,12 @@ var init_pi = __esm({
3783
3977
  });
3784
3978
 
3785
3979
  // src/providers/kimi.ts
3786
- import { createHash as createHash3 } from "crypto";
3980
+ import { createHash as createHash4 } from "crypto";
3787
3981
  import { readdir as readdir8, readFile as readFile8, stat as stat10 } from "fs/promises";
3788
- import { basename as basename7, dirname as dirname4, join as join14 } from "path";
3982
+ import { basename as basename7, dirname as dirname4, join as join15 } from "path";
3789
3983
  import { homedir as homedir8 } from "os";
3790
3984
  function createKimiProvider(home) {
3791
- const root = () => home ?? process.env["KIMI_CODE_HOME"] ?? join14(homedir8(), ".kimi-code");
3985
+ const root = () => home ?? process.env["KIMI_CODE_HOME"] ?? join15(homedir8(), ".kimi-code");
3792
3986
  return {
3793
3987
  name: "kimi",
3794
3988
  displayName: "Kimi",
@@ -3796,7 +3990,7 @@ function createKimiProvider(home) {
3796
3990
  toolDisplayName: (tool) => tool,
3797
3991
  async discoverSessions() {
3798
3992
  const projects = /* @__PURE__ */ new Map();
3799
- const catalog = join14(root(), "workspaces.json");
3993
+ const catalog = join15(root(), "workspaces.json");
3800
3994
  try {
3801
3995
  const size = (await stat10(catalog)).size;
3802
3996
  if (size <= 2 * 1024 * 1024) {
@@ -3818,16 +4012,16 @@ function createKimiProvider(home) {
3818
4012
  const entries = await readdir8(dir, { withFileTypes: true }).catch(() => []);
3819
4013
  for (const entry of entries) {
3820
4014
  if (entry.name.startsWith(".")) continue;
3821
- const path = join14(dir, entry.name);
4015
+ const path = join15(dir, entry.name);
3822
4016
  if (entry.isDirectory()) await walk(path, project, depth + 1);
3823
4017
  else if (entry.isFile() && entry.name === "wire.jsonl") sources.push({ path, project, provider: "kimi" });
3824
4018
  }
3825
4019
  }
3826
- const sessions = join14(root(), "sessions");
4020
+ const sessions = join15(root(), "sessions");
3827
4021
  for (const entry of await readdir8(sessions, { withFileTypes: true }).catch(() => [])) {
3828
4022
  if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
3829
4023
  const project = projects.get(entry.name) ?? entry.name.replace(/^wd_/, "").replace(/_[a-f\d]{12}$/, "");
3830
- await walk(join14(sessions, entry.name), project, 0);
4024
+ await walk(join15(sessions, entry.name), project, 0);
3831
4025
  }
3832
4026
  return sources.sort((a, b) => a.path.localeCompare(b.path));
3833
4027
  },
@@ -3866,7 +4060,7 @@ function createKimiProvider(home) {
3866
4060
  const warning = `No API pricing for model "${model}"; its tokens are counted but its cost is excluded. Configure a model alias only when the underlying API model is known.`;
3867
4061
  if (!warnings.includes(warning)) warnings.push(warning);
3868
4062
  }
3869
- const key2 = "kimi:" + createHash3("sha256").update(JSON.stringify([
4063
+ const key2 = "kimi:" + createHash4("sha256").update(JSON.stringify([
3870
4064
  data.agentId ?? agent,
3871
4065
  data.time,
3872
4066
  data.model,
@@ -3916,10 +4110,10 @@ var init_kimi = __esm({
3916
4110
  // src/cursor-cache.ts
3917
4111
  import { readFile as readFile9, mkdir as mkdir9, stat as stat11, rename as rename7 } from "fs/promises";
3918
4112
  import { open as open6 } from "fs/promises";
3919
- import { join as join15 } from "path";
4113
+ import { join as join16 } from "path";
3920
4114
  import { randomBytes as randomBytes4 } from "crypto";
3921
4115
  function getCachePath2() {
3922
- return join15(getCacheDir(), CACHE_FILE);
4116
+ return join16(getCacheDir(), CACHE_FILE);
3923
4117
  }
3924
4118
  async function getDbFingerprint(dbPath) {
3925
4119
  try {
@@ -3984,100 +4178,23 @@ var init_cursor_cache = __esm({
3984
4178
  }
3985
4179
  });
3986
4180
 
3987
- // src/sqlite.ts
3988
- import { createRequire } from "module";
3989
- function loadDriver() {
3990
- if (loadAttempted) return DatabaseSync !== null;
3991
- loadAttempted = true;
3992
- const origEmit = process.emit.bind(process);
3993
- let restored = false;
3994
- const restore = () => {
3995
- if (restored) return;
3996
- restored = true;
3997
- process.emit = origEmit;
3998
- };
3999
- process.emit = function patchedEmit(event, ...args) {
4000
- if (event === "warning") {
4001
- const warning = args[0];
4002
- if (warning?.name === "ExperimentalWarning" && typeof warning.message === "string" && /SQLite/i.test(warning.message)) {
4003
- return false;
4004
- }
4005
- }
4006
- return origEmit.call(this, event, ...args);
4007
- };
4008
- try {
4009
- const mod = requireForSqlite("node:sqlite");
4010
- DatabaseSync = mod.DatabaseSync;
4011
- return true;
4012
- } catch (err) {
4013
- const message = err instanceof Error ? err.message : String(err);
4014
- loadError = `SQLite-based providers (Cursor, OpenCode) need Node 22+ with the node:sqlite module.
4015
- Current Node: ${process.version}.
4016
- Upgrade Node (https://nodejs.org) and run exe-watcher again.
4017
- (underlying error: ${message})`;
4018
- return false;
4019
- } finally {
4020
- setTimeout(restore, 0);
4021
- }
4022
- }
4023
- function isSqliteAvailable() {
4024
- return loadDriver();
4025
- }
4026
- function getSqliteLoadError() {
4027
- return loadError ?? "SQLite driver not available";
4028
- }
4029
- function openDatabase(path) {
4030
- if (!loadDriver() || DatabaseSync === null) {
4031
- throw new Error(getSqliteLoadError());
4032
- }
4033
- const db = new DatabaseSync(path, { readOnly: true });
4034
- return {
4035
- query(sql, params = []) {
4036
- const rows2 = [];
4037
- let bytes = 0;
4038
- for (const row of db.prepare(sql).iterate(...params)) {
4039
- const size = Buffer.byteLength(JSON.stringify(row));
4040
- bytes += size;
4041
- if (rows2.length >= 1e5 || bytes > 32 * 1024 * 1024) throw new ResourceBudgetError("SQLite result budget");
4042
- chargeRead(size);
4043
- rows2.push(row);
4044
- }
4045
- return rows2;
4046
- },
4047
- close() {
4048
- db.close();
4049
- }
4050
- };
4051
- }
4052
- var requireForSqlite, DatabaseSync, loadAttempted, loadError;
4053
- var init_sqlite = __esm({
4054
- "src/sqlite.ts"() {
4055
- "use strict";
4056
- init_resource_budget();
4057
- requireForSqlite = createRequire(import.meta.url);
4058
- DatabaseSync = null;
4059
- loadAttempted = false;
4060
- loadError = null;
4061
- }
4062
- });
4063
-
4064
4181
  // src/providers/cursor.ts
4065
4182
  var cursor_exports = {};
4066
4183
  __export(cursor_exports, {
4067
4184
  createCursorProvider: () => createCursorProvider,
4068
4185
  cursor: () => cursor
4069
4186
  });
4070
- import { existsSync } from "fs";
4071
- import { join as join16 } from "path";
4187
+ import { existsSync as existsSync2 } from "fs";
4188
+ import { join as join17 } from "path";
4072
4189
  import { homedir as homedir9 } from "os";
4073
4190
  function getCursorDbPath() {
4074
4191
  if (process.platform === "darwin") {
4075
- return join16(homedir9(), "Library", "Application Support", "Cursor", "User", "globalStorage", "state.vscdb");
4192
+ return join17(homedir9(), "Library", "Application Support", "Cursor", "User", "globalStorage", "state.vscdb");
4076
4193
  }
4077
4194
  if (process.platform === "win32") {
4078
- return join16(homedir9(), "AppData", "Roaming", "Cursor", "User", "globalStorage", "state.vscdb");
4195
+ return join17(homedir9(), "AppData", "Roaming", "Cursor", "User", "globalStorage", "state.vscdb");
4079
4196
  }
4080
- return join16(homedir9(), ".config", "Cursor", "User", "globalStorage", "state.vscdb");
4197
+ return join17(homedir9(), ".config", "Cursor", "User", "globalStorage", "state.vscdb");
4081
4198
  }
4082
4199
  function extractLanguages(codeBlocksJson) {
4083
4200
  if (!codeBlocksJson) return [];
@@ -4249,7 +4366,7 @@ function createCursorProvider(dbPathOverride) {
4249
4366
  async discoverSessions() {
4250
4367
  if (!isSqliteAvailable()) return [];
4251
4368
  const dbPath = dbPathOverride ?? getCursorDbPath();
4252
- if (!existsSync(dbPath)) return [];
4369
+ if (!existsSync2(dbPath)) return [];
4253
4370
  return [{ path: dbPath, project: "cursor", provider: "cursor" }];
4254
4371
  },
4255
4372
  createSessionParser(source, seenKeys) {
@@ -4320,19 +4437,19 @@ __export(opencode_exports, {
4320
4437
  opencode: () => opencode
4321
4438
  });
4322
4439
  import { readdir as readdir9 } from "fs/promises";
4323
- import { join as join17 } from "path";
4440
+ import { join as join18 } from "path";
4324
4441
  import { homedir as homedir10 } from "os";
4325
4442
  function sanitize2(dir) {
4326
4443
  return dir.replace(/^\//, "").replace(/\//g, "-");
4327
4444
  }
4328
4445
  function getDataDir(dataDir) {
4329
- const base = dataDir ?? process.env["XDG_DATA_HOME"] ?? join17(homedir10(), ".local", "share");
4330
- return join17(base, "opencode");
4446
+ const base = dataDir ?? process.env["XDG_DATA_HOME"] ?? join18(homedir10(), ".local", "share");
4447
+ return join18(base, "opencode");
4331
4448
  }
4332
4449
  async function findDbFiles(dir) {
4333
4450
  try {
4334
4451
  const entries = await readdir9(dir);
4335
- return entries.filter((f) => f.startsWith("opencode") && f.endsWith(".db")).map((f) => join17(dir, f));
4452
+ return entries.filter((f) => f.startsWith("opencode") && f.endsWith(".db")).map((f) => join18(dir, f));
4336
4453
  } catch (error) {
4337
4454
  if (error instanceof ResourceBudgetError) throw error;
4338
4455
  return [];
@@ -4561,20 +4678,20 @@ __export(cursor_agent_exports, {
4561
4678
  createCursorAgentProvider: () => createCursorAgentProvider,
4562
4679
  cursor_agent: () => cursor_agent
4563
4680
  });
4564
- import { createHash as createHash4 } from "crypto";
4565
- import { existsSync as existsSync2 } from "fs";
4681
+ import { createHash as createHash5 } from "crypto";
4682
+ import { existsSync as existsSync3 } from "fs";
4566
4683
  import { readdir as readdir10, stat as stat12 } from "fs/promises";
4567
- import { join as join18, basename as basename8 } from "path";
4684
+ import { join as join19, basename as basename8 } from "path";
4568
4685
  import { homedir as homedir11 } from "os";
4569
4686
  function getCursorAgentBaseDir(baseDirOverride) {
4570
4687
  if (baseDirOverride) return baseDirOverride;
4571
- return join18(homedir11(), ".cursor");
4688
+ return join19(homedir11(), ".cursor");
4572
4689
  }
4573
4690
  function getProjectsDir(baseDir) {
4574
- return join18(baseDir, "projects");
4691
+ return join19(baseDir, "projects");
4575
4692
  }
4576
4693
  function getAttributionDbPath(baseDir) {
4577
- return join18(baseDir, "ai-tracking", "ai-code-tracking.db");
4694
+ return join19(baseDir, "ai-tracking", "ai-code-tracking.db");
4578
4695
  }
4579
4696
  function estimateTokens(charCount) {
4580
4697
  if (charCount <= 0) return 0;
@@ -4625,7 +4742,7 @@ function resolveModel3(raw) {
4625
4742
  function toConversationId(transcriptPath) {
4626
4743
  const filename = basename8(transcriptPath, ".txt");
4627
4744
  if (filename.length === 36 && UUID_LIKE.test(filename)) return filename;
4628
- return createHash4("sha1").update(transcriptPath).digest("hex").slice(0, 16);
4745
+ return createHash5("sha1").update(transcriptPath).digest("hex").slice(0, 16);
4629
4746
  }
4630
4747
  function extractUserQuery(userBlock) {
4631
4748
  const chunks = [];
@@ -4734,7 +4851,7 @@ function createParser6(source, seenKeys, dbPath, summariesByConversationId) {
4734
4851
  let db = null;
4735
4852
  try {
4736
4853
  if (!summary) {
4737
- if (existsSync2(dbPath)) {
4854
+ if (existsSync3(dbPath)) {
4738
4855
  try {
4739
4856
  db = openDatabase(dbPath);
4740
4857
  const rows2 = db.query(CONVERSATION_SUMMARY_QUERY, [conversationId]);
@@ -4827,19 +4944,19 @@ function createCursorAgentProvider(baseDirOverride) {
4827
4944
  return rawTool;
4828
4945
  },
4829
4946
  async discoverSessions() {
4830
- if (!existsSync2(projectsDir)) return [];
4947
+ if (!existsSync3(projectsDir)) return [];
4831
4948
  const projectEntries = await readdir10(projectsDir, { withFileTypes: true });
4832
4949
  const sources = [];
4833
4950
  for (const entry of projectEntries) {
4834
4951
  if (!entry.isDirectory()) continue;
4835
4952
  const projectId = prettifyProjectId(entry.name);
4836
- const transcriptDir = join18(projectsDir, entry.name, "agent-transcripts");
4837
- if (!existsSync2(transcriptDir)) continue;
4953
+ const transcriptDir = join19(projectsDir, entry.name, "agent-transcripts");
4954
+ if (!existsSync3(transcriptDir)) continue;
4838
4955
  const transcriptEntries = await readdir10(transcriptDir, { withFileTypes: true });
4839
4956
  for (const transcript of transcriptEntries) {
4840
4957
  if (!transcript.isFile()) continue;
4841
4958
  if (!transcript.name.endsWith(".txt")) continue;
4842
- const transcriptPath = join18(transcriptDir, transcript.name);
4959
+ const transcriptPath = join19(transcriptDir, transcript.name);
4843
4960
  sources.push({
4844
4961
  path: transcriptPath,
4845
4962
  project: projectId,
@@ -5153,15 +5270,15 @@ var init_classifier = __esm({
5153
5270
 
5154
5271
  // src/session-index.ts
5155
5272
  import { stat as stat13 } from "fs/promises";
5156
- import { existsSync as existsSync3, unlinkSync } from "fs";
5273
+ import { existsSync as existsSync4, unlinkSync } from "fs";
5157
5274
  import { readFile as readFile11, mkdir as mkdir10, rename as rename8, unlink as unlink6, open as open7 } from "fs/promises";
5158
- import { join as join19 } from "path";
5275
+ import { join as join20 } from "path";
5159
5276
  import { randomBytes as randomBytes5 } from "crypto";
5160
5277
  function emptyIndex() {
5161
5278
  return { v: SESSION_INDEX_VERSION, e: {} };
5162
5279
  }
5163
5280
  function indexPath() {
5164
- return join19(getCacheDir(), INDEX_FILENAME);
5281
+ return join20(getCacheDir(), INDEX_FILENAME);
5165
5282
  }
5166
5283
  async function loadSessionIndex() {
5167
5284
  if (currentIndex) return currentIndex;
@@ -5181,7 +5298,7 @@ async function loadSessionIndex() {
5181
5298
  }
5182
5299
  async function saveSessionIndex(index) {
5183
5300
  const dir = getCacheDir();
5184
- if (!existsSync3(dir)) await mkdir10(dir, { recursive: true });
5301
+ if (!existsSync4(dir)) await mkdir10(dir, { recursive: true });
5185
5302
  const finalPath = indexPath();
5186
5303
  const tempPath = `${finalPath}.${randomBytes5(8).toString("hex")}.tmp`;
5187
5304
  const payload = JSON.stringify(index);
@@ -5263,7 +5380,7 @@ __export(parser_exports, {
5263
5380
  });
5264
5381
  import { AsyncLocalStorage as AsyncLocalStorage2 } from "async_hooks";
5265
5382
  import { readdir as readdir11, stat as stat14 } from "fs/promises";
5266
- import { basename as basename9, join as join20 } from "path";
5383
+ import { basename as basename9, join as join21 } from "path";
5267
5384
  function getParseWarnings() {
5268
5385
  return _parseWarnings;
5269
5386
  }
@@ -5553,13 +5670,13 @@ async function parseSessionFile(filePath, project, seenMsgIds, dateRange) {
5553
5670
  }
5554
5671
  async function collectJsonlFiles2(dirPath) {
5555
5672
  const files = await readdir11(dirPath).catch(() => []);
5556
- const jsonlFiles = files.filter((f) => f.endsWith(".jsonl")).map((f) => join20(dirPath, f));
5673
+ const jsonlFiles = files.filter((f) => f.endsWith(".jsonl")).map((f) => join21(dirPath, f));
5557
5674
  for (const entry of files) {
5558
5675
  if (entry.endsWith(".jsonl")) continue;
5559
- const subagentsPath = join20(dirPath, entry, "subagents");
5676
+ const subagentsPath = join21(dirPath, entry, "subagents");
5560
5677
  const subFiles = await readdir11(subagentsPath).catch(() => []);
5561
5678
  for (const sf of subFiles) {
5562
- if (sf.endsWith(".jsonl")) jsonlFiles.push(join20(subagentsPath, sf));
5679
+ if (sf.endsWith(".jsonl")) jsonlFiles.push(join21(subagentsPath, sf));
5563
5680
  }
5564
5681
  }
5565
5682
  return jsonlFiles;
@@ -5925,8 +6042,8 @@ var init_parser = __esm({
5925
6042
  init_cache_dir();
5926
6043
  init_file_lock();
5927
6044
  init_resource_budget();
5928
- import { existsSync as existsSync7, readFileSync as readFileSync2, statSync as statSync3 } from "fs";
5929
- import { join as join30 } from "path";
6045
+ import { existsSync as existsSync8, readFileSync as readFileSync2, statSync as statSync3 } from "fs";
6046
+ import { join as join31 } from "path";
5930
6047
  import { homedir as homedir17 } from "os";
5931
6048
  import { Command } from "commander";
5932
6049
 
@@ -6657,19 +6774,19 @@ function renderStatusBar(projects) {
6657
6774
 
6658
6775
  // src/menubar-json.ts
6659
6776
  import { readdirSync, readFileSync } from "fs";
6660
- import { join as join21, dirname as dirname5 } from "path";
6777
+ import { join as join22, dirname as dirname5 } from "path";
6661
6778
  init_models();
6662
6779
  import { fileURLToPath } from "url";
6663
6780
  function getCliVersion() {
6664
6781
  try {
6665
- const pkgPath = join21(dirname5(fileURLToPath(import.meta.url)), "..", "package.json");
6782
+ const pkgPath = join22(dirname5(fileURLToPath(import.meta.url)), "..", "package.json");
6666
6783
  const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
6667
6784
  return pkg.version ?? "0.0.0";
6668
6785
  } catch {
6669
6786
  return "0.0.0";
6670
6787
  }
6671
6788
  }
6672
- var MIN_APP_VERSION = "0.2.59";
6789
+ var MIN_APP_VERSION = "0.2.67";
6673
6790
  var TOP_ACTIVITIES_LIMIT = 20;
6674
6791
  var TOP_MODELS_LIMIT = 20;
6675
6792
  var TOP_FINDINGS_LIMIT = 10;
@@ -6825,18 +6942,18 @@ function buildMenubarPayload(current, providers, optimize, dailyHistory, agentSt
6825
6942
  // src/daily-cache.ts
6826
6943
  init_cache_dir();
6827
6944
  init_models();
6828
- import { createHash as createHash5, randomBytes as randomBytes6 } from "crypto";
6829
- import { existsSync as existsSync4 } from "fs";
6945
+ import { createHash as createHash6, randomBytes as randomBytes6 } from "crypto";
6946
+ import { existsSync as existsSync5 } from "fs";
6830
6947
  import { mkdir as mkdir11, open as open8, readFile as readFile12, rename as rename9, unlink as unlink7 } from "fs/promises";
6831
- import { join as join22 } from "path";
6948
+ import { join as join23 } from "path";
6832
6949
  var DAILY_CACHE_VERSION = 9;
6833
6950
  var DEFAULT_DAILY_CACHE_SCOPE = "global";
6834
6951
  var DAILY_CACHE_FILENAME = "daily-cache.json";
6835
6952
  function getCachePath3(scopeKey) {
6836
6953
  if (scopeKey === DEFAULT_DAILY_CACHE_SCOPE) {
6837
- return join22(getCacheDir(), DAILY_CACHE_FILENAME);
6954
+ return join23(getCacheDir(), DAILY_CACHE_FILENAME);
6838
6955
  }
6839
- return join22(getCacheDir(), `daily-cache-${scopeKey}.json`);
6956
+ return join23(getCacheDir(), `daily-cache-${scopeKey}.json`);
6840
6957
  }
6841
6958
  function emptyCache(scopeKey) {
6842
6959
  return { version: DAILY_CACHE_VERSION, scopeKey, lastComputedDate: null, days: [] };
@@ -6851,7 +6968,7 @@ function buildDailyCacheScopeKey(include, exclude) {
6851
6968
  return DEFAULT_DAILY_CACHE_SCOPE;
6852
6969
  }
6853
6970
  const raw = JSON.stringify({ include: normalizedInclude, exclude: normalizedExclude });
6854
- return `scope-${createHash5("sha1").update(raw).digest("hex").slice(0, 12)}`;
6971
+ return `scope-${createHash6("sha1").update(raw).digest("hex").slice(0, 12)}`;
6855
6972
  }
6856
6973
  function isValidCache(parsed) {
6857
6974
  if (!parsed || typeof parsed !== "object") return false;
@@ -6869,20 +6986,20 @@ function isValidCache(parsed) {
6869
6986
  }
6870
6987
  async function loadDailyCache(scopeKey = DEFAULT_DAILY_CACHE_SCOPE) {
6871
6988
  const path = getCachePath3(scopeKey);
6872
- if (!existsSync4(path)) return emptyCache(scopeKey);
6989
+ if (!existsSync5(path)) return emptyCache(scopeKey);
6873
6990
  try {
6874
6991
  const raw = await readFile12(path, "utf-8");
6875
6992
  const parsed = JSON.parse(raw);
6876
6993
  if (!isValidCache(parsed)) return emptyCache(scopeKey);
6877
6994
  if (parsed.scopeKey !== scopeKey) return emptyCache(scopeKey);
6878
- if ((parsed.days.length || parsed.historyBuild) && parsed.pricingFingerprint !== getPricingFingerprint()) {
6879
- return {
6880
- ...parsed,
6881
- version: DAILY_CACHE_VERSION,
6882
- historyBuild: void 0,
6883
- needsRepricing: true,
6884
- previousHistoryStart: parsed.days[0]?.date ?? parsed.historyBuild?.start
6885
- };
6995
+ if (parsed.needsRepricing) {
6996
+ const repricing = parsed.historyBuild;
6997
+ if (parsed.days.length) {
6998
+ parsed.historyBuild = void 0;
6999
+ parsed.backfill = repricing?.previousBackfill ?? null;
7000
+ }
7001
+ delete parsed.needsRepricing;
7002
+ delete parsed.previousHistoryStart;
6886
7003
  }
6887
7004
  if (parsed.version !== DAILY_CACHE_VERSION) {
6888
7005
  return {
@@ -6899,7 +7016,7 @@ async function loadDailyCache(scopeKey = DEFAULT_DAILY_CACHE_SCOPE) {
6899
7016
  }
6900
7017
  async function saveDailyCache(cache) {
6901
7018
  const dir = getCacheDir();
6902
- if (!existsSync4(dir)) await mkdir11(dir, { recursive: true });
7019
+ if (!existsSync5(dir)) await mkdir11(dir, { recursive: true });
6903
7020
  const scopeKey = cache.scopeKey || DEFAULT_DAILY_CACHE_SCOPE;
6904
7021
  const finalPath = getCachePath3(scopeKey);
6905
7022
  const tempPath = `${finalPath}.${randomBytes6(8).toString("hex")}.tmp`;
@@ -6959,16 +7076,16 @@ init_types();
6959
7076
  init_cache_dir();
6960
7077
  init_models();
6961
7078
  import { readFile as readFile13, stat as stat16, mkdir as mkdir12, writeFile as writeFile4, rename as rename10, unlink as unlink8 } from "fs/promises";
6962
- import { createHash as createHash6, randomUUID as randomUUID3 } from "crypto";
7079
+ import { createHash as createHash7, randomUUID as randomUUID3 } from "crypto";
6963
7080
  import { homedir as homedir12 } from "os";
6964
- import { join as join24 } from "path";
7081
+ import { join as join25 } from "path";
6965
7082
 
6966
7083
  // src/history-start.ts
6967
7084
  init_discovery_cache();
6968
7085
  init_fs_utils();
6969
7086
  init_parser();
6970
7087
  import { readdir as readdir12, stat as stat15 } from "fs/promises";
6971
- import { join as join23 } from "path";
7088
+ import { join as join24 } from "path";
6972
7089
  function parseHistoryStartDate(value) {
6973
7090
  if (!value || !/^\d{4}-\d{2}-\d{2}$/.test(value)) return null;
6974
7091
  const [year, month, day] = value.split("-").map(Number);
@@ -6988,7 +7105,7 @@ async function resolveHistoryStart(configured, cachedDates, now = /* @__PURE__ *
6988
7105
  for (const source of sources) {
6989
7106
  const sourceStat = await stat15(source.path).catch(() => null);
6990
7107
  if (!sourceStat) continue;
6991
- const paths = sourceStat.isDirectory() ? (await readdir12(source.path)).filter((name) => name.endsWith(".jsonl")).map((name) => join23(source.path, name)) : source.path.endsWith(".jsonl") ? [source.path] : [];
7108
+ const paths = sourceStat.isDirectory() ? (await readdir12(source.path)).filter((name) => name.endsWith(".jsonl")).map((name) => join24(source.path, name)) : source.path.endsWith(".jsonl") ? [source.path] : [];
6992
7109
  for (const path of paths) {
6993
7110
  const fileStat = await stat15(path).catch(() => null);
6994
7111
  if (!fileStat) continue;
@@ -7007,6 +7124,20 @@ async function resolveHistoryStart(configured, cachedDates, now = /* @__PURE__ *
7007
7124
  }
7008
7125
 
7009
7126
  // src/claude-recovery.ts
7127
+ init_recorded_costs();
7128
+ function lockClaudeRecovery(recovery) {
7129
+ if (!recovery) return null;
7130
+ const rows2 = recovery.models.map((row) => ({
7131
+ ...row,
7132
+ provider: "claude",
7133
+ deduplicationKey: "claude-stats-recovery",
7134
+ cacheCreationInputTokens: row.cacheWriteTokens,
7135
+ cacheReadInputTokens: row.cacheReadTokens
7136
+ }));
7137
+ lockRecordedCosts(rows2);
7138
+ const models = recovery.models.map((row, index) => ({ ...row, costUSD: rows2[index].costUSD }));
7139
+ return { ...recovery, models, costUSD: models.reduce((sum2, row) => sum2 + row.costUSD, 0) };
7140
+ }
7010
7141
  var fields = ["inputTokens", "outputTokens", "cacheReadTokens", "cacheWriteTokens"];
7011
7142
  var emptyTokens = () => ({ inputTokens: 0, outputTokens: 0, cacheReadTokens: 0, cacheWriteTokens: 0 });
7012
7143
  function applyClaudeRecovery(data, recovery) {
@@ -7025,9 +7156,9 @@ function applyClaudeRecovery(data, recovery) {
7025
7156
  }
7026
7157
  }
7027
7158
  async function readClaudeUsageSnapshot() {
7028
- const root = process.env["CLAUDE_CONFIG_DIR"] || join24(homedir12(), ".claude");
7029
- const path = join24(root, "stats-cache.json");
7030
- const savedPath = join24(getCacheDir(), `claude-lifetime-${createHash6("sha256").update(root).digest("hex").slice(0, 16)}.json`);
7159
+ const root = process.env["CLAUDE_CONFIG_DIR"] || join25(homedir12(), ".claude");
7160
+ const path = join25(root, "stats-cache.json");
7161
+ const savedPath = join25(getCacheDir(), `claude-lifetime-${createHash7("sha256").update(root).digest("hex").slice(0, 16)}.json`);
7031
7162
  const read = async (file) => {
7032
7163
  try {
7033
7164
  if ((await stat16(file)).size > 2 * 1024 ** 2) return null;
@@ -7111,7 +7242,7 @@ function recoverClaudeUsage(snapshot, recorded, rangeStart) {
7111
7242
  ...totals,
7112
7243
  costUSD: models.reduce((sum2, row) => sum2 + row.costUSD, 0),
7113
7244
  models,
7114
- note: "Includes historical Claude token counters missing from retained transcripts, reconciled per model and token type to avoid double-counting. Their dates, calls, projects, request sizes, cache TTL and speed are unavailable; recovered cost uses latest standard API rates, short-context rates and 5-minute cache writes. Time-based discounts cannot be reconstructed. Earlier usage without logs or counters remains unknown."
7245
+ note: "Includes historical Claude token counters missing from retained transcripts, reconciled per model and token type to avoid double-counting. Their dates, calls, projects, request sizes, cache TTL and speed are unavailable; recovered cost uses the standard rates available when counted, short-context rates and 5-minute cache writes. Saved estimates are not revalued on pricing refresh. Time-based discounts cannot be reconstructed. Earlier usage without logs or counters remains unknown."
7115
7246
  };
7116
7247
  }
7117
7248
  async function getClaudeRecoveryForRange(range, signal) {
@@ -7132,7 +7263,7 @@ async function getClaudeRecoveryForRange(range, signal) {
7132
7263
  recorded.set(model, total);
7133
7264
  }
7134
7265
  }
7135
- return recoverClaudeUsage(snapshot, recorded, range.start);
7266
+ return lockClaudeRecovery(recoverClaudeUsage(snapshot, recorded, range.start));
7136
7267
  }
7137
7268
 
7138
7269
  // src/latest-load.ts
@@ -7195,8 +7326,8 @@ init_resource_budget();
7195
7326
  init_providers();
7196
7327
  import chalk2 from "chalk";
7197
7328
  import { readdir as readdir13, stat as stat17 } from "fs/promises";
7198
- import { existsSync as existsSync5, statSync as statSync2 } from "fs";
7199
- import { basename as basename10, join as join25 } from "path";
7329
+ import { existsSync as existsSync6, statSync as statSync2 } from "fs";
7330
+ import { basename as basename10, join as join26 } from "path";
7200
7331
  import { homedir as homedir13 } from "os";
7201
7332
  var ORANGE = "#F59E0B";
7202
7333
  var DIM = "#666666";
@@ -7279,13 +7410,13 @@ var DEFAULT_TREND_PERIOD_MS = DEFAULT_TREND_PERIOD_DAYS * 24 * 60 * 60 * 1e3;
7279
7410
  var IMPROVING_THRESHOLD = 0.5;
7280
7411
  async function collectJsonlFiles3(dirPath) {
7281
7412
  const files = await readdir13(dirPath).catch(() => []);
7282
- const result = files.filter((f) => f.endsWith(".jsonl")).map((f) => join25(dirPath, f));
7413
+ const result = files.filter((f) => f.endsWith(".jsonl")).map((f) => join26(dirPath, f));
7283
7414
  for (const entry of files) {
7284
7415
  if (entry.endsWith(".jsonl")) continue;
7285
- const subPath = join25(dirPath, entry, "subagents");
7416
+ const subPath = join26(dirPath, entry, "subagents");
7286
7417
  const subFiles = await readdir13(subPath).catch(() => []);
7287
7418
  for (const sf of subFiles) {
7288
- if (sf.endsWith(".jsonl")) result.push(join25(subPath, sf));
7419
+ if (sf.endsWith(".jsonl")) result.push(join26(subPath, sf));
7289
7420
  }
7290
7421
  }
7291
7422
  return result;
@@ -7475,16 +7606,16 @@ function isReadTool(name) {
7475
7606
  function loadMcpConfigs(projectCwds) {
7476
7607
  const servers = /* @__PURE__ */ new Map();
7477
7608
  const configPaths = [
7478
- join25(homedir13(), ".claude", "settings.json"),
7479
- join25(homedir13(), ".claude", "settings.local.json")
7609
+ join26(homedir13(), ".claude", "settings.json"),
7610
+ join26(homedir13(), ".claude", "settings.local.json")
7480
7611
  ];
7481
7612
  for (const cwd of projectCwds) {
7482
- configPaths.push(join25(cwd, ".mcp.json"));
7483
- configPaths.push(join25(cwd, ".claude", "settings.json"));
7484
- configPaths.push(join25(cwd, ".claude", "settings.local.json"));
7613
+ configPaths.push(join26(cwd, ".mcp.json"));
7614
+ configPaths.push(join26(cwd, ".claude", "settings.json"));
7615
+ configPaths.push(join26(cwd, ".claude", "settings.local.json"));
7485
7616
  }
7486
7617
  for (const p of configPaths) {
7487
- if (!existsSync5(p)) continue;
7618
+ if (!existsSync6(p)) continue;
7488
7619
  const config = readJsonFile(p);
7489
7620
  if (!config) continue;
7490
7621
  let mtime = 0;
@@ -7634,13 +7765,13 @@ function expandImports(filePath, seen, depth) {
7634
7765
  if (content === null) return { totalLines: 0, importedFiles: 0 };
7635
7766
  let totalLines = content.split("\n").length;
7636
7767
  let importedFiles = 0;
7637
- const dir = join25(filePath, "..");
7768
+ const dir = join26(filePath, "..");
7638
7769
  IMPORT_PATTERN.lastIndex = 0;
7639
7770
  for (const match of content.matchAll(IMPORT_PATTERN)) {
7640
7771
  const rawPath = match[1];
7641
7772
  if (!rawPath) continue;
7642
- const resolved = rawPath.startsWith("/") ? rawPath : join25(dir, rawPath);
7643
- if (!existsSync5(resolved)) continue;
7773
+ const resolved = rawPath.startsWith("/") ? rawPath : join26(dir, rawPath);
7774
+ if (!existsSync6(resolved)) continue;
7644
7775
  const nested = expandImports(resolved, seen, depth + 1);
7645
7776
  totalLines += nested.totalLines;
7646
7777
  importedFiles += 1 + nested.importedFiles;
@@ -7651,8 +7782,8 @@ function detectBloatedClaudeMd(projectCwds) {
7651
7782
  const bloated = [];
7652
7783
  for (const cwd of projectCwds) {
7653
7784
  for (const name of ["CLAUDE.md", ".claude/CLAUDE.md"]) {
7654
- const fullPath = join25(cwd, name);
7655
- if (!existsSync5(fullPath)) continue;
7785
+ const fullPath = join26(cwd, name);
7786
+ if (!existsSync6(fullPath)) continue;
7656
7787
  const { totalLines, importedFiles } = expandImports(fullPath, /* @__PURE__ */ new Set(), 0);
7657
7788
  if (totalLines > CLAUDEMD_HEALTHY_LINES) {
7658
7789
  bloated.push({ path: `${shortHomePath(cwd)}/${name}`, expandedLines: totalLines, imports: importedFiles });
@@ -7780,7 +7911,7 @@ function detectCacheBloat(apiCalls, projects, dateRange) {
7780
7911
  };
7781
7912
  }
7782
7913
  async function listMarkdownFiles(dir) {
7783
- if (!existsSync5(dir)) return [];
7914
+ if (!existsSync6(dir)) return [];
7784
7915
  try {
7785
7916
  const entries = await readdir13(dir);
7786
7917
  return entries.filter((e) => e.endsWith(".md")).map((e) => e.replace(/\.md$/, ""));
@@ -7789,12 +7920,12 @@ async function listMarkdownFiles(dir) {
7789
7920
  }
7790
7921
  }
7791
7922
  async function listSkillDirs(dir) {
7792
- if (!existsSync5(dir)) return [];
7923
+ if (!existsSync6(dir)) return [];
7793
7924
  try {
7794
7925
  const entries = await readdir13(dir);
7795
7926
  const names = [];
7796
7927
  for (const entry of entries) {
7797
- if (existsSync5(join25(dir, entry, "SKILL.md"))) names.push(entry);
7928
+ if (existsSync6(join26(dir, entry, "SKILL.md"))) names.push(entry);
7798
7929
  }
7799
7930
  return names;
7800
7931
  } catch {
@@ -7802,7 +7933,7 @@ async function listSkillDirs(dir) {
7802
7933
  }
7803
7934
  }
7804
7935
  async function detectGhostAgents(calls) {
7805
- const defined = await listMarkdownFiles(join25(homedir13(), ".claude", "agents"));
7936
+ const defined = await listMarkdownFiles(join26(homedir13(), ".claude", "agents"));
7806
7937
  if (defined.length === 0) return null;
7807
7938
  const invoked = /* @__PURE__ */ new Set();
7808
7939
  for (const call of calls) {
@@ -7827,7 +7958,7 @@ async function detectGhostAgents(calls) {
7827
7958
  };
7828
7959
  }
7829
7960
  async function detectGhostSkills(calls) {
7830
- const defined = await listSkillDirs(join25(homedir13(), ".claude", "skills"));
7961
+ const defined = await listSkillDirs(join26(homedir13(), ".claude", "skills"));
7831
7962
  if (defined.length === 0) return null;
7832
7963
  const invoked = /* @__PURE__ */ new Set();
7833
7964
  for (const call of calls) {
@@ -7852,7 +7983,7 @@ async function detectGhostSkills(calls) {
7852
7983
  };
7853
7984
  }
7854
7985
  async function detectGhostCommands(userMessages) {
7855
- const defined = await listMarkdownFiles(join25(homedir13(), ".claude", "commands"));
7986
+ const defined = await listMarkdownFiles(join26(homedir13(), ".claude", "commands"));
7856
7987
  if (defined.length === 0) return null;
7857
7988
  const invoked = /* @__PURE__ */ new Set();
7858
7989
  for (const msg of userMessages) {
@@ -7880,8 +8011,8 @@ async function detectGhostCommands(userMessages) {
7880
8011
  }
7881
8012
  function readShellProfileLimit() {
7882
8013
  for (const profile of SHELL_PROFILES) {
7883
- const path = join25(homedir13(), profile);
7884
- if (!existsSync5(path)) continue;
8014
+ const path = join26(homedir13(), profile);
8015
+ if (!existsSync6(path)) continue;
7885
8016
  const content = readSessionFileSync(path);
7886
8017
  if (content === null) continue;
7887
8018
  const match = content.match(/^\s*export\s+BASH_MAX_OUTPUT_LENGTH\s*=\s*['"]?(\d+)['"]?/m);
@@ -8104,7 +8235,7 @@ import { spawn as spawn2 } from "child_process";
8104
8235
  import { randomUUID as randomUUID4 } from "crypto";
8105
8236
  import { stat as stat18, writeFile as writeFile5 } from "fs/promises";
8106
8237
  import { homedir as homedir14 } from "os";
8107
- import { join as join26, resolve as resolve3 } from "path";
8238
+ import { join as join27, resolve as resolve3 } from "path";
8108
8239
  function codeBlock(content, language) {
8109
8240
  const longest = (content.match(/`+/g) ?? []).reduce((max, run) => Math.max(max, run.length), 0);
8110
8241
  const fence = "`".repeat(Math.max(3, longest + 1));
@@ -8198,7 +8329,7 @@ async function copyOptimizeMarkdown(markdown) {
8198
8329
  throw new Error("Could not copy. Press e to save a Markdown file instead.");
8199
8330
  }
8200
8331
  async function saveOptimizeMarkdown(markdown, directory) {
8201
- const downloads = join26(homedir14(), "Downloads");
8332
+ const downloads = join27(homedir14(), "Downloads");
8202
8333
  const folder = directory ?? ((await stat18(downloads).catch(() => null))?.isDirectory() ? downloads : process.cwd());
8203
8334
  const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
8204
8335
  const path = resolve3(folder, `exe-watcher-optimizer-${stamp}-${randomUUID4().slice(0, 8)}.md`);
@@ -8209,8 +8340,8 @@ async function saveOptimizeMarkdown(markdown, directory) {
8209
8340
  // src/context-budget.ts
8210
8341
  init_fs_utils();
8211
8342
  import { readdir as readdir14 } from "fs/promises";
8212
- import { existsSync as existsSync6 } from "fs";
8213
- import { join as join27 } from "path";
8343
+ import { existsSync as existsSync7 } from "fs";
8344
+ import { join as join28 } from "path";
8214
8345
  import { homedir as homedir15 } from "os";
8215
8346
  var CHARS_PER_TOKEN2 = 4;
8216
8347
  var SYSTEM_BASE_TOKENS = 10400;
@@ -8220,7 +8351,7 @@ function estimateTokens2(text) {
8220
8351
  return Math.ceil(text.length / CHARS_PER_TOKEN2);
8221
8352
  }
8222
8353
  async function readConfigFile(path) {
8223
- if (!existsSync6(path)) return null;
8354
+ if (!existsSync7(path)) return null;
8224
8355
  const raw = await readSessionFile(path);
8225
8356
  if (raw === null) return null;
8226
8357
  try {
@@ -8232,13 +8363,13 @@ async function readConfigFile(path) {
8232
8363
  async function countMcpTools(projectPath) {
8233
8364
  const home = homedir15();
8234
8365
  const configPaths = [
8235
- join27(home, ".claude", "settings.json"),
8236
- join27(home, ".claude", "settings.local.json")
8366
+ join28(home, ".claude", "settings.json"),
8367
+ join28(home, ".claude", "settings.local.json")
8237
8368
  ];
8238
8369
  if (projectPath) {
8239
- configPaths.push(join27(projectPath, ".mcp.json"));
8240
- configPaths.push(join27(projectPath, ".claude", "settings.json"));
8241
- configPaths.push(join27(projectPath, ".claude", "settings.local.json"));
8370
+ configPaths.push(join28(projectPath, ".mcp.json"));
8371
+ configPaths.push(join28(projectPath, ".claude", "settings.json"));
8372
+ configPaths.push(join28(projectPath, ".claude", "settings.local.json"));
8242
8373
  }
8243
8374
  const servers = /* @__PURE__ */ new Set();
8244
8375
  let toolCount = 0;
@@ -8255,16 +8386,16 @@ async function countMcpTools(projectPath) {
8255
8386
  return toolCount;
8256
8387
  }
8257
8388
  async function countSkills(projectPath) {
8258
- const dirs = [join27(homedir15(), ".claude", "skills")];
8259
- if (projectPath) dirs.push(join27(projectPath, ".claude", "skills"));
8389
+ const dirs = [join28(homedir15(), ".claude", "skills")];
8390
+ if (projectPath) dirs.push(join28(projectPath, ".claude", "skills"));
8260
8391
  let count = 0;
8261
8392
  for (const dir of dirs) {
8262
- if (!existsSync6(dir)) continue;
8393
+ if (!existsSync7(dir)) continue;
8263
8394
  try {
8264
8395
  const entries = await readdir14(dir);
8265
8396
  for (const entry of entries) {
8266
- const skillFile = join27(dir, entry, "SKILL.md");
8267
- if (existsSync6(skillFile)) count++;
8397
+ const skillFile = join28(dir, entry, "SKILL.md");
8398
+ if (existsSync7(skillFile)) count++;
8268
8399
  }
8269
8400
  } catch {
8270
8401
  continue;
@@ -8276,15 +8407,15 @@ async function scanMemoryFiles(projectPath) {
8276
8407
  const home = homedir15();
8277
8408
  const files = [];
8278
8409
  const paths = [
8279
- { path: join27(home, ".claude", "CLAUDE.md"), name: "~/.claude/CLAUDE.md" }
8410
+ { path: join28(home, ".claude", "CLAUDE.md"), name: "~/.claude/CLAUDE.md" }
8280
8411
  ];
8281
8412
  if (projectPath) {
8282
- paths.push({ path: join27(projectPath, "CLAUDE.md"), name: "CLAUDE.md" });
8283
- paths.push({ path: join27(projectPath, ".claude", "CLAUDE.md"), name: ".claude/CLAUDE.md" });
8284
- paths.push({ path: join27(projectPath, "CLAUDE.local.md"), name: "CLAUDE.local.md" });
8413
+ paths.push({ path: join28(projectPath, "CLAUDE.md"), name: "CLAUDE.md" });
8414
+ paths.push({ path: join28(projectPath, ".claude", "CLAUDE.md"), name: ".claude/CLAUDE.md" });
8415
+ paths.push({ path: join28(projectPath, "CLAUDE.local.md"), name: "CLAUDE.local.md" });
8285
8416
  }
8286
8417
  for (const { path, name } of paths) {
8287
- if (!existsSync6(path)) continue;
8418
+ if (!existsSync7(path)) continue;
8288
8419
  const content = await readSessionFile(path);
8289
8420
  if (content === null) continue;
8290
8421
  files.push({ name, tokens: estimateTokens2(content) });
@@ -8318,7 +8449,7 @@ async function discoverProjectCwd(sessionDir) {
8318
8449
  if (files.length === 0) return null;
8319
8450
  const firstFile = files[0];
8320
8451
  if (!firstFile) return null;
8321
- const content = await readSessionFile(join27(sessionDir, firstFile));
8452
+ const content = await readSessionFile(join28(sessionDir, firstFile));
8322
8453
  if (content === null) return null;
8323
8454
  for (const line of content.split("\n")) {
8324
8455
  if (!line.trim()) continue;
@@ -9012,20 +9143,20 @@ function owlFrame(tick) {
9012
9143
  }
9013
9144
 
9014
9145
  // src/dashboard.tsx
9015
- import { join as join29 } from "path";
9146
+ import { join as join30 } from "path";
9016
9147
 
9017
9148
  // src/time-index.ts
9018
9149
  init_cache_dir();
9019
9150
  init_file_lock();
9020
9151
  init_resource_budget();
9021
- import { createHash as createHash8, randomUUID as randomUUID5 } from "crypto";
9152
+ import { createHash as createHash9, randomUUID as randomUUID5 } from "crypto";
9022
9153
  import { mkdir as mkdir13, open as open9, readFile as readFile14, rename as rename11, stat as stat19, unlink as unlink9, readdir as readdir15 } from "fs/promises";
9023
- import { basename as basename11, dirname as dirname6, join as join28 } from "path";
9154
+ import { basename as basename11, dirname as dirname6, join as join29 } from "path";
9024
9155
  import { gzip as gzip2, gunzip as gunzip2 } from "zlib";
9025
9156
  import { promisify as promisify2 } from "util";
9026
9157
 
9027
9158
  // src/time-tracking.ts
9028
- import { createHash as createHash7 } from "crypto";
9159
+ import { createHash as createHash8 } from "crypto";
9029
9160
  var TIME_HISTORY_START = (/* @__PURE__ */ new Date("2025-07-01T00:00:00")).getTime();
9030
9161
  var TIME_PROVIDERS = ["claude", "codex", "cursor", "cursor-agent", "copilot", "opencode", "kimi", "pi", "omp"];
9031
9162
  function timestamp(value) {
@@ -9238,7 +9369,7 @@ function summarizeTime(index, range, provider = "all", include, exclude) {
9238
9369
  runnableFiles: pending.length - waitingFiles,
9239
9370
  waitingFiles,
9240
9371
  pendingBytes: pending.reduce((n, f) => n + Math.max(0, f.size - f.offset), 0),
9241
- token: createHash7("sha256").update(files.map((f) => `${f.identity}:${f.size}:${f.mtime}:${f.offset}:${f.dbOffset ?? 0}:${!!f.waitingForAppend}:${!!f.unavailable}`).join("|")).digest("hex")
9372
+ token: createHash8("sha256").update(files.map((f) => `${f.identity}:${f.size}:${f.mtime}:${f.offset}:${f.dbOffset ?? 0}:${!!f.waitingForAppend}:${!!f.unavailable}`).join("|")).digest("hex")
9242
9373
  };
9243
9374
  return {
9244
9375
  indexedAt: index.updatedAt,
@@ -9371,7 +9502,7 @@ var MAX_DISK2 = 16 * 1024 ** 2;
9371
9502
  var MAX_FILES = 2e4;
9372
9503
  var BLOCK = 2 * 1024 ** 2;
9373
9504
  var adapterVersion = (provider) => provider === "opencode" ? 2 : 1;
9374
- var indexPath2 = () => join28(getCacheDir(), "time-index-v1.json.gz");
9505
+ var indexPath2 = () => join29(getCacheDir(), "time-index-v1.json.gz");
9375
9506
  var empty = () => ({ version: 1, files: /* @__PURE__ */ Object.create(null), updatedAt: (/* @__PURE__ */ new Date()).toISOString(), warnings: [] });
9376
9507
  async function readTimeIndex() {
9377
9508
  try {
@@ -9434,11 +9565,11 @@ async function expandSources(sources, warnings) {
9434
9565
  continue;
9435
9566
  }
9436
9567
  for (const entry of await readdir15(source.path, { withFileTypes: true }).catch(() => [])) {
9437
- if (entry.isFile() && entry.name.endsWith(".jsonl")) out.push({ ...source, path: join28(source.path, entry.name) });
9568
+ if (entry.isFile() && entry.name.endsWith(".jsonl")) out.push({ ...source, path: join29(source.path, entry.name) });
9438
9569
  else if (entry.isDirectory()) {
9439
- const folder = join28(source.path, entry.name, "subagents");
9570
+ const folder = join29(source.path, entry.name, "subagents");
9440
9571
  for (const agent of await readdir15(folder, { withFileTypes: true }).catch(() => [])) {
9441
- if (agent.isFile() && agent.name.endsWith(".jsonl")) out.push({ ...source, path: join28(folder, agent.name) });
9572
+ if (agent.isFile() && agent.name.endsWith(".jsonl")) out.push({ ...source, path: join29(folder, agent.name) });
9442
9573
  }
9443
9574
  }
9444
9575
  }
@@ -9452,7 +9583,7 @@ async function anchor(path, offset) {
9452
9583
  await handle.read(head, 0, head.length, 0);
9453
9584
  await handle.read(tail, 0, tail.length, Math.max(0, offset - tail.length));
9454
9585
  chargeRead(head.length + tail.length);
9455
- return createHash8("sha256").update(head).update(tail).digest("hex");
9586
+ return createHash9("sha256").update(head).update(tail).digest("hex");
9456
9587
  } finally {
9457
9588
  await handle.close();
9458
9589
  }
@@ -9535,7 +9666,7 @@ async function refreshTimeIndex(options = {}) {
9535
9666
  let current;
9536
9667
  let release;
9537
9668
  try {
9538
- release = await acquireFileLock(join28(getCacheDir(), "time-index.lock"), 100);
9669
+ release = await acquireFileLock(join29(getCacheDir(), "time-index.lock"), 100);
9539
9670
  } catch {
9540
9671
  current = await readTimeIndex();
9541
9672
  current.warnings.push("Another timing update is running; showing the saved timing snapshot.");
@@ -9555,17 +9686,17 @@ async function refreshTimeIndex(options = {}) {
9555
9686
  let fileCount = Object.keys(current.files).length;
9556
9687
  for (const source of expanded) {
9557
9688
  chargeRead(0);
9558
- const database = source.provider === "opencode" ? source.path.slice(0, source.path.lastIndexOf(":")) : source.path;
9559
- const s = await stat19(database).catch(() => null);
9689
+ const database2 = source.provider === "opencode" ? source.path.slice(0, source.path.lastIndexOf(":")) : source.path;
9690
+ const s = await stat19(database2).catch(() => null);
9560
9691
  if (!s) continue;
9561
- const wal = ["cursor", "opencode"].includes(source.provider) ? await stat19(database + "-wal").catch(() => null) : null;
9692
+ const wal = ["cursor", "opencode"].includes(source.provider) ? await stat19(database2 + "-wal").catch(() => null) : null;
9562
9693
  candidates.push({ source, s, wal });
9563
9694
  }
9564
9695
  candidates.sort((a, b) => b.s.mtimeMs - a.s.mtimeMs || a.source.path.localeCompare(b.source.path));
9565
9696
  if (candidates.length > MAX_FILES) current.warnings.push("Some timing sources exceed the bounded file limit; their time remains unknown.");
9566
9697
  const deadline = Date.now() + (options.history ? 15e3 : 750);
9567
9698
  for (const { source, s, wal } of candidates) {
9568
- const key2 = createHash8("sha256").update(`${source.provider}:${source.path}`).digest("hex");
9699
+ const key2 = createHash9("sha256").update(`${source.provider}:${source.path}`).digest("hex");
9569
9700
  if (!current.files[key2] && fileCount < MAX_FILES) {
9570
9701
  current.files[key2] = fresh(source, `${s.dev}:${s.ino}:${wal?.size ?? 0}:${wal?.mtimeMs ?? 0}`, s.size, s.mtimeMs, s.ctimeMs);
9571
9702
  changed = true;
@@ -9577,7 +9708,7 @@ async function refreshTimeIndex(options = {}) {
9577
9708
  }
9578
9709
  for (const { source, s, wal } of candidates) {
9579
9710
  chargeRead(0);
9580
- const key2 = createHash8("sha256").update(`${source.provider}:${source.path}`).digest("hex");
9711
+ const key2 = createHash9("sha256").update(`${source.provider}:${source.path}`).digest("hex");
9581
9712
  const identity = `${s.dev}:${s.ino}:${wal?.size ?? 0}:${wal?.mtimeMs ?? 0}`;
9582
9713
  let row = current.files[key2];
9583
9714
  const sameAdapter = (row?.adapterVersion ?? 1) === adapterVersion(source.provider);
@@ -10482,11 +10613,11 @@ function InteractiveDashboard({ initialPeriod, initialProvider, refreshSeconds,
10482
10613
  useEffect2(() => {
10483
10614
  let cancelled = false;
10484
10615
  async function loadBudgets() {
10485
- const claudeDir = join29(homedir16(), ".claude", "projects");
10616
+ const claudeDir = join30(homedir16(), ".claude", "projects");
10486
10617
  const budgets = /* @__PURE__ */ new Map();
10487
10618
  for (const project of projects.slice(0, 8)) {
10488
10619
  if (cancelled) return;
10489
- const cwd = await discoverProjectCwd(join29(claudeDir, project.project));
10620
+ const cwd = await discoverProjectCwd(join30(claudeDir, project.project));
10490
10621
  if (!cwd) continue;
10491
10622
  budgets.set(project.project, await estimateContextBudget(cwd));
10492
10623
  }
@@ -10809,6 +10940,10 @@ function shouldSkipBlockedChunk(cursor2, date2, maxAttempts = MAX_BLOCKED_CHUNK_
10809
10940
  // src/period-snapshots.ts
10810
10941
  init_day_aggregator();
10811
10942
  init_models();
10943
+ import { createHash as createHash10 } from "crypto";
10944
+ function hasRecordedHistory(cache, today) {
10945
+ return cache.days.some((day) => day.date < today && (day.cost > 0 || day.calls > 0 || day.inputTokens > 0 || day.outputTokens > 0));
10946
+ }
10812
10947
  function assertHistoryComplete(cache, start, today) {
10813
10948
  if (start >= today) return;
10814
10949
  const required = Math.round((Date.parse(today) - Date.parse(start)) / 864e5);
@@ -10830,7 +10965,26 @@ function buildPeriodSnapshots(cache, todayDays, today, windows, provider, names,
10830
10965
  for (const window of windows) {
10831
10966
  const required = Math.max(0, Math.round((Date.parse(today) - Date.parse(window.start)) / 864e5));
10832
10967
  const covered = [...dates].filter((d) => d >= window.start && !partial.has(d)).length;
10833
- if (window.key !== "today" && (cache.needsRepricing || cache.historyBuild || cache.missingProviders?.length || covered !== required)) continue;
10968
+ const incomplete = window.key !== "today" && (cache.needsRepricing || cache.historyBuild || cache.missingProviders?.length || covered !== required);
10969
+ const committed = closed.filter((day) => day.date >= window.start).sort((a, b) => a.date.localeCompare(b.date));
10970
+ const recorded = (day) => day.cost > 0 || day.calls > 0 || day.inputTokens > 0 || day.outputTokens > 0;
10971
+ const hasData = committed.some(recorded) || window.key !== "all" && live2.some(recorded);
10972
+ const savedHistory = incomplete && (window.key !== "all" || hasData) ? {
10973
+ through: committed.at(-1)?.date ?? today,
10974
+ hasData,
10975
+ progressToken: createHash10("sha256").update(JSON.stringify([
10976
+ window.start,
10977
+ cache.lastComputedDate,
10978
+ cache.days.length,
10979
+ cache.historyBuild?.completedProviders,
10980
+ cache.historyBuild?.chunked,
10981
+ cache.missingProviders,
10982
+ cache.partialDates,
10983
+ cache.backfill
10984
+ ])).digest("hex"),
10985
+ message: window.key === "all" ? `Saved history through ${committed.at(-1).date}${live2.length ? ", plus today" : ""}. Some history is still missing; this is not the complete All total.` : `${hasData ? "Showing known usage" : "Recovering local usage"} for ${window.label}. ${covered} of ${required} historical days loaded${live2.some(recorded) ? ", plus today" : ""}. Missing days are unknown; this total is incomplete.`
10986
+ } : void 0;
10987
+ if (incomplete && !savedHistory) continue;
10834
10988
  const selected = periodDays(window.start);
10835
10989
  const scoped = provider === "all" ? selected : filterDaysToProvider(selected, provider);
10836
10990
  const data = buildPeriodDataFromDays(scoped, window.label);
@@ -10852,6 +11006,7 @@ function buildPeriodSnapshots(cache, todayDays, today, windows, provider, names,
10852
11006
  const providers = provider === "all" ? Object.entries(totals).map(([name, cost]) => ({ name: names.get(name) ?? name, cost })) : [{ name: names.get(provider) ?? provider, cost: data.cost }];
10853
11007
  const built = buildMenubarPayload(data, providers, null);
10854
11008
  result[window.key] = {
11009
+ ...savedHistory ? { savedHistory } : {},
10855
11010
  current: built.current,
10856
11011
  projectSpend: buildProjectSpendFromDays(selected, live2, periodDays(week.start), periodDays(month.start)),
10857
11012
  diagnostics: { daysCount: selected.length, parseTimeMs, warnings: notes },
@@ -10862,6 +11017,23 @@ function buildPeriodSnapshots(cache, todayDays, today, windows, provider, names,
10862
11017
  return result;
10863
11018
  }
10864
11019
 
11020
+ // src/saved-history.ts
11021
+ init_day_aggregator();
11022
+ function buildSavedHistory(cache, today, provider, window = { start: "0000-01-01", label: "All Time" }) {
11023
+ if (!hasRecordedHistory(cache, today)) throw new Error("No saved history yet. Watcher is recovering local usage.");
11024
+ const closed = cache.days.filter((day) => day.date >= window.start && day.date < today).sort((a, b) => a.date.localeCompare(b.date));
11025
+ if (!closed.some((day) => day.cost > 0 || day.calls > 0 || day.inputTokens > 0 || day.outputTokens > 0)) throw new Error("No saved usage in this period yet. Watcher is recovering local usage.");
11026
+ const days = provider === "all" ? closed : filterDaysToProvider(closed, provider);
11027
+ const current = buildPeriodDataFromDays(days, window.label);
11028
+ const totals = /* @__PURE__ */ new Map();
11029
+ for (const day of days) for (const [name, data] of Object.entries(day.providers)) totals.set(name, (totals.get(name) ?? 0) + data.cost);
11030
+ const payload = buildMenubarPayload(current, [...totals].map(([name, cost]) => ({ name, cost })), null);
11031
+ return { ...payload, savedHistory: {
11032
+ through: closed.at(-1).date,
11033
+ message: `Saved ${window.label} history through ${closed.at(-1).date}. Newer usage has not loaded yet.`
11034
+ } };
11035
+ }
11036
+
10865
11037
  // src/history-build.ts
10866
11038
  init_day_aggregator();
10867
11039
  init_report_summary();
@@ -10961,7 +11133,7 @@ async function completeHistoryBuild(cache, start, end, providers, scan, save2, o
10961
11133
 
10962
11134
  // src/cli.ts
10963
11135
  import { createInterface } from "readline";
10964
- import { createRequire as createRequire2 } from "module";
11136
+ import { createRequire as createRequire3 } from "module";
10965
11137
 
10966
11138
  // src/menubar-lifecycle.ts
10967
11139
  var live = { parent: () => process.ppid, checkAlive: (pid) => {
@@ -10995,8 +11167,8 @@ function buildTimeSnapshots(index, ranges, provider = "all", include, exclude) {
10995
11167
  }
10996
11168
 
10997
11169
  // src/cli.ts
10998
- var require2 = createRequire2(import.meta.url);
10999
- var { version } = require2("../package.json");
11170
+ var require3 = createRequire3(import.meta.url);
11171
+ var { version } = require3("../package.json");
11000
11172
  var MS_PER_DAY4 = 24 * 60 * 60 * 1e3;
11001
11173
  var configuredHistoryStart = null;
11002
11174
  function toDateString(date2) {
@@ -11111,14 +11283,14 @@ async function runJsonReport(period, provider, project, exclude) {
11111
11283
  console.log(JSON.stringify({ ...report, pricing: getPricingMetadata() }, null, 2));
11112
11284
  }
11113
11285
  var program = new Command().name("exe-watcher").description("See where your AI coding tokens go - by task, tool, model, and project").version(version).option("--verbose", "print warnings to stderr on read failures and skipped files");
11114
- program.hook("preAction", async (thisCommand) => {
11286
+ program.hook("preAction", async (thisCommand, command) => {
11115
11287
  const config = await readConfig();
11116
11288
  setModelAliases(config.modelAliases ?? {});
11117
11289
  configuredHistoryStart = parseHistoryStartDate(config.historyStartDate);
11118
11290
  if (thisCommand.opts().verbose) {
11119
11291
  process.env["EXE_WATCHER_VERBOSE"] = "1";
11120
11292
  }
11121
- await loadCurrency();
11293
+ if (!command.opts().cachedOnly) await loadCurrency();
11122
11294
  });
11123
11295
  function buildJsonReport(projects, period, periodKey) {
11124
11296
  const sessions = projects.flatMap((p) => p.sessions);
@@ -11308,7 +11480,19 @@ function buildPeriodData(label, projects) {
11308
11480
  models: Object.entries(modelTotals).sort(([, a], [, b]) => b.cost - a.cost).map(([name, d]) => ({ name, ...d }))
11309
11481
  };
11310
11482
  }
11311
- program.command("status").description("Compact status output (today + week + month)").option("--format <format>", "Output format: terminal, menubar-json, json", "terminal").option("--provider <provider>", "Filter by provider: all, claude, codex, cursor, cursor-agent, copilot, opencode, kimi, pi, omp", "all").option("--project <name>", "Show only projects matching name (repeatable)", collect, []).option("--exclude <name>", "Exclude projects matching name (repeatable)", collect, []).option("--period <period>", "Primary period for menubar-json: today, week, 30days, month, all", "today").option("--no-optimize", "Skip optimize findings (menubar-json only, faster)").action(async (opts) => {
11483
+ program.command("status").description("Compact status output (today + week + month)").option("--format <format>", "Output format: terminal, menubar-json, json", "terminal").option("--provider <provider>", "Filter by provider: all, claude, codex, cursor, cursor-agent, copilot, opencode, kimi, pi, omp", "all").option("--project <name>", "Show only projects matching name (repeatable)", collect, []).option("--exclude <name>", "Exclude projects matching name (repeatable)", collect, []).option("--period <period>", "Primary period for menubar-json: today, week, 30days, month, all", "today").option("--no-optimize", "Skip optimize findings (menubar-json only, faster)").option("--cached-only", "Read saved period totals without scanning or networking (menubar-json only)").action(async (opts) => {
11484
+ if (opts.cachedOnly) {
11485
+ if (opts.format !== "menubar-json" || !["week", "30days", "month", "all"].includes(opts.period) || opts.optimize !== false) throw new Error("--cached-only requires menubar-json, a historical period and --no-optimize");
11486
+ const cache = await loadDailyCache(buildDailyCacheScopeKey(opts.project, opts.exclude));
11487
+ const window = getDateRange2(opts.period);
11488
+ console.log(JSON.stringify(buildSavedHistory(
11489
+ cache,
11490
+ toDateString(/* @__PURE__ */ new Date()),
11491
+ opts.provider,
11492
+ { start: opts.period === "all" ? "0000-01-01" : toDateString(window.range.start), label: window.label }
11493
+ )));
11494
+ return;
11495
+ }
11312
11496
  await loadPricing();
11313
11497
  const pf = opts.provider;
11314
11498
  const fp = (p) => filterProjectsByName(p, opts.project, opts.exclude);
@@ -11388,7 +11572,7 @@ program.command("status").description("Compact status output (today + week + mon
11388
11572
  backfillWarnings.push(`History rebuild paused at ${c.historyBuild.chunked?.nextDate ?? c.historyBuild.start}; completed scans are saved and will resume on the next historical refresh.`);
11389
11573
  return true;
11390
11574
  };
11391
- const cache = await withDailyCacheLock(async () => {
11575
+ const cache = await withScanBudget(() => withScanSlice(() => withDailyCacheLock(async () => {
11392
11576
  let c = await loadDailyCache(dailyCacheScope);
11393
11577
  if (opts.period === "today" && opts.optimize === false) return c;
11394
11578
  if (c.missingProviders?.length && c.days.length && !c.needsRepricing) {
@@ -11405,19 +11589,13 @@ program.command("status").description("Compact status output (today + week + mon
11405
11589
  c = await buildHistory2(c, from, through);
11406
11590
  if (pausedHistory(c)) return c;
11407
11591
  }
11408
- if (c.needsRepricing || opts.period === "all" && c.days.length === 0) {
11592
+ if (opts.period === "all" && c.days.length === 0) {
11409
11593
  const start = parseHistoryStartDate(c.previousHistoryStart) ?? historyStart;
11410
11594
  if (start <= yesterdayEnd) {
11411
11595
  c = await buildHistory2(c, start, yesterdayEnd);
11412
11596
  if (pausedHistory(c)) return c;
11413
11597
  }
11414
11598
  }
11415
- const hadYesterday = c.days.some((d) => d.date >= yesterdayStr);
11416
- if (hadYesterday && (!c.revalidatedAt || Date.now() - c.revalidatedAt >= 5 * 6e4)) {
11417
- const freshDays = c.days.filter((d) => d.date < yesterdayStr);
11418
- const latestFresh = freshDays.length > 0 ? freshDays[freshDays.length - 1]?.date ?? null : null;
11419
- c = { ...c, days: freshDays, lastComputedDate: latestFresh };
11420
- }
11421
11599
  const oldestCachedDate = c.days.length > 0 ? c.days[0]?.date ?? null : null;
11422
11600
  const effectiveGapStart = computeProgressiveBackfillStart({
11423
11601
  lastComputedDate: c.lastComputedDate,
@@ -11442,8 +11620,10 @@ program.command("status").description("Compact status output (today + week + mon
11442
11620
  }
11443
11621
  }
11444
11622
  return c;
11445
- });
11446
- assertHistoryComplete(cache, toDateString(periodInfo.range.start), toDateString(todayStart));
11623
+ }), { maxMs: 1e4, maxBytes: 8 * 1024 ** 3, maxRecords: 2e6 }), 2e6, void 0, 8 * 1024 ** 3);
11624
+ if (opts.optimize !== false || opts.period === "all" && !hasRecordedHistory(cache, toDateString(todayStart))) {
11625
+ assertHistoryComplete(cache, toDateString(periodInfo.range.start), toDateString(todayStart));
11626
+ }
11447
11627
  let currentData;
11448
11628
  const scanRange = periodInfo.range;
11449
11629
  const todayRange = { start: todayStart, end: periodInfo.range.end };
@@ -11517,7 +11697,7 @@ program.command("status").description("Compact status output (today + week + mon
11517
11697
  let historicalRecovery = null;
11518
11698
  if ((opts.period === "all" || opts.optimize === false) && (isAllProviders || pf === "claude") && !opts.project.length && !opts.exclude.length && !cache.partialDates?.length && !cache.backfill) {
11519
11699
  const snapshot = await readClaudeUsageSnapshot();
11520
- historicalRecovery = recoverClaudeUsage(snapshot, sumClaudeModelTokens([...cache.days, ...todayDays], snapshot?.lastComputedDate ?? ""), historyStart);
11700
+ historicalRecovery = lockClaudeRecovery(recoverClaudeUsage(snapshot, sumClaudeModelTokens([...cache.days, ...todayDays], snapshot?.lastComputedDate ?? ""), historyStart));
11521
11701
  if (historicalRecovery && opts.period === "all") {
11522
11702
  applyClaudeRecovery(currentData, historicalRecovery);
11523
11703
  const claudeProvider = providers.find((p) => p.name.toLowerCase() === "claude");
@@ -11585,13 +11765,13 @@ program.command("status").description("Compact status output (today + week + mon
11585
11765
  `);
11586
11766
  }
11587
11767
  }
11588
- const exeOsDir = join30(homedir17(), ".exe-os");
11589
- const exeOsDetected = existsSync7(exeOsDir);
11768
+ const exeOsDir = join31(homedir17(), ".exe-os");
11769
+ const exeOsDetected = existsSync8(exeOsDir);
11590
11770
  let agentStats = null;
11591
11771
  let statsFileAge = null;
11592
11772
  try {
11593
- const statsPath = join30(exeOsDir, "agent-stats.json");
11594
- if (existsSync7(statsPath)) {
11773
+ const statsPath = join31(exeOsDir, "agent-stats.json");
11774
+ if (existsSync8(statsPath)) {
11595
11775
  statsFileAge = Math.round((Date.now() - statSync3(statsPath).mtimeMs) / 1e3);
11596
11776
  const parsed = JSON.parse(readFileSync2(statsPath, "utf-8"));
11597
11777
  if (parsed && Array.isArray(parsed.agents) && typeof parsed.generated === "string") {
@@ -11654,7 +11834,15 @@ program.command("status").description("Compact status output (today + week + mon
11654
11834
  if (periodSnapshots) for (const [key2, snapshot] of Object.entries(periodSnapshots)) {
11655
11835
  snapshot.time = summarizeTime(timeIndex, getDateRange2(key2, now).range, pf, opts.project, opts.exclude);
11656
11836
  }
11657
- const json = JSON.stringify({ ...payload, time, periodSnapshots, costBasis: "api-equivalent", pricing: getPricingMetadata(), historicalRecovery: opts.period === "all" ? historicalRecovery : null });
11837
+ const json = JSON.stringify({
11838
+ ...payload,
11839
+ time,
11840
+ periodSnapshots,
11841
+ ...periodSnapshots?.[opts.period]?.savedHistory ? { savedHistory: periodSnapshots[opts.period].savedHistory } : {},
11842
+ costBasis: "api-equivalent",
11843
+ pricing: getPricingMetadata(),
11844
+ historicalRecovery: opts.period === "all" ? historicalRecovery : null
11845
+ });
11658
11846
  console.log(json);
11659
11847
  return;
11660
11848
  }
@@ -11984,12 +12172,12 @@ program.command("compare").description("Compare two AI models side-by-side").opt
11984
12172
  await renderCompare(range, opts.provider);
11985
12173
  });
11986
12174
  program.command("agent-status").description("Diagnose exe-os agent data pipeline").action(() => {
11987
- const exeOsDir = join30(homedir17(), ".exe-os");
11988
- const statsPath = join30(exeOsDir, "agent-stats.json");
11989
- const pidPath = join30(exeOsDir, "exed.pid");
11990
- const dirExists = existsSync7(exeOsDir);
11991
- const statsExists = dirExists && existsSync7(statsPath);
11992
- const pidExists = dirExists && existsSync7(pidPath);
12175
+ const exeOsDir = join31(homedir17(), ".exe-os");
12176
+ const statsPath = join31(exeOsDir, "agent-stats.json");
12177
+ const pidPath = join31(exeOsDir, "exed.pid");
12178
+ const dirExists = existsSync8(exeOsDir);
12179
+ const statsExists = dirExists && existsSync8(statsPath);
12180
+ const pidExists = dirExists && existsSync8(pidPath);
11993
12181
  const lines = [];
11994
12182
  lines.push(`exe-os directory: ${dirExists ? "\u2713 found" : "\u2717 not found"} (${exeOsDir})`);
11995
12183
  lines.push(`daemon pid file: ${pidExists ? "\u2713 found" : "\u2717 not found"}`);
@@ -12021,7 +12209,7 @@ program.command("agent-status").description("Diagnose exe-os agent data pipeline
12021
12209
  var releaseScan;
12022
12210
  var stopParentWatch = watchMenubarParent(Number(process.env["EXE_WATCHER_PARENT_PID"]));
12023
12211
  program.hook("preAction", async (_root, command) => {
12024
- if (command.name() === "status") releaseScan = await acquireFileLock(join30(getCacheDir(), "scan.lock"));
12212
+ if (command.name() === "status" && !command.opts().cachedOnly) releaseScan = await acquireFileLock(join31(getCacheDir(), "scan.lock"));
12025
12213
  });
12026
12214
  program.hook("postAction", async () => {
12027
12215
  await releaseScan?.();