ccsidekick 1.6.1 → 1.6.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -48,7 +48,7 @@ var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
48
48
  var __require = /* @__PURE__ */ createRequire(import.meta.url);
49
49
 
50
50
  // src/domain/constants.ts
51
- var COST_TTL_MS = 1500, REFRESH_INTERVAL_SEC = 1, USAGE_TTL_MS = 300000, BALANCE_FRESHNESS_MS = 300000, FX_TTL_MS = 604800000, MIN_RIGHT_WIDTH = 53, FIGURE_COLS = 25, FIGURE_ROWS = 9, GAP = 2, SESSION_TTL_DAYS = 30, HOT_MS = 15000, MOOD_WINDOW_MS = 300000, IDLE_ROTATE_MS = 900000, MOOD_FAIL_N = 3, EGG_EVERY_N = 12, COMEBACK_GAP_DAYS = 3, STREAK_MILESTONES, SESSION_MILESTONES, STREAK_GRACE_DAYS = 1, RECENT_WINDOW_DAYS = 30, ANALYTICS_TTL_MS = 1500, CHAR_LINE_MAX = 66, HELPFUL_SHOW_MS = 300000, HELPFUL_COOLDOWN_MS = 600000, BURN_WINDOW_MS = 18000000, COMPACT_URGENT_PCT = 90, QUOTA_HIGH_PCT = 80, PAY_AS_YOU_GO_CAUTION_PCT = 60, PAY_AS_YOU_GO_NEAR_PCT = 80, BALANCE_LOW = 25, SHIMMER_PERIOD_MS = 24000, THEME_CHROMA_MIN = 40, THEME_MAXCH_MIN = 95, SIGNAL_HUE_RANGES;
51
+ var COST_TTL_MS = 5000, REFRESH_INTERVAL_SEC = 1, USAGE_TTL_MS = 300000, CREDS_TTL_MS = 3600000, BALANCE_FRESHNESS_MS = 300000, FX_TTL_MS = 604800000, MIN_RIGHT_WIDTH = 53, FIGURE_COLS = 25, FIGURE_ROWS = 9, GAP = 2, SESSION_TTL_DAYS = 30, HOT_MS = 15000, MOOD_WINDOW_MS = 300000, IDLE_ROTATE_MS = 900000, MOOD_FAIL_N = 3, EGG_EVERY_N = 12, COMEBACK_GAP_DAYS = 3, STREAK_MILESTONES, SESSION_MILESTONES, STREAK_GRACE_DAYS = 1, RECENT_WINDOW_DAYS = 30, ANALYTICS_TTL_MS = 1500, CHAR_LINE_MAX = 66, HELPFUL_SHOW_MS = 300000, HELPFUL_COOLDOWN_MS = 600000, BURN_WINDOW_MS = 18000000, COMPACT_URGENT_PCT = 90, QUOTA_HIGH_PCT = 80, QUOTA_PACE_MIN_PCT = 20, QUOTA_PROJECT_MIN_PCT = 50, PAY_AS_YOU_GO_CAUTION_PCT = 60, PAY_AS_YOU_GO_NEAR_PCT = 80, BALANCE_LOW = 25, SHIMMER_PERIOD_MS = 24000, THEME_CHROMA_MIN = 40, THEME_MAXCH_MIN = 95, SIGNAL_HUE_RANGES, COMPACT_SOON_PCT = 60, COMPACTION_THRASH_N = 3, CACHE_RATIO_FLOOR = 0.5, CACHE_WARMUP_TURNS = 20, BIG_DIFF_LINES = 1000, UNTRACKED_N = 20, STASH_N = 5, STALE_BRANCH_N = 20, TODO_STALLED_MIN = 30, TIER_THRESHOLDS, DAILY_WINDOW_DAYS = 60, PRICING_TIER_THRESHOLD = 200000;
52
52
  var init_constants = __esm(() => {
53
53
  STREAK_MILESTONES = [3, 7, 30, 100];
54
54
  SESSION_MILESTONES = [10, 50, 100, 250, 500];
@@ -57,10 +57,11 @@ var init_constants = __esm(() => {
57
57
  caution: { min: 20, max: 55 },
58
58
  critical: { wrapMax: 15, wrapMin: 345 }
59
59
  };
60
+ TIER_THRESHOLDS = [3, 15, 50, 100];
60
61
  });
61
62
 
62
63
  // src/domain/enums.ts
63
- var MOODS, PRESSURE_MOODS, TIERS, STACKS, EVENT_CATEGORIES, REACTION_CATEGORIES, isStack = (s) => STACKS.includes(s), isEventCategory = (s) => EVENT_CATEGORIES.includes(s);
64
+ var MOODS, PRESSURE_MOODS, TIERS, STACKS, EVENT_CATEGORIES, REACTION_CATEGORIES, MIN_SEVERITIES, CHARACTER_MODES, BANDINGS, isStack = (s) => STACKS.includes(s), isEventCategory = (s) => EVENT_CATEGORIES.includes(s);
64
65
  var init_enums = __esm(() => {
65
66
  MOODS = ["idle", "busy", "happy", "struggling", "recovery"];
66
67
  PRESSURE_MOODS = ["compact_hint", "block_limit", "weekly_limit"];
@@ -147,6 +148,9 @@ var init_enums = __esm(() => {
147
148
  "db_migrate",
148
149
  "dangerous"
149
150
  ];
151
+ MIN_SEVERITIES = ["low", "medium", "high", "critical"];
152
+ CHARACTER_MODES = ["fixed", "random"];
153
+ BANDINGS = ["solid", "cycle"];
150
154
  });
151
155
 
152
156
  // src/domain/ids.ts
@@ -2012,7 +2016,7 @@ function hot(i, category) {
2012
2016
  function cut(s, max = 24) {
2013
2017
  return s.length <= max ? s : `${s.slice(0, Math.max(0, max - 1))}…`;
2014
2018
  }
2015
- var COMPACT_SOON_PCT = 60, QUOTA_PROJECT_MIN_PCT = 50, COMPACTION_THRASH_N = 3, CACHE_RATIO_FLOOR = 0.5, CACHE_WARMUP_TURNS = 20, BIG_DIFF_LINES = 1000, UNTRACKED_N = 20, STASH_N = 5, STALE_BRANCH_N = 20, TODO_STALLED_MIN = 30, EFFORT_LOW_LEVEL = "low", PROD_CONTEXT_PATTERN, SECRET_PATTERNS, SECRET_SAFE, SECRET_RE, SAFE_RE, detached = (g) => g !== null && g.branch === undefined, dirty = (g) => g !== null && g.staged + g.unstaged > 0, onDefault = (g) => g !== null && g.branch !== undefined && g.defaultBranch !== undefined && g.branch === g.defaultBranch, ahead = (g) => g?.ahead ?? 0, behind = (g) => g?.behind ?? 0, bare = (left) => (left ?? "").replace(/ left$/, ""), branchName = (g) => cut(g?.branch ?? ""), HELPFUL_CATALOG;
2019
+ var EFFORT_LOW_LEVEL = "low", PROD_CONTEXT_PATTERN, SECRET_PATTERNS, SECRET_SAFE, SECRET_RE, SAFE_RE, detached = (g) => g !== null && g.branch === undefined, dirty = (g) => g !== null && g.staged + g.unstaged > 0, onDefault = (g) => g !== null && g.branch !== undefined && g.defaultBranch !== undefined && g.branch === g.defaultBranch, ahead = (g) => g?.ahead ?? 0, behind = (g) => g?.behind ?? 0, bare = (left) => (left ?? "").replace(/ left$/, ""), branchName = (g) => cut(g?.branch ?? ""), HELPFUL_CATALOG;
2016
2020
  var init_catalog = __esm(() => {
2017
2021
  init_domain();
2018
2022
  init_format();
@@ -2162,7 +2166,7 @@ var init_catalog = __esm(() => {
2162
2166
  category: "quota",
2163
2167
  momentary: false,
2164
2168
  template: "5h limit at {pct}%, {reset} to reset. Save heavy asks for after.",
2165
- test: (i) => i.quota.block !== undefined && i.quota.block.usedPct > QUOTA_HIGH_PCT,
2169
+ test: (i) => i.quota.block !== undefined && i.quota.block.usedPct > QUOTA_HIGH_PCT && i.quota.block.overPace === true,
2166
2170
  render: (i) => `5h limit at ${pct(i.quota.block?.usedPct ?? 0)}, ${bare(i.quota.block?.resetIn)} to reset. Save heavy asks for after.`
2167
2171
  },
2168
2172
  {
@@ -2171,7 +2175,7 @@ var init_catalog = __esm(() => {
2171
2175
  category: "quota",
2172
2176
  momentary: false,
2173
2177
  template: "Weekly quota {pct}% spent, {reset} to go. Ration the big requests.",
2174
- test: (i) => i.quota.weekly !== undefined && i.quota.weekly.usedPct > QUOTA_HIGH_PCT,
2178
+ test: (i) => i.quota.weekly !== undefined && i.quota.weekly.usedPct > QUOTA_HIGH_PCT && i.quota.weekly.overPace === true,
2175
2179
  render: (i) => `Weekly quota ${pct(i.quota.weekly?.usedPct ?? 0)} spent, ${bare(i.quota.weekly?.resetIn)} to go. Ration the big requests.`
2176
2180
  },
2177
2181
  {
@@ -2180,7 +2184,7 @@ var init_catalog = __esm(() => {
2180
2184
  category: "quota",
2181
2185
  momentary: false,
2182
2186
  template: "This burn rate empties the 5h block before reset, cutting you off mid-task.",
2183
- test: (i) => i.quota.block !== undefined && i.quota.block.band !== "nominal" && i.quota.block.usedPct > QUOTA_PROJECT_MIN_PCT,
2187
+ test: (i) => i.quota.block !== undefined && i.quota.block.usedPct > QUOTA_PROJECT_MIN_PCT && i.quota.block.overPace === true,
2184
2188
  render: () => "This burn rate empties the 5h block before reset, cutting you off mid-task."
2185
2189
  },
2186
2190
  {
@@ -2189,7 +2193,7 @@ var init_catalog = __esm(() => {
2189
2193
  category: "quota",
2190
2194
  momentary: false,
2191
2195
  template: "At this pace the weekly quota runs dry before reset, locking you out for days.",
2192
- test: (i) => i.quota.weekly !== undefined && i.quota.weekly.band !== "nominal" && i.quota.weekly.usedPct > QUOTA_PROJECT_MIN_PCT,
2196
+ test: (i) => i.quota.weekly !== undefined && i.quota.weekly.usedPct > QUOTA_PROJECT_MIN_PCT && i.quota.weekly.overPace === true,
2193
2197
  render: () => "At this pace the weekly quota runs dry before reset, locking you out for days."
2194
2198
  },
2195
2199
  {
@@ -2810,9 +2814,9 @@ function modelKeyOf(modelId, aliases = NO_ALIASES) {
2810
2814
  return resolveKey(modelId, aliases) ?? modelId;
2811
2815
  }
2812
2816
  function tiered(tokens, base, above) {
2813
- if (tokens <= TIER_THRESHOLD || above === undefined)
2817
+ if (tokens <= PRICING_TIER_THRESHOLD || above === undefined)
2814
2818
  return tokens * base;
2815
- return TIER_THRESHOLD * base + (tokens - TIER_THRESHOLD) * above;
2819
+ return PRICING_TIER_THRESHOLD * base + (tokens - PRICING_TIER_THRESHOLD) * above;
2816
2820
  }
2817
2821
  function priceMessage(usage, modelId, aliases = NO_ALIASES, atMs) {
2818
2822
  const row = resolvePrice(modelId, aliases, atMs);
@@ -2829,9 +2833,10 @@ function priceMessage(usage, modelId, aliases = NO_ALIASES, atMs) {
2829
2833
  const speedMult = usage.speed === "fast" ? row.fast_mult : 1;
2830
2834
  return cost * speedMult;
2831
2835
  }
2832
- var PER_MILLION = 1e6, TIER_THRESHOLD = 200000, RAW, TABLE, NO_ALIASES, isAlnum = (c) => c !== "" && /[a-z0-9]/i.test(c);
2836
+ var PER_MILLION = 1e6, RAW, TABLE, NO_ALIASES, isAlnum = (c) => c !== "" && /[a-z0-9]/i.test(c);
2833
2837
  var init_pricing2 = __esm(() => {
2834
2838
  init_pricing();
2839
+ init_domain();
2835
2840
  RAW = pricing_default;
2836
2841
  TABLE = (() => {
2837
2842
  const byKey = new Map;
@@ -2914,6 +2919,7 @@ function joinRecords(attribution, cache, tz) {
2914
2919
  for (const [session, g] of groupBySession(cache)) {
2915
2920
  const attr = attribution[session];
2916
2921
  out.push({
2922
+ session,
2917
2923
  character: attr?.character ?? "unknown",
2918
2924
  project: attr?.project ?? g.projectRaw,
2919
2925
  start: g.start,
@@ -2949,7 +2955,7 @@ function currentStreak(activeDays, today) {
2949
2955
  }
2950
2956
  return streak2;
2951
2957
  }
2952
- function deriveFamiliarity(attribution, character, cache, project, clock) {
2958
+ function deriveFamiliarity(attribution, character, cache, project, clock, currentSession = "") {
2953
2959
  const tz = clock.timezone();
2954
2960
  const records = joinRecords(attribution, cache, tz);
2955
2961
  const seenProject = records.some((r) => r.project === String(project));
@@ -2961,10 +2967,12 @@ function deriveFamiliarity(attribution, character, cache, project, clock) {
2961
2967
  let lastEnd = Number.NEGATIVE_INFINITY;
2962
2968
  let workingSinceMs = Number.POSITIVE_INFINITY;
2963
2969
  for (const r of mine) {
2964
- if (r.end > lastEnd)
2965
- lastEnd = r.end;
2966
2970
  if (r.start < workingSinceMs)
2967
2971
  workingSinceMs = r.start;
2972
+ if (r.session === currentSession)
2973
+ continue;
2974
+ if (r.end > lastEnd)
2975
+ lastEnd = r.end;
2968
2976
  }
2969
2977
  const daysSinceLastSession = Number.isFinite(lastEnd) ? Math.floor((clock.now() - lastEnd) / MS_PER_DAY) : Number.POSITIVE_INFINITY;
2970
2978
  return {
@@ -3003,12 +3011,20 @@ function hourOfDay(ms, tz) {
3003
3011
  }
3004
3012
  function joinFull(attribution, cache, tz, normalizeProject) {
3005
3013
  const tokenPriced = cache.aggregate.tokenPriced;
3014
+ const groups = [...groupBySession(cache)];
3015
+ const rootToName = new Map;
3016
+ for (const [session, g] of groups) {
3017
+ const attr = attribution[session];
3018
+ if (attr !== undefined)
3019
+ rootToName.set(normalizeProject(g.projectRaw), attr.project);
3020
+ }
3006
3021
  const out = [];
3007
- for (const [session, g] of groupBySession(cache)) {
3022
+ for (const [session, g] of groups) {
3008
3023
  const attr = attribution[session];
3024
+ const root = normalizeProject(g.projectRaw);
3009
3025
  out.push({
3010
3026
  session,
3011
- project: normalizeProject(attr?.project ?? g.projectRaw),
3027
+ project: attr?.project ?? rootToName.get(root) ?? root,
3012
3028
  character: attr?.character ?? "unknown",
3013
3029
  start: g.start,
3014
3030
  end: g.end,
@@ -3267,11 +3283,10 @@ function buildModels(byModel) {
3267
3283
  share: totalCost > 0 ? v.cost / totalCost : 0
3268
3284
  })).sort((a, b) => b.costUsd - a.costUsd);
3269
3285
  }
3270
- var MS_PER_DAY = 86400000, TIER_THRESHOLDS, DAILY_WINDOW_DAYS = 60;
3286
+ var MS_PER_DAY = 86400000;
3271
3287
  var init_analytics = __esm(() => {
3272
3288
  init_domain();
3273
3289
  init_pricing2();
3274
- TIER_THRESHOLDS = [3, 15, 50, 100];
3275
3290
  });
3276
3291
 
3277
3292
  // src/derived/signals.ts
@@ -3283,9 +3298,9 @@ function contextBand(pct2) {
3283
3298
  return "critical";
3284
3299
  }
3285
3300
  function quotaBand(usedPct, resetsAtMs, windowMs, nowMs) {
3286
- if (usedPct >= QUOTA_CRITICAL_PCT)
3301
+ if (usedPct >= QUOTA_HIGH_PCT)
3287
3302
  return "critical";
3288
- if (resetsAtMs === undefined)
3303
+ if (resetsAtMs === undefined || usedPct < QUOTA_PACE_MIN_PCT)
3289
3304
  return contextBand(usedPct);
3290
3305
  const usedFraction = usedPct / 100;
3291
3306
  const elapsedFraction = Math.max((nowMs - (resetsAtMs - windowMs)) / windowMs, 0.01);
@@ -3296,7 +3311,16 @@ function quotaBand(usedPct, resetsAtMs, windowMs, nowMs) {
3296
3311
  return "caution";
3297
3312
  return "critical";
3298
3313
  }
3299
- var QUOTA_CRITICAL_PCT = 80;
3314
+ function overQuotaPace(usedPct, resetsAtMs, windowMs, nowMs) {
3315
+ if (resetsAtMs === undefined)
3316
+ return false;
3317
+ const usedFraction = usedPct / 100;
3318
+ const elapsedFraction = Math.max((nowMs - (resetsAtMs - windowMs)) / windowMs, 0.01);
3319
+ return usedFraction / elapsedFraction > 1.5;
3320
+ }
3321
+ var init_signals = __esm(() => {
3322
+ init_domain();
3323
+ });
3300
3324
 
3301
3325
  // src/derived/context.ts
3302
3326
  function deriveContext(payload, scan) {
@@ -3320,6 +3344,7 @@ function deriveContext(payload, scan) {
3320
3344
  }
3321
3345
  var init_context = __esm(() => {
3322
3346
  init_domain();
3347
+ init_signals();
3323
3348
  });
3324
3349
 
3325
3350
  // src/derived/cost.ts
@@ -3604,12 +3629,13 @@ function buildWindow(payloadQuota, oauthQuota, windowMs, clock) {
3604
3629
  return;
3605
3630
  }
3606
3631
  const band2 = quotaBand(usedPct, resetsAtMs, windowMs, clock.now());
3632
+ const overPace = overQuotaPace(usedPct, resetsAtMs, windowMs, clock.now());
3607
3633
  if (resetsAtMs === undefined)
3608
- return { usedPct, band: band2 };
3634
+ return { usedPct, band: band2, overPace };
3609
3635
  const left = resetsAtMs - clock.now();
3610
3636
  if (left <= 0)
3611
- return { usedPct, band: band2 };
3612
- return { usedPct, band: band2, resetIn: fmtLeft(left) };
3637
+ return { usedPct, band: band2, overPace };
3638
+ return { usedPct, band: band2, overPace, resetIn: fmtLeft(left) };
3613
3639
  }
3614
3640
  function buildPayg(usage) {
3615
3641
  const extra = usage?.extra_usage;
@@ -3649,6 +3675,7 @@ var FIVE_HOUR_MS, SEVEN_DAY_MS;
3649
3675
  var init_quota = __esm(() => {
3650
3676
  init_domain();
3651
3677
  init_format();
3678
+ init_signals();
3652
3679
  FIVE_HOUR_MS = 5 * 60 * 60 * 1000;
3653
3680
  SEVEN_DAY_MS = 7 * 24 * 60 * 60 * 1000;
3654
3681
  });
@@ -4006,7 +4033,7 @@ function readJson(path, fallback) {
4006
4033
  var init_readJson = () => {};
4007
4034
 
4008
4035
  // src/sources/storage/withLock.ts
4009
- import { closeSync, mkdirSync as mkdirSync2, openSync, statSync, unlinkSync } from "node:fs";
4036
+ import { closeSync, mkdirSync as mkdirSync2, openSync, renameSync as renameSync2, statSync, unlinkSync } from "node:fs";
4010
4037
  import { dirname as dirname2 } from "node:path";
4011
4038
  function withLock(lockPath, fn, readOnly) {
4012
4039
  let fd;
@@ -4020,8 +4047,12 @@ function withLock(lockPath, fn, readOnly) {
4020
4047
  } catch {
4021
4048
  try {
4022
4049
  if (Date.now() - statSync(lockPath).mtimeMs > STALE_MS) {
4023
- unlinkSync(lockPath);
4050
+ const aside = `${lockPath}.stale`;
4051
+ renameSync2(lockPath, aside);
4024
4052
  fd = openSync(lockPath, "wx");
4053
+ try {
4054
+ unlinkSync(aside);
4055
+ } catch {}
4025
4056
  }
4026
4057
  } catch {}
4027
4058
  }
@@ -4109,6 +4140,10 @@ function upsertAttribution(root, sessionId, rec) {
4109
4140
  const path = storePath(root);
4110
4141
  withLock(`${path}.lock`, () => {
4111
4142
  const store = coerceStore(readJson(path, undefined));
4143
+ const existing = store[sessionId];
4144
+ if (existing !== undefined && existing.project === rec.project && existing.character === rec.character) {
4145
+ return;
4146
+ }
4112
4147
  store[sessionId] = rec.updatedMs !== undefined ? { project: rec.project, character: rec.character, updatedMs: rec.updatedMs } : { project: rec.project, character: rec.character };
4113
4148
  atomicWrite(path, JSON.stringify(store));
4114
4149
  }, () => {});
@@ -4119,9 +4154,17 @@ var init_analyticsStore = __esm(() => {
4119
4154
  });
4120
4155
 
4121
4156
  // src/sources/balance.ts
4157
+ import { statSync as statSync3 } from "node:fs";
4122
4158
  function readBalance(path, clock) {
4123
4159
  if (path === "")
4124
4160
  return null;
4161
+ try {
4162
+ const st = statSync3(path);
4163
+ if (!st.isFile() || st.size > BALANCE_MAX_BYTES)
4164
+ return null;
4165
+ } catch {
4166
+ return null;
4167
+ }
4125
4168
  const raw = readJson(path, undefined);
4126
4169
  if (typeof raw !== "object" || raw === null)
4127
4170
  return null;
@@ -4139,9 +4182,11 @@ function readBalance(path, clock) {
4139
4182
  return null;
4140
4183
  return { amount, currency, ts };
4141
4184
  }
4185
+ var BALANCE_MAX_BYTES;
4142
4186
  var init_balance = __esm(() => {
4143
4187
  init_domain();
4144
4188
  init_storage();
4189
+ BALANCE_MAX_BYTES = 64 * 1024;
4145
4190
  });
4146
4191
 
4147
4192
  // src/sources/clock.ts
@@ -5400,10 +5445,10 @@ function loadConfig(globalToml = "", projectToml = "") {
5400
5445
  const statusline = section(g, p, "statusline");
5401
5446
  const d = DEFAULT_CONFIG;
5402
5447
  return {
5403
- schema_version: numv(p["schema_version"] ?? g["schema_version"], d.schema_version),
5448
+ schema_version: numv(p["schema_version"], numv(g["schema_version"], d.schema_version)),
5404
5449
  character: {
5405
5450
  enabled: bool(character["enabled"], d.character.enabled),
5406
- mode: oneOf(character["mode"], ["fixed", "random"], d.character.mode),
5451
+ mode: oneOf(character["mode"], CHARACTER_MODES, d.character.mode),
5407
5452
  name: str(character["name"], d.character.name),
5408
5453
  roster: stringArray(character["roster"], d.character.roster)
5409
5454
  },
@@ -5412,7 +5457,7 @@ function loadConfig(globalToml = "", projectToml = "") {
5412
5457
  ...typeof theme["statusline"] === "string" ? { statusline: theme["statusline"] } : {},
5413
5458
  ...typeof theme["logo"] === "string" ? { logo: theme["logo"] } : {},
5414
5459
  ...typeof theme["comment"] === "string" ? { comment: theme["comment"] } : {},
5415
- banding: oneOf(theme["banding"], ["solid", "cycle"], d.theme.banding),
5460
+ banding: oneOf(theme["banding"], BANDINGS, d.theme.banding),
5416
5461
  mood_shift: bool(theme["mood_shift"], d.theme.mood_shift),
5417
5462
  icons: mergeIcons({
5418
5463
  ...obj(obj(g["theme"])["icons"]),
@@ -5422,7 +5467,7 @@ function loadConfig(globalToml = "", projectToml = "") {
5422
5467
  comments: {
5423
5468
  character: bool(comments["character"], d.comments.character),
5424
5469
  helpful: bool(comments["helpful"], d.comments.helpful),
5425
- min_severity: oneOf(comments["min_severity"], ["low", "medium", "high", "critical"], d.comments.min_severity)
5470
+ min_severity: oneOf(comments["min_severity"], MIN_SEVERITIES, d.comments.min_severity)
5426
5471
  },
5427
5472
  network: {
5428
5473
  fx_refresh: bool(network["fx_refresh"], d.network.fx_refresh),
@@ -5445,6 +5490,7 @@ var DEFAULT_WIDGETS, DEFAULT_CONFIG, obj = (v) => v !== null && typeof v === "ob
5445
5490
  });
5446
5491
  var init_config = __esm(() => {
5447
5492
  init_dist();
5493
+ init_domain();
5448
5494
  init_locale();
5449
5495
  DEFAULT_WIDGETS = {
5450
5496
  dir: true,
@@ -5598,7 +5644,19 @@ function coerceEntry(v) {
5598
5644
  }
5599
5645
  const projectPath = typeof v["projectPath"] === "string" ? v["projectPath"] : String(record.project);
5600
5646
  const models = Array.isArray(v["models"]) ? v["models"].filter((x) => typeof x === "string") : [];
5601
- return { mtime, size, total, lines, models, projectPath, record };
5647
+ const byteOffset = asNumber(v["byteOffset"]);
5648
+ const headHash = typeof v["headHash"] === "string" ? v["headHash"] : undefined;
5649
+ return {
5650
+ mtime,
5651
+ size,
5652
+ total,
5653
+ lines,
5654
+ models,
5655
+ projectPath,
5656
+ record,
5657
+ ...byteOffset !== undefined ? { byteOffset } : {},
5658
+ ...headHash !== undefined ? { headHash } : {}
5659
+ };
5602
5660
  }
5603
5661
  function coerceByModel(v) {
5604
5662
  if (!isObject(v))
@@ -5855,13 +5913,9 @@ var init_oauthUsage = __esm(() => {
5855
5913
  // src/sources/creds.ts
5856
5914
  import { spawnSync as spawnSync2 } from "node:child_process";
5857
5915
  import { readFileSync as readFileSync4 } from "node:fs";
5858
- import { homedir as homedir3 } from "node:os";
5859
5916
  import { join as join5 } from "node:path";
5860
- function configBase2() {
5861
- return process.env["CLAUDE_CONFIG_DIR"] ?? join5(homedir3(), ".claude");
5862
- }
5863
5917
  function readBlob(run, base) {
5864
- for (const service of [keychainService(base), KEYCHAIN_SERVICE2]) {
5918
+ for (const service of [keychainService(base), KEYCHAIN_SERVICE]) {
5865
5919
  const keychain = run("security", ["find-generic-password", "-s", service, "-w"]);
5866
5920
  if (keychain.trim() !== "")
5867
5921
  return keychain;
@@ -5871,7 +5925,7 @@ function readBlob(run, base) {
5871
5925
  if (file.trim() !== "")
5872
5926
  return file;
5873
5927
  } catch {}
5874
- const secretTool = run("secret-tool", ["lookup", "service", KEYCHAIN_SERVICE2]);
5928
+ const secretTool = run("secret-tool", ["lookup", "service", KEYCHAIN_SERVICE]);
5875
5929
  if (secretTool.trim() !== "")
5876
5930
  return secretTool;
5877
5931
  return "";
@@ -5900,7 +5954,7 @@ function readOrgTier(base) {
5900
5954
  }
5901
5955
  function readCreds(run = defaultRunner) {
5902
5956
  try {
5903
- const base = configBase2();
5957
+ const base = configBase();
5904
5958
  const blob = readBlob(run, base);
5905
5959
  if (blob.trim() !== "") {
5906
5960
  const tier = parseSubscription(blob);
@@ -5914,7 +5968,39 @@ function readCreds(run = defaultRunner) {
5914
5968
  return null;
5915
5969
  }
5916
5970
  }
5917
- var KEYCHAIN_SERVICE2 = "Claude Code-credentials", SUBSCRIPTIONS, isObject3 = (v) => typeof v === "object" && v !== null, defaultRunner = (cmd, args) => {
5971
+ function credsCachePath(root) {
5972
+ return join5(cacheDir(root), CREDS_CACHE_FILE);
5973
+ }
5974
+ function coerceCachedCreds(raw) {
5975
+ if (!isObject3(raw) || typeof raw["at"] !== "number")
5976
+ return null;
5977
+ const at = raw["at"];
5978
+ const info = raw["info"];
5979
+ if (info === null)
5980
+ return { info: null, at };
5981
+ if (isObject3(info) && typeof info["present"] === "boolean") {
5982
+ const sub = info["subscriptionType"];
5983
+ return {
5984
+ at,
5985
+ info: typeof sub === "string" && SUBSCRIPTIONS.has(sub) ? { present: info["present"], subscriptionType: sub } : { present: info["present"] }
5986
+ };
5987
+ }
5988
+ return null;
5989
+ }
5990
+ function readCredsCached(root) {
5991
+ return coerceCachedCreds(readJson(credsCachePath(root), undefined))?.info ?? null;
5992
+ }
5993
+ function refreshCreds(root, clock, run = defaultRunner) {
5994
+ const cached = coerceCachedCreds(readJson(credsCachePath(root), undefined));
5995
+ const now = clock.now();
5996
+ if (cached !== null && now - cached.at <= CREDS_TTL_MS)
5997
+ return;
5998
+ const info = readCreds(run);
5999
+ try {
6000
+ atomicWrite(credsCachePath(root), JSON.stringify({ info, at: now }));
6001
+ } catch {}
6002
+ }
6003
+ var SUBSCRIPTIONS, isObject3 = (v) => typeof v === "object" && v !== null, defaultRunner = (cmd, args) => {
5918
6004
  try {
5919
6005
  const r = spawnSync2(cmd, args, {
5920
6006
  encoding: "utf8",
@@ -5927,9 +6013,11 @@ var KEYCHAIN_SERVICE2 = "Claude Code-credentials", SUBSCRIPTIONS, isObject3 = (v
5927
6013
  } catch {
5928
6014
  return "";
5929
6015
  }
5930
- };
6016
+ }, CREDS_CACHE_FILE = "creds.json";
5931
6017
  var init_creds = __esm(() => {
6018
+ init_domain();
5932
6019
  init_oauthUsage();
6020
+ init_storage();
5933
6021
  SUBSCRIPTIONS = new Set(["pro", "max", "team", "enterprise"]);
5934
6022
  });
5935
6023
 
@@ -6408,12 +6496,14 @@ function readSubmoduleRollup(run, workTreeRoot) {
6408
6496
  return rollup;
6409
6497
  }
6410
6498
  function readGit(cwd, run = defaultRunner2(cwd)) {
6411
- if (run(["rev-parse", "--is-inside-work-tree"]).trim() !== "true")
6499
+ const probe = run(["rev-parse", "--is-inside-work-tree", "--git-dir", "--show-toplevel"]).split(`
6500
+ `);
6501
+ if ((probe[0] ?? "").trim() !== "true")
6412
6502
  return null;
6413
6503
  const status = parseStatus(run(["status", "--porcelain=v2", "--branch", "--show-stash"]));
6414
- const gitDirRaw = run(["rev-parse", "--git-dir"]).trim();
6504
+ const gitDirRaw = (probe[1] ?? "").trim();
6415
6505
  const gitDir = gitDirRaw === "" ? "" : resolve(cwd, gitDirRaw);
6416
- const workTreeRoot = run(["rev-parse", "--show-toplevel"]).trim();
6506
+ const workTreeRoot = (probe[2] ?? "").trim();
6417
6507
  const tag = run(["describe", "--tags", "--exact-match"]).trim() || undefined;
6418
6508
  const originRepo = normalizeOriginRepo(run(["remote", "get-url", "origin"]));
6419
6509
  const defaultBranch = resolveDefaultBranch(run);
@@ -6474,7 +6564,7 @@ var init_git = __esm(() => {
6474
6564
 
6475
6565
  // src/sources/helpfulEnv.ts
6476
6566
  import { readFileSync as readFileSync7 } from "node:fs";
6477
- import { homedir as homedir4 } from "node:os";
6567
+ import { homedir as homedir3 } from "node:os";
6478
6568
  import { delimiter, join as join9 } from "node:path";
6479
6569
  function kubeconfigPath() {
6480
6570
  const env = process.env["KUBECONFIG"];
@@ -6483,7 +6573,7 @@ function kubeconfigPath() {
6483
6573
  if (first !== undefined)
6484
6574
  return first;
6485
6575
  }
6486
- return join9(homedir4(), ".kube", "config");
6576
+ return join9(homedir3(), ".kube", "config");
6487
6577
  }
6488
6578
  function readKubeContext() {
6489
6579
  try {
@@ -6799,7 +6889,7 @@ var init_state = __esm(() => {
6799
6889
  });
6800
6890
 
6801
6891
  // src/sources/transcript.ts
6802
- import { readFileSync as readFileSync8, readdirSync as readdirSync2, statSync as statSync3 } from "node:fs";
6892
+ import { closeSync as closeSync2, openSync as openSync2, readFileSync as readFileSync8, readSync, readdirSync as readdirSync2, statSync as statSync4 } from "node:fs";
6803
6893
  import { basename as basename2, dirname as dirname5, join as join12 } from "node:path";
6804
6894
  function parseTs(s) {
6805
6895
  if (s === undefined)
@@ -7021,7 +7111,7 @@ function reconstructTodos(lines) {
7021
7111
  }
7022
7112
  function statSafe(p) {
7023
7113
  try {
7024
- const s = statSync3(p);
7114
+ const s = statSync4(p);
7025
7115
  return { mtimeMs: s.mtimeMs, size: s.size };
7026
7116
  } catch {
7027
7117
  return null;
@@ -7034,6 +7124,61 @@ function readSafe(p) {
7034
7124
  return "";
7035
7125
  }
7036
7126
  }
7127
+ function readBufSafe(p) {
7128
+ try {
7129
+ return readFileSync8(p);
7130
+ } catch {
7131
+ return null;
7132
+ }
7133
+ }
7134
+ function readRange(p, start, end) {
7135
+ if (end <= start)
7136
+ return Buffer.alloc(0);
7137
+ let fd;
7138
+ try {
7139
+ fd = openSync2(p, "r");
7140
+ const len = end - start;
7141
+ const buf = Buffer.allocUnsafe(len);
7142
+ let off = 0;
7143
+ while (off < len) {
7144
+ const n = readSync(fd, buf, off, len - off, start + off);
7145
+ if (n === 0)
7146
+ break;
7147
+ off += n;
7148
+ }
7149
+ return off === len ? buf : null;
7150
+ } catch {
7151
+ return null;
7152
+ } finally {
7153
+ if (fd !== undefined)
7154
+ closeSync2(fd);
7155
+ }
7156
+ }
7157
+ function headHashOf(head) {
7158
+ let h = 2166136261;
7159
+ for (const b of head) {
7160
+ h ^= b;
7161
+ h = Math.imul(h, 16777619);
7162
+ }
7163
+ return (h >>> 0).toString(16);
7164
+ }
7165
+ function makeInternModel(models) {
7166
+ const index = new Map;
7167
+ models.forEach((m, i) => index.set(m, i));
7168
+ return (model) => {
7169
+ let i = index.get(model);
7170
+ if (i === undefined) {
7171
+ i = models.length;
7172
+ models.push(model);
7173
+ index.set(model, i);
7174
+ }
7175
+ return i;
7176
+ };
7177
+ }
7178
+ function lastLineEnd(buf) {
7179
+ const i = buf.lastIndexOf(10);
7180
+ return i < 0 ? 0 : i + 1;
7181
+ }
7037
7182
  function emptyScan(mtime, size) {
7038
7183
  return { tokens: EMPTY_TOKENS, messages: 0, compactions: 0, todos: [], burn: [], mtime, size };
7039
7184
  }
@@ -7113,7 +7258,7 @@ function repoRootForCwd(cwd) {
7113
7258
  let result = cwd;
7114
7259
  while (dir !== "" && dir !== dirname5(dir)) {
7115
7260
  try {
7116
- if (statSync3(join12(dir, ".git")).isDirectory()) {
7261
+ if (statSync4(join12(dir, ".git")).isDirectory()) {
7117
7262
  result = dir;
7118
7263
  break;
7119
7264
  }
@@ -7134,7 +7279,7 @@ function* walkTree(dir, encodedDir) {
7134
7279
  const full = join12(dir, name);
7135
7280
  let st;
7136
7281
  try {
7137
- st = statSync3(full);
7282
+ st = statSync4(full);
7138
7283
  } catch {
7139
7284
  continue;
7140
7285
  }
@@ -7173,22 +7318,16 @@ function scanBounds(lines) {
7173
7318
  return { sessionId, start, end };
7174
7319
  }
7175
7320
  function priceFile(path, encodedDir, st, price, resolveProject) {
7176
- const lines = parseAll(readSafe(path));
7321
+ const buf = readBufSafe(path) ?? Buffer.alloc(0);
7322
+ const byteOffset = lastLineEnd(buf);
7323
+ const headHash = headHashOf(buf.subarray(0, headLen(byteOffset)));
7324
+ const lines = parseAll(buf.subarray(0, byteOffset).toString("utf8"));
7177
7325
  const { sessionId, start, end } = scanBounds(lines);
7178
7326
  const priced = collapseStreaming(lines);
7179
7327
  const gate = makeDedupGate();
7180
7328
  const costLines = [];
7181
7329
  const models = [];
7182
- const modelIndex = new Map;
7183
- const internModel = (model) => {
7184
- let i = modelIndex.get(model);
7185
- if (i === undefined) {
7186
- i = models.length;
7187
- models.push(model);
7188
- modelIndex.set(model, i);
7189
- }
7190
- return i;
7191
- };
7330
+ const internModel = makeInternModel(models);
7192
7331
  let total = 0;
7193
7332
  let input = 0;
7194
7333
  let output = 0;
@@ -7233,6 +7372,8 @@ function priceFile(path, encodedDir, st, price, resolveProject) {
7233
7372
  return {
7234
7373
  mtime: st.mtimeMs,
7235
7374
  size: st.size,
7375
+ byteOffset,
7376
+ headHash,
7236
7377
  total,
7237
7378
  lines: costLines,
7238
7379
  models,
@@ -7240,6 +7381,138 @@ function priceFile(path, encodedDir, st, price, resolveProject) {
7240
7381
  record
7241
7382
  };
7242
7383
  }
7384
+ function resumeCostFile(cached, path, st, price) {
7385
+ const tail = readAppendedTail(cached, path, st);
7386
+ if (tail === null)
7387
+ return null;
7388
+ if (tail === "partial")
7389
+ return { ...cached, mtime: st.mtimeMs, size: st.size };
7390
+ const models = [...cached.models];
7391
+ const keyIndex = new Map;
7392
+ cached.lines.forEach((l, i) => {
7393
+ if (l.id !== undefined && l.reqId !== undefined)
7394
+ keyIndex.set(`${l.id}|${l.reqId}`, i);
7395
+ });
7396
+ const gate = makeDedupGate();
7397
+ const acc = {
7398
+ lines: [...cached.lines],
7399
+ models,
7400
+ counted: cached.lines.map((l) => gate(l.id, l.reqId, l.sidechain)),
7401
+ keyIndex,
7402
+ gate,
7403
+ internModel: makeInternModel(models),
7404
+ input: cached.record.tokens.input,
7405
+ output: cached.record.tokens.output,
7406
+ cacheRead: cached.record.tokens.cache_read,
7407
+ cacheCreation: cached.record.tokens.cache_creation,
7408
+ total: cached.total,
7409
+ messages: cached.record.messages,
7410
+ start: cached.record.start,
7411
+ end: cached.record.end
7412
+ };
7413
+ for (const l of collapseStreaming(tail.appended))
7414
+ foldAppendedLine(acc, l, price);
7415
+ return {
7416
+ mtime: st.mtimeMs,
7417
+ size: st.size,
7418
+ byteOffset: tail.newByteOffset,
7419
+ headHash: tail.newHeadHash,
7420
+ total: acc.total,
7421
+ lines: acc.lines,
7422
+ models: acc.models,
7423
+ projectPath: cached.projectPath,
7424
+ record: {
7425
+ ...cached.record,
7426
+ start: acc.start,
7427
+ end: acc.end,
7428
+ tokens: {
7429
+ input: acc.input,
7430
+ output: acc.output,
7431
+ cache_read: acc.cacheRead,
7432
+ cache_creation: acc.cacheCreation
7433
+ },
7434
+ messages: acc.messages
7435
+ }
7436
+ };
7437
+ }
7438
+ function costLineOf(internModel, l, cost, totalTok) {
7439
+ return {
7440
+ ...l.id !== undefined ? { id: l.id } : {},
7441
+ ...l.requestId !== undefined ? { reqId: l.requestId } : {},
7442
+ sidechain: l.isSidechain,
7443
+ ts: l.tsMs ?? 0,
7444
+ cost,
7445
+ ...l.model !== undefined ? { m: internModel(l.model) } : {},
7446
+ tok: totalTok
7447
+ };
7448
+ }
7449
+ function readAppendedTail(cached, path, st) {
7450
+ const offset = cached.byteOffset;
7451
+ if (offset === undefined || offset === 0 || st.size < offset)
7452
+ return null;
7453
+ const head = readRange(path, 0, headLen(offset));
7454
+ if (head === null || headHashOf(head) !== cached.headHash)
7455
+ return null;
7456
+ const tailBuf = readRange(path, offset, st.size);
7457
+ if (tailBuf === null)
7458
+ return null;
7459
+ const added = lastLineEnd(tailBuf);
7460
+ if (added === 0)
7461
+ return "partial";
7462
+ const newByteOffset = offset + added;
7463
+ const newHead = headLen(newByteOffset) === head.length ? head : readRange(path, 0, headLen(newByteOffset));
7464
+ if (newHead === null)
7465
+ return null;
7466
+ return {
7467
+ appended: parseAll(tailBuf.subarray(0, added).toString("utf8")),
7468
+ newByteOffset,
7469
+ newHeadHash: headHashOf(newHead)
7470
+ };
7471
+ }
7472
+ function widenBounds(acc, tsMs) {
7473
+ if (tsMs === undefined)
7474
+ return;
7475
+ if (tsMs > acc.end)
7476
+ acc.end = tsMs;
7477
+ if (acc.start === 0 || tsMs < acc.start)
7478
+ acc.start = tsMs;
7479
+ }
7480
+ function foldAppendedLine(acc, l, price) {
7481
+ widenBounds(acc, l.tsMs);
7482
+ const usage = l.usage;
7483
+ if (usage === undefined)
7484
+ return;
7485
+ const t = tokenize(usage);
7486
+ const totalTok = t.input + t.output + t.cache_read + t.c5m + t.c1h;
7487
+ const key = l.id !== undefined && l.requestId !== undefined ? `${l.id}|${l.requestId}` : null;
7488
+ const existing = key !== null ? acc.keyIndex.get(key) : undefined;
7489
+ const old = existing !== undefined ? acc.lines[existing] : undefined;
7490
+ if (existing !== undefined && old !== undefined) {
7491
+ if (totalTok <= (old.tok ?? 0))
7492
+ return;
7493
+ const cost2 = price(usage, l.model ?? "", l.tsMs);
7494
+ if (acc.counted[existing] === true) {
7495
+ acc.output += totalTok - (old.tok ?? 0);
7496
+ acc.total += cost2 - old.cost;
7497
+ }
7498
+ acc.lines[existing] = costLineOf(acc.internModel, l, cost2, totalTok);
7499
+ return;
7500
+ }
7501
+ const cost = price(usage, l.model ?? "", l.tsMs);
7502
+ const isCounted = acc.gate(l.id, l.requestId, l.isSidechain);
7503
+ if (key !== null)
7504
+ acc.keyIndex.set(key, acc.lines.length);
7505
+ acc.lines.push(costLineOf(acc.internModel, l, cost, totalTok));
7506
+ acc.counted.push(isCounted);
7507
+ if (isCounted) {
7508
+ acc.input += t.input;
7509
+ acc.output += t.output;
7510
+ acc.cacheRead += t.cache_read;
7511
+ acc.cacheCreation += t.c5m + t.c1h;
7512
+ acc.total += cost;
7513
+ acc.messages += 1;
7514
+ }
7515
+ }
7243
7516
  function buildAggregate(files, chat) {
7244
7517
  const sessionProject = {};
7245
7518
  const all = [];
@@ -7274,16 +7547,26 @@ function scanCostTree(root, cache, clock, price, resolveProject) {
7274
7547
  return cache;
7275
7548
  const chat = cache.aggregate.chat;
7276
7549
  const files = {};
7550
+ let changed = false;
7277
7551
  for (const { path, encodedDir } of walkJsonl(root)) {
7278
7552
  const st = statSafe(path);
7279
7553
  if (!st)
7280
7554
  continue;
7281
7555
  const cached = cache.files[path];
7282
- files[path] = cached && cached.mtime === st.mtimeMs && cached.size === st.size ? cached : priceFile(path, encodedDir, st, price, resolveProject);
7556
+ if (cached && cached.mtime === st.mtimeMs && cached.size === st.size) {
7557
+ files[path] = cached;
7558
+ } else {
7559
+ const resumed = cached ? resumeCostFile(cached, path, st, price) : null;
7560
+ files[path] = resumed ?? priceFile(path, encodedDir, st, price, resolveProject);
7561
+ changed = true;
7562
+ }
7283
7563
  }
7564
+ const sameFileSet = Object.keys(files).length === Object.keys(cache.files).length;
7565
+ if (!changed && sameFileSet)
7566
+ return { files, aggregate: cache.aggregate, lastScanTs: now };
7284
7567
  return { files, aggregate: buildAggregate(files, chat), lastScanTs: now };
7285
7568
  }
7286
- var asObj = (v) => v !== null && typeof v === "object" ? v : undefined, asStr = (v) => typeof v === "string" ? v : undefined, asNum = (v) => typeof v === "number" && Number.isFinite(v) ? v : 0, asArr = (v) => Array.isArray(v) ? v : [], EMPTY_TOKENS, repoRootCache;
7569
+ var asObj = (v) => v !== null && typeof v === "object" ? v : undefined, asStr = (v) => typeof v === "string" ? v : undefined, asNum = (v) => typeof v === "number" && Number.isFinite(v) ? v : 0, asArr = (v) => Array.isArray(v) ? v : [], HEAD_HASH_BYTES = 4096, headLen = (byteOffset) => Math.min(HEAD_HASH_BYTES, byteOffset), EMPTY_TOKENS, repoRootCache;
7287
7570
  var init_transcript = __esm(() => {
7288
7571
  init_domain();
7289
7572
  EMPTY_TOKENS = {
@@ -7317,15 +7600,31 @@ var init_sources = __esm(() => {
7317
7600
  });
7318
7601
 
7319
7602
  // src/cli/gc.ts
7320
- import { existsSync as existsSync2, readdirSync as readdirSync3, rmSync, statSync as statSync4 } from "node:fs";
7603
+ import {
7604
+ existsSync as existsSync2,
7605
+ mkdirSync as mkdirSync4,
7606
+ readFileSync as readFileSync9,
7607
+ readdirSync as readdirSync3,
7608
+ rmSync,
7609
+ statSync as statSync5,
7610
+ writeFileSync as writeFileSync3
7611
+ } from "node:fs";
7321
7612
  import { join as join13 } from "node:path";
7613
+ function readGcStamp(path) {
7614
+ try {
7615
+ const n = Number(readFileSync9(path, "utf8"));
7616
+ return Number.isFinite(n) ? n : undefined;
7617
+ } catch {
7618
+ return;
7619
+ }
7620
+ }
7322
7621
  function newestMtime(dir) {
7323
7622
  let newest = 0;
7324
7623
  try {
7325
- newest = statSync4(dir).mtimeMs;
7624
+ newest = statSync5(dir).mtimeMs;
7326
7625
  for (const name of readdirSync3(dir)) {
7327
7626
  try {
7328
- const m = statSync4(join13(dir, name)).mtimeMs;
7627
+ const m = statSync5(join13(dir, name)).mtimeMs;
7329
7628
  if (m > newest)
7330
7629
  newest = m;
7331
7630
  } catch {}
@@ -7345,7 +7644,7 @@ function pruneSessions(root, now) {
7345
7644
  for (const name of names) {
7346
7645
  const dir = join13(sessions, name);
7347
7646
  try {
7348
- if (!statSync4(dir).isDirectory())
7647
+ if (!statSync5(dir).isDirectory())
7349
7648
  continue;
7350
7649
  } catch {
7351
7650
  continue;
@@ -7358,36 +7657,62 @@ function pruneSessions(root, now) {
7358
7657
  function pruneCostCache(root) {
7359
7658
  const cache = readCostCache(root);
7360
7659
  const kept = {};
7660
+ const liveSessions = new Set;
7361
7661
  let dropped = false;
7362
7662
  for (const [path, entry] of Object.entries(cache.files)) {
7363
- if (existsSync2(path))
7663
+ if (existsSync2(path)) {
7364
7664
  kept[path] = entry;
7365
- else
7665
+ liveSessions.add(String(entry.record.session));
7666
+ } else {
7366
7667
  dropped = true;
7668
+ }
7669
+ }
7670
+ const chatKept = {};
7671
+ let chatDropped = false;
7672
+ for (const [session, cost] of Object.entries(cache.aggregate.chat)) {
7673
+ if (liveSessions.has(session))
7674
+ chatKept[session] = cost;
7675
+ else
7676
+ chatDropped = true;
7677
+ }
7678
+ if (dropped || chatDropped) {
7679
+ writeCostCache(root, {
7680
+ ...cache,
7681
+ files: kept,
7682
+ aggregate: { ...cache.aggregate, chat: chatKept }
7683
+ });
7367
7684
  }
7368
- if (dropped)
7369
- writeCostCache(root, { ...cache, files: kept });
7370
7685
  }
7371
7686
  function runGc(root, clock) {
7687
+ const now = clock.now();
7688
+ const stampPath = join13(root, "cache", ".gc-stamp");
7689
+ const last = readGcStamp(stampPath);
7690
+ if (last !== undefined && now - last < GC_MIN_INTERVAL_MS)
7691
+ return;
7372
7692
  try {
7373
- pruneSessions(root, clock.now());
7693
+ pruneSessions(root, now);
7374
7694
  } catch {}
7375
7695
  try {
7376
7696
  pruneCostCache(root);
7377
7697
  } catch {}
7698
+ try {
7699
+ mkdirSync4(join13(root, "cache"), { recursive: true });
7700
+ writeFileSync3(stampPath, String(now));
7701
+ } catch {}
7378
7702
  }
7379
- var DAY_MS = 86400000;
7703
+ var DAY_MS = 86400000, GC_MIN_INTERVAL_MS;
7380
7704
  var init_gc = __esm(() => {
7381
7705
  init_domain();
7382
7706
  init_sources();
7707
+ GC_MIN_INTERVAL_MS = 6 * 60 * 60 * 1000;
7383
7708
  });
7384
7709
 
7385
7710
  // src/cli/render.ts
7386
- import { readFileSync as readFileSync9 } from "node:fs";
7711
+ import { readFileSync as readFileSync10 } from "node:fs";
7387
7712
  import { dirname as dirname6, join as join14 } from "node:path";
7388
7713
  function readTextSafe(path) {
7389
7714
  try {
7390
- return readFileSync9(path, "utf8");
7715
+ return readFileSync10(path, "utf8");
7391
7716
  } catch {
7392
7717
  return "";
7393
7718
  }
@@ -7422,18 +7747,28 @@ function applyThemeIcons(fields, theme, config) {
7422
7747
  });
7423
7748
  }
7424
7749
  function readGated(overrides, envInputs, config, root, dir, markers, clock) {
7425
- const creds = overrides?.creds !== undefined ? overrides.creds : envInputs.hasApiKey || config.network.usage_fetch ? readCreds() : null;
7750
+ const creds = overrides?.creds !== undefined ? overrides.creds : envInputs.hasApiKey || config.network.usage_fetch ? readCredsCached(root) : null;
7426
7751
  const usage = overrides?.usage !== undefined ? overrides.usage : config.network.usage_fetch ? readUsageCached(root) : null;
7427
7752
  const balance = overrides?.balance !== undefined ? overrides.balance : config.network.balance_path !== "" ? readBalance(config.network.balance_path, clock) : null;
7428
7753
  const helpfulEnv = needsHelpfulEnv(markers) ? readHelpfulEnv(dir) : EMPTY_HELPFUL_ENV;
7429
7754
  return { creds, usage, balance, helpfulEnv };
7430
7755
  }
7431
- function lookupPackTheme(name) {
7432
- const loaded = loadPack(name);
7756
+ function packThemeOf(loaded) {
7433
7757
  if (!loaded.ok || loaded.pack.theme === undefined)
7434
7758
  return null;
7435
7759
  return { displayName: loaded.pack.displayName, ...loaded.pack.theme };
7436
7760
  }
7761
+ function makePackThemeLookup(persona, loaded, load = loadPack) {
7762
+ const memo = new Map;
7763
+ return (name) => {
7764
+ const cached = memo.get(name);
7765
+ if (cached !== undefined || memo.has(name))
7766
+ return cached ?? null;
7767
+ const theme = packThemeOf(name === persona ? loaded : load(name));
7768
+ memo.set(name, theme);
7769
+ return theme;
7770
+ };
7771
+ }
7437
7772
  function runRender(stdin, env, term, clock, overrides) {
7438
7773
  try {
7439
7774
  return build(stdin, env, term, clock, overrides);
@@ -7495,8 +7830,8 @@ function build(stdin, env, term, clock, overrides) {
7495
7830
  const stacks = deriveStacks(markers, events);
7496
7831
  const fresh = freshestEvent(events);
7497
7832
  const stack = pickStack(stacks, fresh?.stack);
7498
- const familiarity = deriveFamiliarity(attribution, persona, scannedCache, project, clock);
7499
- const theme = resolveTheme(config, lookupPackTheme, persona);
7833
+ const familiarity = deriveFamiliarity(attribution, persona, scannedCache, project, clock, String(session));
7834
+ const theme = resolveTheme(config, makePackThemeLookup(persona, loaded), persona);
7500
7835
  const composeInputs = {
7501
7836
  provider,
7502
7837
  model,
@@ -7597,6 +7932,10 @@ function build(stdin, env, term, clock, overrides) {
7597
7932
  }
7598
7933
  swallow(() => {
7599
7934
  const payloadChat = payload.cost?.total_cost_usd;
7935
+ const rescanned = scannedCache !== costCache;
7936
+ const chatChanged = !isDefault && payloadChat !== undefined && scannedCache.aggregate.chat[String(session)] !== payloadChat;
7937
+ if (!rescanned && !chatChanged)
7938
+ return;
7600
7939
  const toWrite = !isDefault && payloadChat !== undefined ? {
7601
7940
  ...scannedCache,
7602
7941
  aggregate: {
@@ -7620,6 +7959,10 @@ function build(stdin, env, term, clock, overrides) {
7620
7959
  swallow(() => {
7621
7960
  readUsage(root, clock, { enabled: config.network.usage_fetch });
7622
7961
  });
7962
+ swallow(() => {
7963
+ if (envInputs.hasApiKey || config.network.usage_fetch)
7964
+ refreshCreds(root, clock);
7965
+ });
7623
7966
  swallow(() => {
7624
7967
  runGc(root, clock);
7625
7968
  });
@@ -7646,7 +7989,7 @@ var init_render2 = __esm(() => {
7646
7989
  });
7647
7990
 
7648
7991
  // src/cli/settings.ts
7649
- import { existsSync as existsSync3, readFileSync as readFileSync10, readdirSync as readdirSync4, renameSync as renameSync2, rmSync as rmSync2, writeFileSync as writeFileSync3 } from "node:fs";
7992
+ import { existsSync as existsSync3, readFileSync as readFileSync11, readdirSync as readdirSync4, renameSync as renameSync3, rmSync as rmSync2, writeFileSync as writeFileSync4 } from "node:fs";
7650
7993
  import { basename as basename3, dirname as dirname7, join as join15 } from "node:path";
7651
7994
  function asRecord(v) {
7652
7995
  return typeof v === "object" && v !== null && !Array.isArray(v) ? v : {};
@@ -7677,16 +8020,16 @@ function pruneBackups(settingsPath) {
7677
8020
  }
7678
8021
  function safeWriteJson(path, text, restoreText) {
7679
8022
  const tmp = `${path}.tmp.${String(process.pid)}.${Math.random().toString(36).slice(2)}`;
7680
- writeFileSync3(tmp, text);
7681
- renameSync2(tmp, path);
8023
+ writeFileSync4(tmp, text);
8024
+ renameSync3(tmp, path);
7682
8025
  let ok = false;
7683
8026
  try {
7684
- ok = parseJsonObject(readFileSync10(path, "utf8")) !== null;
8027
+ ok = parseJsonObject(readFileSync11(path, "utf8")) !== null;
7685
8028
  } catch {
7686
8029
  ok = false;
7687
8030
  }
7688
8031
  if (!ok) {
7689
- writeFileSync3(path, restoreText);
8032
+ writeFileSync4(path, restoreText);
7690
8033
  throw new Error(`ccsidekick: settings write failed verification at ${path}; restored prior content`);
7691
8034
  }
7692
8035
  }
@@ -7696,7 +8039,7 @@ function installSettings(opts) {
7696
8039
  let base = {};
7697
8040
  let originalText = "";
7698
8041
  if (exists) {
7699
- originalText = readFileSync10(settingsPath, "utf8");
8042
+ originalText = readFileSync11(settingsPath, "utf8");
7700
8043
  const parsed = parseJsonObject(originalText);
7701
8044
  if (parsed === null) {
7702
8045
  throw new Error(`ccsidekick: refusing to modify unparseable settings at ${settingsPath}`);
@@ -7721,7 +8064,7 @@ function installSettings(opts) {
7721
8064
  spinnerVerbs: { mode: "replace", verbs: [...spinnerVerbs] }
7722
8065
  };
7723
8066
  if (exists) {
7724
- writeFileSync3(`${settingsPath}.ccsidekick-bak.${String(Date.now())}`, originalText);
8067
+ writeFileSync4(`${settingsPath}.ccsidekick-bak.${String(Date.now())}`, originalText);
7725
8068
  pruneBackups(settingsPath);
7726
8069
  }
7727
8070
  safeWriteJson(settingsPath, `${JSON.stringify(merged, null, 2)}
@@ -7735,11 +8078,11 @@ var init_settings = __esm(() => {
7735
8078
  init_dist();
7736
8079
  init_domain();
7737
8080
  init_sources();
7738
- HOOK_EVENTS = ["PostToolUse", "PostToolUseFailure", "PostToolBatch"];
8081
+ HOOK_EVENTS = ["PostToolUse", "PostToolUseFailure"];
7739
8082
  });
7740
8083
 
7741
8084
  // src/cli/setup.ts
7742
- import { readFileSync as readFileSync11 } from "node:fs";
8085
+ import { readFileSync as readFileSync12 } from "node:fs";
7743
8086
  import { join as join16 } from "node:path";
7744
8087
  function themeNames() {
7745
8088
  return [CHARACTER_THEME, ...Object.keys(THEMES), ...PACKS];
@@ -7891,14 +8234,14 @@ function setupHelp() {
7891
8234
  "",
7892
8235
  "Flags (only the ones you pass are applied):",
7893
8236
  ` --character <name> ${PACKS.join(", ")}`,
7894
- ` --mode <mode> ${MODES.join(", ")}`,
8237
+ ` --mode <mode> ${CHARACTER_MODES.join(", ")}`,
7895
8238
  " --roster <a,b,c> characters for random mode (from --character's list)",
7896
8239
  ` --theme <name> ${themeNames().join(", ")}`,
7897
8240
  " --currency <code> e.g. USD, EUR, INR",
7898
8241
  " --budget <usd> monthly budget, a number >= 0",
7899
8242
  " --comments <on|off> the character's own comment line",
7900
8243
  " --helpful <on|off> the helpful-tip line",
7901
- ` --min-severity <sev> ${SEVERITIES.join(", ")}`,
8244
+ ` --min-severity <sev> ${MIN_SEVERITIES.join(", ")}`,
7902
8245
  " --widgets <a,b,c> statusline widgets to enable (others are turned off)",
7903
8246
  " --usage-fetch <on|off> account-usage lookup to Anthropic (needed by the pay_as_you_go widget; no tokens)",
7904
8247
  " --global | --local save target (default: global)",
@@ -7911,12 +8254,12 @@ function setupHelp() {
7911
8254
  }
7912
8255
  function defaultReadConfig(path) {
7913
8256
  try {
7914
- return readFileSync11(path, "utf8");
8257
+ return readFileSync12(path, "utf8");
7915
8258
  } catch {
7916
8259
  return null;
7917
8260
  }
7918
8261
  }
7919
- var WIDGET_IDS, MODES, SEVERITIES, VALUE_FLAGS, BOOL_FLAGS, splitList = (raw) => raw.split(",").map((s) => s.trim()).filter((s) => s !== ""), invalid = (flag, value, valid) => `ccsidekick: invalid --${flag} "${value}". Valid: ${valid.join(", ")}`, parseOnOff = (flag, value, errors) => {
8262
+ var WIDGET_IDS, VALUE_FLAGS, BOOL_FLAGS, splitList = (raw) => raw.split(",").map((s) => s.trim()).filter((s) => s !== ""), invalid = (flag, value, valid) => `ccsidekick: invalid --${flag} "${value}". Valid: ${valid.join(", ")}`, parseOnOff = (flag, value, errors) => {
7920
8263
  if (value === "on")
7921
8264
  return true;
7922
8265
  if (value === "off")
@@ -7926,12 +8269,11 @@ var WIDGET_IDS, MODES, SEVERITIES, VALUE_FLAGS, BOOL_FLAGS, splitList = (raw) =>
7926
8269
  }, memberOf = (value, valid) => valid.includes(value), VALIDATORS;
7927
8270
  var init_setup = __esm(() => {
7928
8271
  init_data();
8272
+ init_domain();
7929
8273
  init_packs();
7930
8274
  init_render();
7931
8275
  init_sources();
7932
8276
  WIDGET_IDS = Object.keys(DEFAULT_CONFIG.statusline.widgets);
7933
- MODES = ["fixed", "random"];
7934
- SEVERITIES = ["low", "medium", "high", "critical"];
7935
8277
  VALUE_FLAGS = new Set([
7936
8278
  "character",
7937
8279
  "mode",
@@ -7952,7 +8294,7 @@ var init_setup = __esm(() => {
7952
8294
  enumInto(v, PACKS, "character", e, (x) => f.character = x);
7953
8295
  },
7954
8296
  mode: (v, f, e) => {
7955
- enumInto(v, MODES, "mode", e, (x) => f.mode = x);
8297
+ enumInto(v, CHARACTER_MODES, "mode", e, (x) => f.mode = x);
7956
8298
  },
7957
8299
  roster: (v, f, e) => {
7958
8300
  listInto(v, PACKS, "roster", e, (x) => f.roster = x);
@@ -7985,7 +8327,7 @@ var init_setup = __esm(() => {
7985
8327
  f.helpful = b;
7986
8328
  },
7987
8329
  "min-severity": (v, f, e) => {
7988
- enumInto(v, SEVERITIES, "min-severity", e, (x) => f.minSeverity = x);
8330
+ enumInto(v, MIN_SEVERITIES, "min-severity", e, (x) => f.minSeverity = x);
7989
8331
  },
7990
8332
  widgets: (v, f, e) => {
7991
8333
  listInto(v, WIDGET_IDS, "widgets", e, (x) => f.widgets = x);
@@ -7999,7 +8341,7 @@ var init_setup = __esm(() => {
7999
8341
  });
8000
8342
 
8001
8343
  // src/cli/uninstall.ts
8002
- import { existsSync as existsSync4, readFileSync as readFileSync12, readdirSync as readdirSync5 } from "node:fs";
8344
+ import { existsSync as existsSync4, readFileSync as readFileSync13, readdirSync as readdirSync5 } from "node:fs";
8003
8345
  import { basename as basename4, dirname as dirname8, join as join17 } from "node:path";
8004
8346
  function asRecord2(v) {
8005
8347
  return typeof v === "object" && v !== null && !Array.isArray(v) ? v : {};
@@ -8046,7 +8388,7 @@ function restoreNewestBackup(settingsPath) {
8046
8388
  }
8047
8389
  if (newest === undefined)
8048
8390
  return null;
8049
- atomicWrite(settingsPath, readFileSync12(join17(dir, newest.name), "utf8"));
8391
+ atomicWrite(settingsPath, readFileSync13(join17(dir, newest.name), "utf8"));
8050
8392
  return newest.name;
8051
8393
  }
8052
8394
  function runRestore(settingsPath, emit) {
@@ -8100,7 +8442,7 @@ function runUninstall(opts) {
8100
8442
  return;
8101
8443
  }
8102
8444
  if (existsSync4(settingsPath)) {
8103
- const parsed = parseJsonObject2(readFileSync12(settingsPath, "utf8"));
8445
+ const parsed = parseJsonObject2(readFileSync13(settingsPath, "utf8"));
8104
8446
  if (parsed === null) {
8105
8447
  throw new Error(`ccsidekick: refusing to modify unparseable settings at ${settingsPath}`);
8106
8448
  }
@@ -8182,7 +8524,7 @@ var init_metrics = __esm(() => {
8182
8524
  var ACCENT = "#ffd700", FRAME = "gray", DANGER = "red";
8183
8525
 
8184
8526
  // src/tui/save.ts
8185
- import { mkdirSync as mkdirSync4 } from "node:fs";
8527
+ import { mkdirSync as mkdirSync5 } from "node:fs";
8186
8528
  import { join as join19 } from "node:path";
8187
8529
  function defaultResolveVerbs(name) {
8188
8530
  const loaded = loadPack(name);
@@ -8219,7 +8561,7 @@ function save(config, scope, dir, renderBin, opts = {}) {
8219
8561
  writeConfigToml(join19(cwd, ".ccsidekick"), normalized);
8220
8562
  if (opts.wireLocalSettings === true) {
8221
8563
  const claudeDir = join19(cwd, ".claude");
8222
- mkdirSync4(claudeDir, { recursive: true });
8564
+ mkdirSync5(claudeDir, { recursive: true });
8223
8565
  installSettings({
8224
8566
  settingsPath: join19(claudeDir, "settings.json"),
8225
8567
  renderBin,
@@ -10917,14 +11259,14 @@ var init_PreviewPanel = __esm(() => {
10917
11259
 
10918
11260
  // src/tui/preview/fixture.ts
10919
11261
  import { execFileSync } from "node:child_process";
10920
- import { existsSync as existsSync6, mkdirSync as mkdirSync5, mkdtempSync, realpathSync, writeFileSync as writeFileSync4 } from "node:fs";
11262
+ import { existsSync as existsSync6, mkdirSync as mkdirSync6, mkdtempSync, realpathSync, writeFileSync as writeFileSync5 } from "node:fs";
10921
11263
  import { tmpdir } from "node:os";
10922
11264
  import { join as join20 } from "node:path";
10923
11265
  function seedTranscriptFixture(workdir, root) {
10924
11266
  const dir = root !== undefined ? join20(root, "projects", workdir.replace(/[/.]/g, "-")) : workdir;
10925
11267
  const path = join20(dir, "preview-transcript.jsonl");
10926
11268
  try {
10927
- mkdirSync5(dir, { recursive: true });
11269
+ mkdirSync6(dir, { recursive: true });
10928
11270
  if (!existsSync6(path)) {
10929
11271
  const lines = [
10930
11272
  JSON.stringify({ type: "system", subtype: "compact_boundary" }),
@@ -10948,7 +11290,7 @@ function seedTranscriptFixture(workdir, root) {
10948
11290
  }
10949
11291
  })
10950
11292
  ];
10951
- writeFileSync4(path, `${lines.join(`
11293
+ writeFileSync5(path, `${lines.join(`
10952
11294
  `)}
10953
11295
  `);
10954
11296
  }
@@ -10992,7 +11334,7 @@ function gitEnv() {
10992
11334
  }
10993
11335
  function setupGitFixture(workdir) {
10994
11336
  try {
10995
- mkdirSync5(workdir, { recursive: true });
11337
+ mkdirSync6(workdir, { recursive: true });
10996
11338
  if (existsSync6(join20(workdir, ".git")))
10997
11339
  return;
10998
11340
  const env2 = gitEnv();
@@ -11003,20 +11345,20 @@ function setupGitFixture(workdir) {
11003
11345
  git("config", "user.email", "wayne@example.com");
11004
11346
  git("config", "user.name", "Bruce Wayne");
11005
11347
  git("remote", "add", "origin", "https://github.com/wayne/ccsidekick.git");
11006
- writeFileSync4(join20(workdir, "README.md"), `# ccsidekick
11348
+ writeFileSync5(join20(workdir, "README.md"), `# ccsidekick
11007
11349
 
11008
11350
  original line
11009
11351
  keep me
11010
11352
  `);
11011
11353
  git("add", "README.md");
11012
11354
  git("commit", "-q", "-m", "initial commit");
11013
- writeFileSync4(join20(workdir, "README.md"), `# ccsidekick
11355
+ writeFileSync5(join20(workdir, "README.md"), `# ccsidekick
11014
11356
 
11015
11357
  edited line
11016
11358
  keep me
11017
11359
  new line
11018
11360
  `);
11019
- writeFileSync4(join20(workdir, "src.ts"), `export const answer = 42;
11361
+ writeFileSync5(join20(workdir, "src.ts"), `export const answer = 42;
11020
11362
  `);
11021
11363
  git("add", "src.ts");
11022
11364
  } catch {}
@@ -11041,11 +11383,11 @@ function seedCostFixture(root, workdir) {
11041
11383
  const projectsRoot = join20(root, "projects");
11042
11384
  const currentProjectDir = join20(projectsRoot, workdir.replace(/[/.]/g, "-"));
11043
11385
  const otherProjectDir = join20(projectsRoot, "-other-project");
11044
- mkdirSync5(currentProjectDir, { recursive: true });
11045
- mkdirSync5(otherProjectDir, { recursive: true });
11046
- writeFileSync4(join20(currentProjectDir, "sess-a.jsonl"), `${transcriptLine("sess-a-1", "claude-opus-4-8", 1e5, 20000)}
11386
+ mkdirSync6(currentProjectDir, { recursive: true });
11387
+ mkdirSync6(otherProjectDir, { recursive: true });
11388
+ writeFileSync5(join20(currentProjectDir, "sess-a.jsonl"), `${transcriptLine("sess-a-1", "claude-opus-4-8", 1e5, 20000)}
11047
11389
  `);
11048
- writeFileSync4(join20(otherProjectDir, "sess-b.jsonl"), `${transcriptLine("sess-b-1", "claude-opus-4-8", 200000, 50000)}
11390
+ writeFileSync5(join20(otherProjectDir, "sess-b.jsonl"), `${transcriptLine("sess-b-1", "claude-opus-4-8", 200000, 50000)}
11049
11391
  `);
11050
11392
  }
11051
11393
  function scratchRoot(scratchDir) {
@@ -11056,7 +11398,7 @@ function scratchRoot(scratchDir) {
11056
11398
  }
11057
11399
  function previewEnv(root, extra) {
11058
11400
  const homeBase = join20(root, "home");
11059
- mkdirSync5(homeBase, { recursive: true });
11401
+ mkdirSync6(homeBase, { recursive: true });
11060
11402
  let home = homeBase;
11061
11403
  try {
11062
11404
  home = realpathSync(homeBase);
@@ -11265,8 +11607,8 @@ function checkedTargets(targets, checked) {
11265
11607
  }
11266
11608
 
11267
11609
  // src/tui/sections/configdir.tsx
11268
- import { existsSync as existsSync7, mkdirSync as mkdirSync6 } from "node:fs";
11269
- import { homedir as homedir5 } from "node:os";
11610
+ import { existsSync as existsSync7, mkdirSync as mkdirSync7 } from "node:fs";
11611
+ import { homedir as homedir4 } from "node:os";
11270
11612
  import { Box as Box22, Text as Text27, useApp, useInput as useInput7 } from "ink";
11271
11613
  import { useState as useState6 } from "react";
11272
11614
  import { jsxDEV as jsxDEV14 } from "react/jsx-dev-runtime";
@@ -11310,7 +11652,7 @@ function handleCustomKey(input, key, d) {
11310
11652
  function handleConfirmKey(key, d) {
11311
11653
  if (key.return && d.pending !== null) {
11312
11654
  try {
11313
- mkdirSync6(d.pending, { recursive: true });
11655
+ mkdirSync7(d.pending, { recursive: true });
11314
11656
  d.addDir(d.pending);
11315
11657
  } catch {
11316
11658
  d.setError(`Could not create ${d.pending}`);
@@ -11377,7 +11719,7 @@ function ConfigDirPicker({
11377
11719
  onQuit
11378
11720
  }) {
11379
11721
  const { exit } = useApp();
11380
- const home = homedir5();
11722
+ const home = homedir4();
11381
11723
  const [cursor4, setCursor] = useState6(0);
11382
11724
  const [mode, setMode] = useState6("list");
11383
11725
  const [text, setText] = useState6("");
@@ -11640,7 +11982,10 @@ function commentsFields(d) {
11640
11982
  value: d.comments.min_severity,
11641
11983
  next: (c) => ({
11642
11984
  ...c,
11643
- comments: { ...c.comments, min_severity: step(SEVERITIES2, c.comments.min_severity, 1) }
11985
+ comments: {
11986
+ ...c.comments,
11987
+ min_severity: step(MIN_SEVERITIES, c.comments.min_severity, 1)
11988
+ }
11644
11989
  })
11645
11990
  };
11646
11991
  return d.comments.helpful ? [characterComments, helpfulComments, minSeverity] : [characterComments, helpfulComments];
@@ -11688,16 +12033,15 @@ function sectionFields(section2, d) {
11688
12033
  return [];
11689
12034
  }
11690
12035
  }
11691
- var onOff = (b) => b ? "on" : "off", WIDGET_IDS2, SEVERITIES2, BANDINGS, step = (arr, cur, dir) => {
12036
+ var onOff = (b) => b ? "on" : "off", WIDGET_IDS2, step = (arr, cur, dir) => {
11692
12037
  const n = arr.length;
11693
12038
  const i = arr.indexOf(cur);
11694
12039
  return arr[((i + dir) % n + n) % n] ?? cur;
11695
12040
  };
11696
12041
  var init_forms = __esm(() => {
12042
+ init_domain();
11697
12043
  init_sources();
11698
12044
  WIDGET_IDS2 = Object.keys(DEFAULT_CONFIG.statusline.widgets);
11699
- SEVERITIES2 = ["low", "medium", "high", "critical"];
11700
- BANDINGS = ["solid", "cycle"];
11701
12045
  });
11702
12046
 
11703
12047
  // src/tui/sections/FormSection.tsx
@@ -12838,7 +13182,7 @@ var init_inputRoute = __esm(() => {
12838
13182
  });
12839
13183
 
12840
13184
  // src/tui/shell/saveTarget.ts
12841
- import { existsSync as existsSync8, readFileSync as readFileSync13 } from "node:fs";
13185
+ import { existsSync as existsSync8, readFileSync as readFileSync14 } from "node:fs";
12842
13186
  import { join as join22 } from "node:path";
12843
13187
  function chipFor(targets) {
12844
13188
  if (targets.every((t) => t.scope === "global"))
@@ -12851,7 +13195,7 @@ function alreadyWired(settingsPath) {
12851
13195
  if (!existsSync8(settingsPath))
12852
13196
  return false;
12853
13197
  try {
12854
- const parsed = JSON.parse(readFileSync13(settingsPath, "utf8"));
13198
+ const parsed = JSON.parse(readFileSync14(settingsPath, "utf8"));
12855
13199
  return isOurStatusLine(parsed.statusLine);
12856
13200
  } catch {
12857
13201
  return false;
@@ -13105,7 +13449,7 @@ var init_useMouseWheel = __esm(() => {
13105
13449
  });
13106
13450
 
13107
13451
  // src/tui/shell/Dashboard.tsx
13108
- import { readFileSync as readFileSync14 } from "node:fs";
13452
+ import { readFileSync as readFileSync15 } from "node:fs";
13109
13453
  import { join as join23 } from "node:path";
13110
13454
  import { Box as Box30, Text as Text35, useApp as useApp2, useInput as useInput8 } from "ink";
13111
13455
  import { useCallback as useCallback6, useMemo as useMemo9, useState as useState8 } from "react";
@@ -13904,7 +14248,7 @@ var seed = (configDir2, initial) => {
13904
14248
  if (initial)
13905
14249
  return initial;
13906
14250
  try {
13907
- return loadConfig(readFileSync14(join23(configDir2, "ccsidekick", "config.toml"), "utf8"));
14251
+ return loadConfig(readFileSync15(join23(configDir2, "ccsidekick", "config.toml"), "utf8"));
13908
14252
  } catch {
13909
14253
  return loadConfig("");
13910
14254
  }
@@ -14547,8 +14891,8 @@ var init_Wizard = __esm(() => {
14547
14891
  });
14548
14892
 
14549
14893
  // src/tui/shell/App.tsx
14550
- import { existsSync as existsSync9, readFileSync as readFileSync15 } from "node:fs";
14551
- import { homedir as homedir6 } from "node:os";
14894
+ import { existsSync as existsSync9, readFileSync as readFileSync16 } from "node:fs";
14895
+ import { homedir as homedir5 } from "node:os";
14552
14896
  import { join as join24 } from "node:path";
14553
14897
  import { useInput as useInput10 } from "ink";
14554
14898
  import { useMemo as useMemo10, useState as useState10 } from "react";
@@ -14562,7 +14906,7 @@ function welcomeTheme(suggestedDir) {
14562
14906
  if (suggestedDir === undefined || suggestedDir === "")
14563
14907
  return THEMES.houston;
14564
14908
  try {
14565
- const text = readFileSync15(join24(suggestedDir, "ccsidekick", "config.toml"), "utf8");
14909
+ const text = readFileSync16(join24(suggestedDir, "ccsidekick", "config.toml"), "utf8");
14566
14910
  return pickTheme(loadConfig(text).theme.name);
14567
14911
  } catch {
14568
14912
  return THEMES.houston;
@@ -14611,7 +14955,7 @@ function App(props) {
14611
14955
  const rows = props.rows ?? live.rows;
14612
14956
  const atFloor = breakpointFor(columns, rows) === "floor";
14613
14957
  useInput10(() => {});
14614
- const home = props.homeDir ?? homedir6();
14958
+ const home = props.homeDir ?? homedir5();
14615
14959
  const cwd = props.cwd ?? process.cwd();
14616
14960
  const suggested = props.suggestedDir ?? env2["CLAUDE_CONFIG_DIR"];
14617
14961
  const wordmarkHues = useMemo10(() => welcomeTheme(suggested).hues, [suggested]);
@@ -14722,12 +15066,12 @@ var init_shell = __esm(() => {
14722
15066
  init_cli();
14723
15067
  init_domain();
14724
15068
  init_tui();
14725
- import { homedir as homedir7 } from "node:os";
15069
+ import { homedir as homedir6 } from "node:os";
14726
15070
  import { join as join25 } from "node:path";
14727
15071
  import { fileURLToPath as fileURLToPath2 } from "node:url";
14728
15072
  var sub = process.argv[2];
14729
15073
  function claudeDir() {
14730
- return process.env["CLAUDE_CONFIG_DIR"] ?? join25(homedir7(), ".claude");
15074
+ return process.env["CLAUDE_CONFIG_DIR"] ?? join25(homedir6(), ".claude");
14731
15075
  }
14732
15076
  function printHelp() {
14733
15077
  process.stdout.write([
@@ -14772,7 +15116,7 @@ async function launchTui() {
14772
15116
  const renderBin = fileURLToPath2(new URL("ccsidekick-render.js", import.meta.url));
14773
15117
  const suggested = process.env["CLAUDE_CONFIG_DIR"];
14774
15118
  const instance = render(createElement(App2, {
14775
- homeDir: homedir7(),
15119
+ homeDir: homedir6(),
14776
15120
  renderBin,
14777
15121
  ...suggested !== undefined ? { suggestedDir: suggested } : {}
14778
15122
  }));
@@ -14805,7 +15149,7 @@ async function main() {
14805
15149
  renderBin,
14806
15150
  readConfig: defaultReadConfig,
14807
15151
  cwd: process.cwd(),
14808
- homeDir: homedir7(),
15152
+ homeDir: homedir6(),
14809
15153
  env: process.env,
14810
15154
  out: (s) => process.stdout.write(s),
14811
15155
  err: (s) => process.stderr.write(s)