ccsidekick 1.6.1 → 1.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,14 +1,15 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/bin/ccsidekick-render.ts
4
- import { readFileSync as readFileSync11 } from "node:fs";
4
+ import { readFileSync as readFileSync12 } from "node:fs";
5
5
 
6
6
  // src/cli/classify.ts
7
7
  import { createHash as createHash4 } from "node:crypto";
8
8
 
9
9
  // src/domain/constants.ts
10
- var COST_TTL_MS = 1500;
10
+ var COST_TTL_MS = 5000;
11
11
  var USAGE_TTL_MS = 300000;
12
+ var CREDS_TTL_MS = 3600000;
12
13
  var BALANCE_FRESHNESS_MS = 300000;
13
14
  var FX_TTL_MS = 604800000;
14
15
  var MIN_RIGHT_WIDTH = 53;
@@ -33,6 +34,8 @@ var HELPFUL_COOLDOWN_MS = 600000;
33
34
  var BURN_WINDOW_MS = 18000000;
34
35
  var COMPACT_URGENT_PCT = 90;
35
36
  var QUOTA_HIGH_PCT = 80;
37
+ var QUOTA_PACE_MIN_PCT = 20;
38
+ var QUOTA_PROJECT_MIN_PCT = 50;
36
39
  var PAY_AS_YOU_GO_CAUTION_PCT = 60;
37
40
  var PAY_AS_YOU_GO_NEAR_PCT = 80;
38
41
  var BALANCE_LOW = 25;
@@ -44,6 +47,17 @@ var SIGNAL_HUE_RANGES = {
44
47
  caution: { min: 20, max: 55 },
45
48
  critical: { wrapMax: 15, wrapMin: 345 }
46
49
  };
50
+ var COMPACT_SOON_PCT = 60;
51
+ var COMPACTION_THRASH_N = 3;
52
+ var CACHE_RATIO_FLOOR = 0.5;
53
+ var CACHE_WARMUP_TURNS = 20;
54
+ var BIG_DIFF_LINES = 1000;
55
+ var UNTRACKED_N = 20;
56
+ var STASH_N = 5;
57
+ var STALE_BRANCH_N = 20;
58
+ var TODO_STALLED_MIN = 30;
59
+ var TIER_THRESHOLDS = [3, 15, 50, 100];
60
+ var PRICING_TIER_THRESHOLD = 200000;
47
61
  // src/domain/enums.ts
48
62
  var MOODS = ["idle", "busy", "happy", "struggling", "recovery"];
49
63
  var PRESSURE_MOODS = ["compact_hint", "block_limit", "weekly_limit"];
@@ -130,6 +144,9 @@ var REACTION_CATEGORIES = [
130
144
  "db_migrate",
131
145
  "dangerous"
132
146
  ];
147
+ var MIN_SEVERITIES = ["low", "medium", "high", "critical"];
148
+ var CHARACTER_MODES = ["fixed", "random"];
149
+ var BANDINGS = ["solid", "cycle"];
133
150
  var isStack = (s) => STACKS.includes(s);
134
151
  var isEventCategory = (s) => EVENT_CATEGORIES.includes(s);
135
152
  // src/domain/ids.ts
@@ -324,7 +341,6 @@ var pricing_default = [
324
341
 
325
342
  // src/derived/pricing.ts
326
343
  var PER_MILLION = 1e6;
327
- var TIER_THRESHOLD = 200000;
328
344
  var RAW = pricing_default;
329
345
  var TABLE = (() => {
330
346
  const byKey = new Map;
@@ -403,9 +419,9 @@ function resolvePrice(modelId, aliases = NO_ALIASES, atMs) {
403
419
  return rows ? pickRow(rows, atMs) : null;
404
420
  }
405
421
  function tiered(tokens, base, above) {
406
- if (tokens <= TIER_THRESHOLD || above === undefined)
422
+ if (tokens <= PRICING_TIER_THRESHOLD || above === undefined)
407
423
  return tokens * base;
408
- return TIER_THRESHOLD * base + (tokens - TIER_THRESHOLD) * above;
424
+ return PRICING_TIER_THRESHOLD * base + (tokens - PRICING_TIER_THRESHOLD) * above;
409
425
  }
410
426
  function priceMessage(usage, modelId, aliases = NO_ALIASES, atMs) {
411
427
  const row = resolvePrice(modelId, aliases, atMs);
@@ -480,6 +496,7 @@ function joinRecords(attribution, cache, tz) {
480
496
  for (const [session, g] of groupBySession(cache)) {
481
497
  const attr = attribution[session];
482
498
  out.push({
499
+ session,
483
500
  character: attr?.character ?? "unknown",
484
501
  project: attr?.project ?? g.projectRaw,
485
502
  start: g.start,
@@ -489,7 +506,6 @@ function joinRecords(attribution, cache, tz) {
489
506
  }
490
507
  return out;
491
508
  }
492
- var TIER_THRESHOLDS = [3, 15, 50, 100];
493
509
  function tierForCount(n) {
494
510
  const [t1, t2, t3, t4] = TIER_THRESHOLDS;
495
511
  if (n < t1)
@@ -516,7 +532,7 @@ function currentStreak(activeDays, today) {
516
532
  }
517
533
  return streak;
518
534
  }
519
- function deriveFamiliarity(attribution, character, cache, project, clock) {
535
+ function deriveFamiliarity(attribution, character, cache, project, clock, currentSession = "") {
520
536
  const tz = clock.timezone();
521
537
  const records = joinRecords(attribution, cache, tz);
522
538
  const seenProject = records.some((r) => r.project === String(project));
@@ -528,10 +544,12 @@ function deriveFamiliarity(attribution, character, cache, project, clock) {
528
544
  let lastEnd = Number.NEGATIVE_INFINITY;
529
545
  let workingSinceMs = Number.POSITIVE_INFINITY;
530
546
  for (const r of mine) {
531
- if (r.end > lastEnd)
532
- lastEnd = r.end;
533
547
  if (r.start < workingSinceMs)
534
548
  workingSinceMs = r.start;
549
+ if (r.session === currentSession)
550
+ continue;
551
+ if (r.end > lastEnd)
552
+ lastEnd = r.end;
535
553
  }
536
554
  const daysSinceLastSession = Number.isFinite(lastEnd) ? Math.floor((clock.now() - lastEnd) / MS_PER_DAY) : Number.POSITIVE_INFINITY;
537
555
  return {
@@ -757,7 +775,6 @@ function classify(toolName, command, ok) {
757
775
  return category !== undefined ? { category } : null;
758
776
  }
759
777
  // src/derived/signals.ts
760
- var QUOTA_CRITICAL_PCT = 80;
761
778
  function contextBand(pct) {
762
779
  if (pct < 34)
763
780
  return "nominal";
@@ -766,9 +783,9 @@ function contextBand(pct) {
766
783
  return "critical";
767
784
  }
768
785
  function quotaBand(usedPct, resetsAtMs, windowMs, nowMs) {
769
- if (usedPct >= QUOTA_CRITICAL_PCT)
786
+ if (usedPct >= QUOTA_HIGH_PCT)
770
787
  return "critical";
771
- if (resetsAtMs === undefined)
788
+ if (resetsAtMs === undefined || usedPct < QUOTA_PACE_MIN_PCT)
772
789
  return contextBand(usedPct);
773
790
  const usedFraction = usedPct / 100;
774
791
  const elapsedFraction = Math.max((nowMs - (resetsAtMs - windowMs)) / windowMs, 0.01);
@@ -779,6 +796,13 @@ function quotaBand(usedPct, resetsAtMs, windowMs, nowMs) {
779
796
  return "caution";
780
797
  return "critical";
781
798
  }
799
+ function overQuotaPace(usedPct, resetsAtMs, windowMs, nowMs) {
800
+ if (resetsAtMs === undefined)
801
+ return false;
802
+ const usedFraction = usedPct / 100;
803
+ const elapsedFraction = Math.max((nowMs - (resetsAtMs - windowMs)) / windowMs, 0.01);
804
+ return usedFraction / elapsedFraction > 1.5;
805
+ }
782
806
 
783
807
  // src/derived/context.ts
784
808
  function deriveContext(payload, scan) {
@@ -1149,12 +1173,13 @@ function buildWindow(payloadQuota, oauthQuota, windowMs, clock) {
1149
1173
  return;
1150
1174
  }
1151
1175
  const band = quotaBand(usedPct, resetsAtMs, windowMs, clock.now());
1176
+ const overPace = overQuotaPace(usedPct, resetsAtMs, windowMs, clock.now());
1152
1177
  if (resetsAtMs === undefined)
1153
- return { usedPct, band };
1178
+ return { usedPct, band, overPace };
1154
1179
  const left = resetsAtMs - clock.now();
1155
1180
  if (left <= 0)
1156
- return { usedPct, band };
1157
- return { usedPct, band, resetIn: fmtLeft(left) };
1181
+ return { usedPct, band, overPace };
1182
+ return { usedPct, band, overPace, resetIn: fmtLeft(left) };
1158
1183
  }
1159
1184
  function buildPayg(usage) {
1160
1185
  const extra = usage?.extra_usage;
@@ -1252,7 +1277,7 @@ function readJson(path, fallback) {
1252
1277
  }
1253
1278
 
1254
1279
  // src/sources/storage/withLock.ts
1255
- import { closeSync, mkdirSync as mkdirSync2, openSync, statSync, unlinkSync } from "node:fs";
1280
+ import { closeSync, mkdirSync as mkdirSync2, openSync, renameSync as renameSync2, statSync, unlinkSync } from "node:fs";
1256
1281
  import { dirname as dirname2 } from "node:path";
1257
1282
  var STALE_MS = 30000;
1258
1283
  function withLock(lockPath, fn, readOnly) {
@@ -1267,8 +1292,12 @@ function withLock(lockPath, fn, readOnly) {
1267
1292
  } catch {
1268
1293
  try {
1269
1294
  if (Date.now() - statSync(lockPath).mtimeMs > STALE_MS) {
1270
- unlinkSync(lockPath);
1295
+ const aside = `${lockPath}.stale`;
1296
+ renameSync2(lockPath, aside);
1271
1297
  fd = openSync(lockPath, "wx");
1298
+ try {
1299
+ unlinkSync(aside);
1300
+ } catch {}
1272
1301
  }
1273
1302
  } catch {}
1274
1303
  }
@@ -1355,14 +1384,27 @@ function upsertAttribution(root, sessionId, rec) {
1355
1384
  const path = storePath(root);
1356
1385
  withLock(`${path}.lock`, () => {
1357
1386
  const store = coerceStore(readJson(path, undefined));
1387
+ const existing = store[sessionId];
1388
+ if (existing !== undefined && existing.project === rec.project && existing.character === rec.character) {
1389
+ return;
1390
+ }
1358
1391
  store[sessionId] = rec.updatedMs !== undefined ? { project: rec.project, character: rec.character, updatedMs: rec.updatedMs } : { project: rec.project, character: rec.character };
1359
1392
  atomicWrite(path, JSON.stringify(store));
1360
1393
  }, () => {});
1361
1394
  }
1362
1395
  // src/sources/balance.ts
1396
+ import { statSync as statSync3 } from "node:fs";
1397
+ var BALANCE_MAX_BYTES = 64 * 1024;
1363
1398
  function readBalance(path, clock) {
1364
1399
  if (path === "")
1365
1400
  return null;
1401
+ try {
1402
+ const st = statSync3(path);
1403
+ if (!st.isFile() || st.size > BALANCE_MAX_BYTES)
1404
+ return null;
1405
+ } catch {
1406
+ return null;
1407
+ }
1366
1408
  const raw = readJson(path, undefined);
1367
1409
  if (typeof raw !== "object" || raw === null)
1368
1410
  return null;
@@ -2493,10 +2535,10 @@ function loadConfig(globalToml = "", projectToml = "") {
2493
2535
  const statusline = section(g, p, "statusline");
2494
2536
  const d = DEFAULT_CONFIG;
2495
2537
  return {
2496
- schema_version: numv(p["schema_version"] ?? g["schema_version"], d.schema_version),
2538
+ schema_version: numv(p["schema_version"], numv(g["schema_version"], d.schema_version)),
2497
2539
  character: {
2498
2540
  enabled: bool(character["enabled"], d.character.enabled),
2499
- mode: oneOf(character["mode"], ["fixed", "random"], d.character.mode),
2541
+ mode: oneOf(character["mode"], CHARACTER_MODES, d.character.mode),
2500
2542
  name: str(character["name"], d.character.name),
2501
2543
  roster: stringArray(character["roster"], d.character.roster)
2502
2544
  },
@@ -2505,7 +2547,7 @@ function loadConfig(globalToml = "", projectToml = "") {
2505
2547
  ...typeof theme["statusline"] === "string" ? { statusline: theme["statusline"] } : {},
2506
2548
  ...typeof theme["logo"] === "string" ? { logo: theme["logo"] } : {},
2507
2549
  ...typeof theme["comment"] === "string" ? { comment: theme["comment"] } : {},
2508
- banding: oneOf(theme["banding"], ["solid", "cycle"], d.theme.banding),
2550
+ banding: oneOf(theme["banding"], BANDINGS, d.theme.banding),
2509
2551
  mood_shift: bool(theme["mood_shift"], d.theme.mood_shift),
2510
2552
  icons: mergeIcons({
2511
2553
  ...obj(obj(g["theme"])["icons"]),
@@ -2515,7 +2557,7 @@ function loadConfig(globalToml = "", projectToml = "") {
2515
2557
  comments: {
2516
2558
  character: bool(comments["character"], d.comments.character),
2517
2559
  helpful: bool(comments["helpful"], d.comments.helpful),
2518
- min_severity: oneOf(comments["min_severity"], ["low", "medium", "high", "critical"], d.comments.min_severity)
2560
+ min_severity: oneOf(comments["min_severity"], MIN_SEVERITIES, d.comments.min_severity)
2519
2561
  },
2520
2562
  network: {
2521
2563
  fx_refresh: bool(network["fx_refresh"], d.network.fx_refresh),
@@ -2645,7 +2687,19 @@ function coerceEntry(v) {
2645
2687
  }
2646
2688
  const projectPath = typeof v["projectPath"] === "string" ? v["projectPath"] : String(record.project);
2647
2689
  const models = Array.isArray(v["models"]) ? v["models"].filter((x) => typeof x === "string") : [];
2648
- return { mtime, size, total, lines, models, projectPath, record };
2690
+ const byteOffset = asNumber(v["byteOffset"]);
2691
+ const headHash = typeof v["headHash"] === "string" ? v["headHash"] : undefined;
2692
+ return {
2693
+ mtime,
2694
+ size,
2695
+ total,
2696
+ lines,
2697
+ models,
2698
+ projectPath,
2699
+ record,
2700
+ ...byteOffset !== undefined ? { byteOffset } : {},
2701
+ ...headHash !== undefined ? { headHash } : {}
2702
+ };
2649
2703
  }
2650
2704
  function coerceByModel(v) {
2651
2705
  if (!isObject(v))
@@ -2702,7 +2756,6 @@ function writeCostCache(root, c) {
2702
2756
  // src/sources/creds.ts
2703
2757
  import { spawnSync as spawnSync2 } from "node:child_process";
2704
2758
  import { readFileSync as readFileSync4 } from "node:fs";
2705
- import { homedir as homedir3 } from "node:os";
2706
2759
  import { join as join5 } from "node:path";
2707
2760
 
2708
2761
  // src/sources/oauthUsage.ts
@@ -2899,7 +2952,6 @@ function readUsage(root, clock, opts) {
2899
2952
  }
2900
2953
 
2901
2954
  // src/sources/creds.ts
2902
- var KEYCHAIN_SERVICE2 = "Claude Code-credentials";
2903
2955
  var SUBSCRIPTIONS = new Set(["pro", "max", "team", "enterprise"]);
2904
2956
  var isObject3 = (v) => typeof v === "object" && v !== null;
2905
2957
  var defaultRunner = (cmd, args) => {
@@ -2916,11 +2968,8 @@ var defaultRunner = (cmd, args) => {
2916
2968
  return "";
2917
2969
  }
2918
2970
  };
2919
- function configBase2() {
2920
- return process.env["CLAUDE_CONFIG_DIR"] ?? join5(homedir3(), ".claude");
2921
- }
2922
2971
  function readBlob(run, base) {
2923
- for (const service of [keychainService(base), KEYCHAIN_SERVICE2]) {
2972
+ for (const service of [keychainService(base), KEYCHAIN_SERVICE]) {
2924
2973
  const keychain = run("security", ["find-generic-password", "-s", service, "-w"]);
2925
2974
  if (keychain.trim() !== "")
2926
2975
  return keychain;
@@ -2930,7 +2979,7 @@ function readBlob(run, base) {
2930
2979
  if (file.trim() !== "")
2931
2980
  return file;
2932
2981
  } catch {}
2933
- const secretTool = run("secret-tool", ["lookup", "service", KEYCHAIN_SERVICE2]);
2982
+ const secretTool = run("secret-tool", ["lookup", "service", KEYCHAIN_SERVICE]);
2934
2983
  if (secretTool.trim() !== "")
2935
2984
  return secretTool;
2936
2985
  return "";
@@ -2959,7 +3008,7 @@ function readOrgTier(base) {
2959
3008
  }
2960
3009
  function readCreds(run = defaultRunner) {
2961
3010
  try {
2962
- const base = configBase2();
3011
+ const base = configBase();
2963
3012
  const blob = readBlob(run, base);
2964
3013
  if (blob.trim() !== "") {
2965
3014
  const tier = parseSubscription(blob);
@@ -2973,6 +3022,39 @@ function readCreds(run = defaultRunner) {
2973
3022
  return null;
2974
3023
  }
2975
3024
  }
3025
+ var CREDS_CACHE_FILE = "creds.json";
3026
+ function credsCachePath(root) {
3027
+ return join5(cacheDir(root), CREDS_CACHE_FILE);
3028
+ }
3029
+ function coerceCachedCreds(raw) {
3030
+ if (!isObject3(raw) || typeof raw["at"] !== "number")
3031
+ return null;
3032
+ const at = raw["at"];
3033
+ const info = raw["info"];
3034
+ if (info === null)
3035
+ return { info: null, at };
3036
+ if (isObject3(info) && typeof info["present"] === "boolean") {
3037
+ const sub = info["subscriptionType"];
3038
+ return {
3039
+ at,
3040
+ info: typeof sub === "string" && SUBSCRIPTIONS.has(sub) ? { present: info["present"], subscriptionType: sub } : { present: info["present"] }
3041
+ };
3042
+ }
3043
+ return null;
3044
+ }
3045
+ function readCredsCached(root) {
3046
+ return coerceCachedCreds(readJson(credsCachePath(root), undefined))?.info ?? null;
3047
+ }
3048
+ function refreshCreds(root, clock, run = defaultRunner) {
3049
+ const cached = coerceCachedCreds(readJson(credsCachePath(root), undefined));
3050
+ const now = clock.now();
3051
+ if (cached !== null && now - cached.at <= CREDS_TTL_MS)
3052
+ return;
3053
+ const info = readCreds(run);
3054
+ try {
3055
+ atomicWrite(credsCachePath(root), JSON.stringify({ info, at: now }));
3056
+ } catch {}
3057
+ }
2976
3058
  // src/sources/env.ts
2977
3059
  var on = (v) => v === "1" || v === "true";
2978
3060
  function isCustomHost(base) {
@@ -3455,12 +3537,14 @@ function readSubmoduleRollup(run, workTreeRoot) {
3455
3537
  return rollup;
3456
3538
  }
3457
3539
  function readGit(cwd, run = defaultRunner2(cwd)) {
3458
- if (run(["rev-parse", "--is-inside-work-tree"]).trim() !== "true")
3540
+ const probe = run(["rev-parse", "--is-inside-work-tree", "--git-dir", "--show-toplevel"]).split(`
3541
+ `);
3542
+ if ((probe[0] ?? "").trim() !== "true")
3459
3543
  return null;
3460
3544
  const status = parseStatus(run(["status", "--porcelain=v2", "--branch", "--show-stash"]));
3461
- const gitDirRaw = run(["rev-parse", "--git-dir"]).trim();
3545
+ const gitDirRaw = (probe[1] ?? "").trim();
3462
3546
  const gitDir = gitDirRaw === "" ? "" : resolve(cwd, gitDirRaw);
3463
- const workTreeRoot = run(["rev-parse", "--show-toplevel"]).trim();
3547
+ const workTreeRoot = (probe[2] ?? "").trim();
3464
3548
  const tag = run(["describe", "--tags", "--exact-match"]).trim() || undefined;
3465
3549
  const originRepo = normalizeOriginRepo(run(["remote", "get-url", "origin"]));
3466
3550
  const defaultBranch = resolveDefaultBranch(run);
@@ -3507,7 +3591,7 @@ function readGit(cwd, run = defaultRunner2(cwd)) {
3507
3591
  }
3508
3592
  // src/sources/helpfulEnv.ts
3509
3593
  import { readFileSync as readFileSync7 } from "node:fs";
3510
- import { homedir as homedir4 } from "node:os";
3594
+ import { homedir as homedir3 } from "node:os";
3511
3595
  import { delimiter, join as join9 } from "node:path";
3512
3596
  var opt3 = (key, value) => value !== undefined ? { [key]: value } : {};
3513
3597
  function kubeconfigPath() {
@@ -3517,7 +3601,7 @@ function kubeconfigPath() {
3517
3601
  if (first !== undefined)
3518
3602
  return first;
3519
3603
  }
3520
- return join9(homedir4(), ".kube", "config");
3604
+ return join9(homedir3(), ".kube", "config");
3521
3605
  }
3522
3606
  function readKubeContext() {
3523
3607
  try {
@@ -3821,7 +3905,7 @@ function writeState(sessionDir2, s) {
3821
3905
  }, () => {});
3822
3906
  }
3823
3907
  // src/sources/transcript.ts
3824
- import { readFileSync as readFileSync8, readdirSync as readdirSync2, statSync as statSync3 } from "node:fs";
3908
+ import { closeSync as closeSync2, openSync as openSync2, readFileSync as readFileSync8, readSync, readdirSync as readdirSync2, statSync as statSync4 } from "node:fs";
3825
3909
  import { basename, dirname as dirname6, join as join12 } from "node:path";
3826
3910
  var asObj = (v) => v !== null && typeof v === "object" ? v : undefined;
3827
3911
  var asStr = (v) => typeof v === "string" ? v : undefined;
@@ -4047,7 +4131,7 @@ function reconstructTodos(lines) {
4047
4131
  }
4048
4132
  function statSafe(p) {
4049
4133
  try {
4050
- const s = statSync3(p);
4134
+ const s = statSync4(p);
4051
4135
  return { mtimeMs: s.mtimeMs, size: s.size };
4052
4136
  } catch {
4053
4137
  return null;
@@ -4060,6 +4144,63 @@ function readSafe(p) {
4060
4144
  return "";
4061
4145
  }
4062
4146
  }
4147
+ function readBufSafe(p) {
4148
+ try {
4149
+ return readFileSync8(p);
4150
+ } catch {
4151
+ return null;
4152
+ }
4153
+ }
4154
+ function readRange(p, start, end) {
4155
+ if (end <= start)
4156
+ return Buffer.alloc(0);
4157
+ let fd;
4158
+ try {
4159
+ fd = openSync2(p, "r");
4160
+ const len = end - start;
4161
+ const buf = Buffer.allocUnsafe(len);
4162
+ let off = 0;
4163
+ while (off < len) {
4164
+ const n = readSync(fd, buf, off, len - off, start + off);
4165
+ if (n === 0)
4166
+ break;
4167
+ off += n;
4168
+ }
4169
+ return off === len ? buf : null;
4170
+ } catch {
4171
+ return null;
4172
+ } finally {
4173
+ if (fd !== undefined)
4174
+ closeSync2(fd);
4175
+ }
4176
+ }
4177
+ var HEAD_HASH_BYTES = 4096;
4178
+ function headHashOf(head) {
4179
+ let h = 2166136261;
4180
+ for (const b of head) {
4181
+ h ^= b;
4182
+ h = Math.imul(h, 16777619);
4183
+ }
4184
+ return (h >>> 0).toString(16);
4185
+ }
4186
+ function makeInternModel(models) {
4187
+ const index = new Map;
4188
+ models.forEach((m, i) => index.set(m, i));
4189
+ return (model) => {
4190
+ let i = index.get(model);
4191
+ if (i === undefined) {
4192
+ i = models.length;
4193
+ models.push(model);
4194
+ index.set(model, i);
4195
+ }
4196
+ return i;
4197
+ };
4198
+ }
4199
+ function lastLineEnd(buf) {
4200
+ const i = buf.lastIndexOf(10);
4201
+ return i < 0 ? 0 : i + 1;
4202
+ }
4203
+ var headLen = (byteOffset) => Math.min(HEAD_HASH_BYTES, byteOffset);
4063
4204
  var EMPTY_TOKENS = {
4064
4205
  input: 0,
4065
4206
  output: 0,
@@ -4150,7 +4291,7 @@ function* walkTree(dir, encodedDir) {
4150
4291
  const full = join12(dir, name);
4151
4292
  let st;
4152
4293
  try {
4153
- st = statSync3(full);
4294
+ st = statSync4(full);
4154
4295
  } catch {
4155
4296
  continue;
4156
4297
  }
@@ -4189,22 +4330,16 @@ function scanBounds(lines) {
4189
4330
  return { sessionId, start, end };
4190
4331
  }
4191
4332
  function priceFile(path, encodedDir, st, price, resolveProject) {
4192
- const lines = parseAll(readSafe(path));
4333
+ const buf = readBufSafe(path) ?? Buffer.alloc(0);
4334
+ const byteOffset = lastLineEnd(buf);
4335
+ const headHash = headHashOf(buf.subarray(0, headLen(byteOffset)));
4336
+ const lines = parseAll(buf.subarray(0, byteOffset).toString("utf8"));
4193
4337
  const { sessionId, start, end } = scanBounds(lines);
4194
4338
  const priced = collapseStreaming(lines);
4195
4339
  const gate = makeDedupGate();
4196
4340
  const costLines = [];
4197
4341
  const models = [];
4198
- const modelIndex = new Map;
4199
- const internModel = (model) => {
4200
- let i = modelIndex.get(model);
4201
- if (i === undefined) {
4202
- i = models.length;
4203
- models.push(model);
4204
- modelIndex.set(model, i);
4205
- }
4206
- return i;
4207
- };
4342
+ const internModel = makeInternModel(models);
4208
4343
  let total = 0;
4209
4344
  let input = 0;
4210
4345
  let output = 0;
@@ -4249,6 +4384,8 @@ function priceFile(path, encodedDir, st, price, resolveProject) {
4249
4384
  return {
4250
4385
  mtime: st.mtimeMs,
4251
4386
  size: st.size,
4387
+ byteOffset,
4388
+ headHash,
4252
4389
  total,
4253
4390
  lines: costLines,
4254
4391
  models,
@@ -4256,6 +4393,138 @@ function priceFile(path, encodedDir, st, price, resolveProject) {
4256
4393
  record
4257
4394
  };
4258
4395
  }
4396
+ function resumeCostFile(cached, path, st, price) {
4397
+ const tail = readAppendedTail(cached, path, st);
4398
+ if (tail === null)
4399
+ return null;
4400
+ if (tail === "partial")
4401
+ return { ...cached, mtime: st.mtimeMs, size: st.size };
4402
+ const models = [...cached.models];
4403
+ const keyIndex = new Map;
4404
+ cached.lines.forEach((l, i) => {
4405
+ if (l.id !== undefined && l.reqId !== undefined)
4406
+ keyIndex.set(`${l.id}|${l.reqId}`, i);
4407
+ });
4408
+ const gate = makeDedupGate();
4409
+ const acc = {
4410
+ lines: [...cached.lines],
4411
+ models,
4412
+ counted: cached.lines.map((l) => gate(l.id, l.reqId, l.sidechain)),
4413
+ keyIndex,
4414
+ gate,
4415
+ internModel: makeInternModel(models),
4416
+ input: cached.record.tokens.input,
4417
+ output: cached.record.tokens.output,
4418
+ cacheRead: cached.record.tokens.cache_read,
4419
+ cacheCreation: cached.record.tokens.cache_creation,
4420
+ total: cached.total,
4421
+ messages: cached.record.messages,
4422
+ start: cached.record.start,
4423
+ end: cached.record.end
4424
+ };
4425
+ for (const l of collapseStreaming(tail.appended))
4426
+ foldAppendedLine(acc, l, price);
4427
+ return {
4428
+ mtime: st.mtimeMs,
4429
+ size: st.size,
4430
+ byteOffset: tail.newByteOffset,
4431
+ headHash: tail.newHeadHash,
4432
+ total: acc.total,
4433
+ lines: acc.lines,
4434
+ models: acc.models,
4435
+ projectPath: cached.projectPath,
4436
+ record: {
4437
+ ...cached.record,
4438
+ start: acc.start,
4439
+ end: acc.end,
4440
+ tokens: {
4441
+ input: acc.input,
4442
+ output: acc.output,
4443
+ cache_read: acc.cacheRead,
4444
+ cache_creation: acc.cacheCreation
4445
+ },
4446
+ messages: acc.messages
4447
+ }
4448
+ };
4449
+ }
4450
+ function costLineOf(internModel, l, cost, totalTok) {
4451
+ return {
4452
+ ...l.id !== undefined ? { id: l.id } : {},
4453
+ ...l.requestId !== undefined ? { reqId: l.requestId } : {},
4454
+ sidechain: l.isSidechain,
4455
+ ts: l.tsMs ?? 0,
4456
+ cost,
4457
+ ...l.model !== undefined ? { m: internModel(l.model) } : {},
4458
+ tok: totalTok
4459
+ };
4460
+ }
4461
+ function readAppendedTail(cached, path, st) {
4462
+ const offset = cached.byteOffset;
4463
+ if (offset === undefined || offset === 0 || st.size < offset)
4464
+ return null;
4465
+ const head = readRange(path, 0, headLen(offset));
4466
+ if (head === null || headHashOf(head) !== cached.headHash)
4467
+ return null;
4468
+ const tailBuf = readRange(path, offset, st.size);
4469
+ if (tailBuf === null)
4470
+ return null;
4471
+ const added = lastLineEnd(tailBuf);
4472
+ if (added === 0)
4473
+ return "partial";
4474
+ const newByteOffset = offset + added;
4475
+ const newHead = headLen(newByteOffset) === head.length ? head : readRange(path, 0, headLen(newByteOffset));
4476
+ if (newHead === null)
4477
+ return null;
4478
+ return {
4479
+ appended: parseAll(tailBuf.subarray(0, added).toString("utf8")),
4480
+ newByteOffset,
4481
+ newHeadHash: headHashOf(newHead)
4482
+ };
4483
+ }
4484
+ function widenBounds(acc, tsMs) {
4485
+ if (tsMs === undefined)
4486
+ return;
4487
+ if (tsMs > acc.end)
4488
+ acc.end = tsMs;
4489
+ if (acc.start === 0 || tsMs < acc.start)
4490
+ acc.start = tsMs;
4491
+ }
4492
+ function foldAppendedLine(acc, l, price) {
4493
+ widenBounds(acc, l.tsMs);
4494
+ const usage = l.usage;
4495
+ if (usage === undefined)
4496
+ return;
4497
+ const t = tokenize(usage);
4498
+ const totalTok = t.input + t.output + t.cache_read + t.c5m + t.c1h;
4499
+ const key = l.id !== undefined && l.requestId !== undefined ? `${l.id}|${l.requestId}` : null;
4500
+ const existing = key !== null ? acc.keyIndex.get(key) : undefined;
4501
+ const old = existing !== undefined ? acc.lines[existing] : undefined;
4502
+ if (existing !== undefined && old !== undefined) {
4503
+ if (totalTok <= (old.tok ?? 0))
4504
+ return;
4505
+ const cost2 = price(usage, l.model ?? "", l.tsMs);
4506
+ if (acc.counted[existing] === true) {
4507
+ acc.output += totalTok - (old.tok ?? 0);
4508
+ acc.total += cost2 - old.cost;
4509
+ }
4510
+ acc.lines[existing] = costLineOf(acc.internModel, l, cost2, totalTok);
4511
+ return;
4512
+ }
4513
+ const cost = price(usage, l.model ?? "", l.tsMs);
4514
+ const isCounted = acc.gate(l.id, l.requestId, l.isSidechain);
4515
+ if (key !== null)
4516
+ acc.keyIndex.set(key, acc.lines.length);
4517
+ acc.lines.push(costLineOf(acc.internModel, l, cost, totalTok));
4518
+ acc.counted.push(isCounted);
4519
+ if (isCounted) {
4520
+ acc.input += t.input;
4521
+ acc.output += t.output;
4522
+ acc.cacheRead += t.cache_read;
4523
+ acc.cacheCreation += t.c5m + t.c1h;
4524
+ acc.total += cost;
4525
+ acc.messages += 1;
4526
+ }
4527
+ }
4259
4528
  function buildAggregate(files, chat) {
4260
4529
  const sessionProject = {};
4261
4530
  const all = [];
@@ -4290,17 +4559,27 @@ function scanCostTree(root, cache, clock, price, resolveProject) {
4290
4559
  return cache;
4291
4560
  const chat = cache.aggregate.chat;
4292
4561
  const files = {};
4562
+ let changed = false;
4293
4563
  for (const { path, encodedDir } of walkJsonl(root)) {
4294
4564
  const st = statSafe(path);
4295
4565
  if (!st)
4296
4566
  continue;
4297
4567
  const cached = cache.files[path];
4298
- files[path] = cached && cached.mtime === st.mtimeMs && cached.size === st.size ? cached : priceFile(path, encodedDir, st, price, resolveProject);
4568
+ if (cached && cached.mtime === st.mtimeMs && cached.size === st.size) {
4569
+ files[path] = cached;
4570
+ } else {
4571
+ const resumed = cached ? resumeCostFile(cached, path, st, price) : null;
4572
+ files[path] = resumed ?? priceFile(path, encodedDir, st, price, resolveProject);
4573
+ changed = true;
4574
+ }
4299
4575
  }
4576
+ const sameFileSet = Object.keys(files).length === Object.keys(cache.files).length;
4577
+ if (!changed && sameFileSet)
4578
+ return { files, aggregate: cache.aggregate, lastScanTs: now };
4300
4579
  return { files, aggregate: buildAggregate(files, chat), lastScanTs: now };
4301
4580
  }
4302
4581
  // src/cli/classify.ts
4303
- var FAIL_RE = /FAILED|Error|✗|not ok|FAIL/;
4582
+ var FAIL_RE = /\bFAILED\b|\bFAIL\b|✗|✕|\bnot ok\b|error:|error\[|error TS\d|error CS\d|\bpanic:|Traceback \(most recent call last\)|[1-9]\d* (?:failed|failing|errors?)\b/;
4304
4583
  function asObject(v) {
4305
4584
  return typeof v === "object" && v !== null && !Array.isArray(v) ? v : undefined;
4306
4585
  }
@@ -4316,10 +4595,8 @@ function softFail(toolResponse) {
4316
4595
  return false;
4317
4596
  if (r["isError"] === true || r["interrupted"] === true)
4318
4597
  return true;
4319
- const stderr = asString(r["stderr"]);
4320
- if (stderr !== undefined && stderr !== "")
4321
- return true;
4322
- const out = `${asString(r["stdout"]) ?? ""}${stderr ?? ""}`;
4598
+ const out = `${asString(r["stdout"]) ?? ""}
4599
+ ${asString(r["stderr"]) ?? ""}`;
4323
4600
  return FAIL_RE.test(out);
4324
4601
  }
4325
4602
  function commandOf(toolInput) {
@@ -4340,16 +4617,6 @@ function appendClassified(dir, toolName, command, ok, ts) {
4340
4617
  if (c !== null)
4341
4618
  appendEvent(dir, { ts, ...c });
4342
4619
  }
4343
- function appendBatch(dir, calls, ts) {
4344
- for (const call of calls) {
4345
- const c = asObject(call);
4346
- const toolName = c !== undefined ? asString(c["tool_name"]) : undefined;
4347
- if (c === undefined || toolName === undefined)
4348
- continue;
4349
- const ok = !softFail(c["tool_response"]);
4350
- appendClassified(dir, toolName, commandOf(c["tool_input"]), ok, ts);
4351
- }
4352
- }
4353
4620
  function runClassify(stdin, env, clock) {
4354
4621
  try {
4355
4622
  const r = asObject(JSON.parse(stdin));
@@ -4361,12 +4628,6 @@ function runClassify(stdin, env, clock) {
4361
4628
  const dir = sessionDir(ccsidekickRoot(env), session);
4362
4629
  const ts = clock.now();
4363
4630
  const hook = asString(r["hook_event_name"]);
4364
- if (hook === "PostToolBatch") {
4365
- const calls = r["tool_calls"];
4366
- if (Array.isArray(calls))
4367
- appendBatch(dir, calls, ts);
4368
- return;
4369
- }
4370
4631
  const toolName = asString(r["tool_name"]);
4371
4632
  if (toolName === undefined)
4372
4633
  return;
@@ -4379,7 +4640,7 @@ function runClassify(stdin, env, clock) {
4379
4640
  } catch {}
4380
4641
  }
4381
4642
  // src/cli/render.ts
4382
- import { readFileSync as readFileSync10 } from "node:fs";
4643
+ import { readFileSync as readFileSync11 } from "node:fs";
4383
4644
  import { dirname as dirname7, join as join14 } from "node:path";
4384
4645
 
4385
4646
  // src/compose/character.ts
@@ -6096,16 +6357,6 @@ function composeStatusline(ctx) {
6096
6357
  return { fields, providerBadge };
6097
6358
  }
6098
6359
  // src/compose/helpful/catalog.ts
6099
- var COMPACT_SOON_PCT = 60;
6100
- var QUOTA_PROJECT_MIN_PCT = 50;
6101
- var COMPACTION_THRASH_N = 3;
6102
- var CACHE_RATIO_FLOOR = 0.5;
6103
- var CACHE_WARMUP_TURNS = 20;
6104
- var BIG_DIFF_LINES = 1000;
6105
- var UNTRACKED_N = 20;
6106
- var STASH_N = 5;
6107
- var STALE_BRANCH_N = 20;
6108
- var TODO_STALLED_MIN = 30;
6109
6360
  var EFFORT_LOW_LEVEL = "low";
6110
6361
  var PROD_CONTEXT_PATTERN = /prod/i;
6111
6362
  var SECRET_PATTERNS = [
@@ -6280,7 +6531,7 @@ var HELPFUL_CATALOG = [
6280
6531
  category: "quota",
6281
6532
  momentary: false,
6282
6533
  template: "5h limit at {pct}%, {reset} to reset. Save heavy asks for after.",
6283
- test: (i) => i.quota.block !== undefined && i.quota.block.usedPct > QUOTA_HIGH_PCT,
6534
+ test: (i) => i.quota.block !== undefined && i.quota.block.usedPct > QUOTA_HIGH_PCT && i.quota.block.overPace === true,
6284
6535
  render: (i) => `5h limit at ${pct(i.quota.block?.usedPct ?? 0)}, ${bare(i.quota.block?.resetIn)} to reset. Save heavy asks for after.`
6285
6536
  },
6286
6537
  {
@@ -6289,7 +6540,7 @@ var HELPFUL_CATALOG = [
6289
6540
  category: "quota",
6290
6541
  momentary: false,
6291
6542
  template: "Weekly quota {pct}% spent, {reset} to go. Ration the big requests.",
6292
- test: (i) => i.quota.weekly !== undefined && i.quota.weekly.usedPct > QUOTA_HIGH_PCT,
6543
+ test: (i) => i.quota.weekly !== undefined && i.quota.weekly.usedPct > QUOTA_HIGH_PCT && i.quota.weekly.overPace === true,
6293
6544
  render: (i) => `Weekly quota ${pct(i.quota.weekly?.usedPct ?? 0)} spent, ${bare(i.quota.weekly?.resetIn)} to go. Ration the big requests.`
6294
6545
  },
6295
6546
  {
@@ -6298,7 +6549,7 @@ var HELPFUL_CATALOG = [
6298
6549
  category: "quota",
6299
6550
  momentary: false,
6300
6551
  template: "This burn rate empties the 5h block before reset, cutting you off mid-task.",
6301
- test: (i) => i.quota.block !== undefined && i.quota.block.band !== "nominal" && i.quota.block.usedPct > QUOTA_PROJECT_MIN_PCT,
6552
+ test: (i) => i.quota.block !== undefined && i.quota.block.usedPct > QUOTA_PROJECT_MIN_PCT && i.quota.block.overPace === true,
6302
6553
  render: () => "This burn rate empties the 5h block before reset, cutting you off mid-task."
6303
6554
  },
6304
6555
  {
@@ -6307,7 +6558,7 @@ var HELPFUL_CATALOG = [
6307
6558
  category: "quota",
6308
6559
  momentary: false,
6309
6560
  template: "At this pace the weekly quota runs dry before reset, locking you out for days.",
6310
- test: (i) => i.quota.weekly !== undefined && i.quota.weekly.band !== "nominal" && i.quota.weekly.usedPct > QUOTA_PROJECT_MIN_PCT,
6561
+ test: (i) => i.quota.weekly !== undefined && i.quota.weekly.usedPct > QUOTA_PROJECT_MIN_PCT && i.quota.weekly.overPace === true,
6311
6562
  render: () => "At this pace the weekly quota runs dry before reset, locking you out for days."
6312
6563
  },
6313
6564
  {
@@ -6928,16 +7179,33 @@ var PACKS = [
6928
7179
  "yoda"
6929
7180
  ];
6930
7181
  // src/cli/gc.ts
6931
- import { existsSync as existsSync2, readdirSync as readdirSync3, rmSync, statSync as statSync4 } from "node:fs";
7182
+ import {
7183
+ existsSync as existsSync2,
7184
+ mkdirSync as mkdirSync5,
7185
+ readFileSync as readFileSync10,
7186
+ readdirSync as readdirSync3,
7187
+ rmSync,
7188
+ statSync as statSync5,
7189
+ writeFileSync as writeFileSync3
7190
+ } from "node:fs";
6932
7191
  import { join as join13 } from "node:path";
6933
7192
  var DAY_MS = 86400000;
7193
+ var GC_MIN_INTERVAL_MS = 6 * 60 * 60 * 1000;
7194
+ function readGcStamp(path) {
7195
+ try {
7196
+ const n = Number(readFileSync10(path, "utf8"));
7197
+ return Number.isFinite(n) ? n : undefined;
7198
+ } catch {
7199
+ return;
7200
+ }
7201
+ }
6934
7202
  function newestMtime(dir) {
6935
7203
  let newest = 0;
6936
7204
  try {
6937
- newest = statSync4(dir).mtimeMs;
7205
+ newest = statSync5(dir).mtimeMs;
6938
7206
  for (const name of readdirSync3(dir)) {
6939
7207
  try {
6940
- const m = statSync4(join13(dir, name)).mtimeMs;
7208
+ const m = statSync5(join13(dir, name)).mtimeMs;
6941
7209
  if (m > newest)
6942
7210
  newest = m;
6943
7211
  } catch {}
@@ -6957,7 +7225,7 @@ function pruneSessions(root, now) {
6957
7225
  for (const name of names) {
6958
7226
  const dir = join13(sessions, name);
6959
7227
  try {
6960
- if (!statSync4(dir).isDirectory())
7228
+ if (!statSync5(dir).isDirectory())
6961
7229
  continue;
6962
7230
  } catch {
6963
7231
  continue;
@@ -6970,23 +7238,48 @@ function pruneSessions(root, now) {
6970
7238
  function pruneCostCache(root) {
6971
7239
  const cache = readCostCache(root);
6972
7240
  const kept = {};
7241
+ const liveSessions = new Set;
6973
7242
  let dropped = false;
6974
7243
  for (const [path, entry] of Object.entries(cache.files)) {
6975
- if (existsSync2(path))
7244
+ if (existsSync2(path)) {
6976
7245
  kept[path] = entry;
6977
- else
7246
+ liveSessions.add(String(entry.record.session));
7247
+ } else {
6978
7248
  dropped = true;
7249
+ }
7250
+ }
7251
+ const chatKept = {};
7252
+ let chatDropped = false;
7253
+ for (const [session, cost] of Object.entries(cache.aggregate.chat)) {
7254
+ if (liveSessions.has(session))
7255
+ chatKept[session] = cost;
7256
+ else
7257
+ chatDropped = true;
7258
+ }
7259
+ if (dropped || chatDropped) {
7260
+ writeCostCache(root, {
7261
+ ...cache,
7262
+ files: kept,
7263
+ aggregate: { ...cache.aggregate, chat: chatKept }
7264
+ });
6979
7265
  }
6980
- if (dropped)
6981
- writeCostCache(root, { ...cache, files: kept });
6982
7266
  }
6983
7267
  function runGc(root, clock) {
7268
+ const now = clock.now();
7269
+ const stampPath = join13(root, "cache", ".gc-stamp");
7270
+ const last = readGcStamp(stampPath);
7271
+ if (last !== undefined && now - last < GC_MIN_INTERVAL_MS)
7272
+ return;
6984
7273
  try {
6985
- pruneSessions(root, clock.now());
7274
+ pruneSessions(root, now);
6986
7275
  } catch {}
6987
7276
  try {
6988
7277
  pruneCostCache(root);
6989
7278
  } catch {}
7279
+ try {
7280
+ mkdirSync5(join13(root, "cache"), { recursive: true });
7281
+ writeFileSync3(stampPath, String(now));
7282
+ } catch {}
6990
7283
  }
6991
7284
 
6992
7285
  // src/cli/render.ts
@@ -6995,7 +7288,7 @@ var TIER_MILESTONES = new Set(TIER_THRESHOLDS);
6995
7288
  var YEAR_MS = 365 * 86400000;
6996
7289
  function readTextSafe(path) {
6997
7290
  try {
6998
- return readFileSync10(path, "utf8");
7291
+ return readFileSync11(path, "utf8");
6999
7292
  } catch {
7000
7293
  return "";
7001
7294
  }
@@ -7030,19 +7323,29 @@ function applyThemeIcons(fields, theme, config) {
7030
7323
  });
7031
7324
  }
7032
7325
  function readGated(overrides, envInputs, config, root, dir, markers, clock) {
7033
- const creds = overrides?.creds !== undefined ? overrides.creds : envInputs.hasApiKey || config.network.usage_fetch ? readCreds() : null;
7326
+ const creds = overrides?.creds !== undefined ? overrides.creds : envInputs.hasApiKey || config.network.usage_fetch ? readCredsCached(root) : null;
7034
7327
  const usage = overrides?.usage !== undefined ? overrides.usage : config.network.usage_fetch ? readUsageCached(root) : null;
7035
7328
  const balance = overrides?.balance !== undefined ? overrides.balance : config.network.balance_path !== "" ? readBalance(config.network.balance_path, clock) : null;
7036
7329
  const helpfulEnv = needsHelpfulEnv(markers) ? readHelpfulEnv(dir) : EMPTY_HELPFUL_ENV;
7037
7330
  return { creds, usage, balance, helpfulEnv };
7038
7331
  }
7039
7332
  var DEFAULT_EMBLEM = "❝";
7040
- function lookupPackTheme(name) {
7041
- const loaded = loadPack(name);
7333
+ function packThemeOf(loaded) {
7042
7334
  if (!loaded.ok || loaded.pack.theme === undefined)
7043
7335
  return null;
7044
7336
  return { displayName: loaded.pack.displayName, ...loaded.pack.theme };
7045
7337
  }
7338
+ function makePackThemeLookup(persona, loaded, load = loadPack) {
7339
+ const memo = new Map;
7340
+ return (name) => {
7341
+ const cached = memo.get(name);
7342
+ if (cached !== undefined || memo.has(name))
7343
+ return cached ?? null;
7344
+ const theme = packThemeOf(name === persona ? loaded : load(name));
7345
+ memo.set(name, theme);
7346
+ return theme;
7347
+ };
7348
+ }
7046
7349
  var NOOP = () => {};
7047
7350
  function runRender(stdin, env, term, clock, overrides) {
7048
7351
  try {
@@ -7105,8 +7408,8 @@ function build(stdin, env, term, clock, overrides) {
7105
7408
  const stacks = deriveStacks(markers, events);
7106
7409
  const fresh = freshestEvent(events);
7107
7410
  const stack = pickStack(stacks, fresh?.stack);
7108
- const familiarity = deriveFamiliarity(attribution, persona, scannedCache, project, clock);
7109
- const theme = resolveTheme(config, lookupPackTheme, persona);
7411
+ const familiarity = deriveFamiliarity(attribution, persona, scannedCache, project, clock, String(session));
7412
+ const theme = resolveTheme(config, makePackThemeLookup(persona, loaded), persona);
7110
7413
  const composeInputs = {
7111
7414
  provider,
7112
7415
  model,
@@ -7207,6 +7510,10 @@ function build(stdin, env, term, clock, overrides) {
7207
7510
  }
7208
7511
  swallow(() => {
7209
7512
  const payloadChat = payload.cost?.total_cost_usd;
7513
+ const rescanned = scannedCache !== costCache;
7514
+ const chatChanged = !isDefault && payloadChat !== undefined && scannedCache.aggregate.chat[String(session)] !== payloadChat;
7515
+ if (!rescanned && !chatChanged)
7516
+ return;
7210
7517
  const toWrite = !isDefault && payloadChat !== undefined ? {
7211
7518
  ...scannedCache,
7212
7519
  aggregate: {
@@ -7230,6 +7537,10 @@ function build(stdin, env, term, clock, overrides) {
7230
7537
  swallow(() => {
7231
7538
  readUsage(root, clock, { enabled: config.network.usage_fetch });
7232
7539
  });
7540
+ swallow(() => {
7541
+ if (envInputs.hasApiKey || config.network.usage_fetch)
7542
+ refreshCreds(root, clock);
7543
+ });
7233
7544
  swallow(() => {
7234
7545
  runGc(root, clock);
7235
7546
  });
@@ -7251,7 +7562,7 @@ function dispatchRender() {
7251
7562
  noColor: process.env["NO_COLOR"] !== undefined,
7252
7563
  isTTY: true
7253
7564
  };
7254
- const { line, persist } = runRender(readFileSync11(0, "utf8"), process.env, term, systemClock);
7565
+ const { line, persist } = runRender(readFileSync12(0, "utf8"), process.env, term, systemClock);
7255
7566
  process.stdout.write(`${line}
7256
7567
  `);
7257
7568
  persist();
@@ -7259,7 +7570,7 @@ function dispatchRender() {
7259
7570
  }
7260
7571
  function dispatchClassify() {
7261
7572
  try {
7262
- runClassify(readFileSync11(0, "utf8"), process.env, systemClock);
7573
+ runClassify(readFileSync12(0, "utf8"), process.env, systemClock);
7263
7574
  } catch {} finally {
7264
7575
  process.exit(0);
7265
7576
  }