ccsidekick 1.6.0 → 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.
- package/dist/ccsidekick-render.js +428 -110
- package/dist/ccsidekick.js +490 -139
- package/package.json +1 -1
package/dist/ccsidekick.js
CHANGED
|
@@ -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 =
|
|
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
|
|
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();
|
|
@@ -2149,7 +2153,7 @@ var init_catalog = __esm(() => {
|
|
|
2149
2153
|
},
|
|
2150
2154
|
{
|
|
2151
2155
|
id: "pay_as_you_go_active",
|
|
2152
|
-
severity: "
|
|
2156
|
+
severity: "low",
|
|
2153
2157
|
category: "billing",
|
|
2154
2158
|
momentary: false,
|
|
2155
2159
|
template: "Pay-as-you-go is on. Every request now bills on top of your plan.",
|
|
@@ -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.
|
|
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.
|
|
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
|
{
|
|
@@ -2206,9 +2210,9 @@ var init_catalog = __esm(() => {
|
|
|
2206
2210
|
severity: "high",
|
|
2207
2211
|
category: "context",
|
|
2208
2212
|
momentary: false,
|
|
2209
|
-
template: "{pct}% full with uncommitted work. Commit
|
|
2213
|
+
template: "{pct}% full with uncommitted work. Commit now while the context is fresh.",
|
|
2210
2214
|
test: (i) => i.context.usedPct > COMPACT_SOON_PCT && dirty(i.git),
|
|
2211
|
-
render: (i) => `${pct(i.context.usedPct)} full with uncommitted work. Commit
|
|
2215
|
+
render: (i) => `${pct(i.context.usedPct)} full with uncommitted work. Commit now while the context is fresh.`
|
|
2212
2216
|
},
|
|
2213
2217
|
{
|
|
2214
2218
|
id: "compaction_thrash",
|
|
@@ -2324,7 +2328,7 @@ var init_catalog = __esm(() => {
|
|
|
2324
2328
|
category: "git",
|
|
2325
2329
|
momentary: false,
|
|
2326
2330
|
template: "No upstream set. `git push -u origin HEAD` to track it.",
|
|
2327
|
-
test: (i) => i.git !== null && i.git.branch !== undefined && !i.git.upstream,
|
|
2331
|
+
test: (i) => i.git !== null && i.git.branch !== undefined && !i.git.upstream && !i.git.remoteBranchExists,
|
|
2328
2332
|
render: () => "No upstream set. `git push -u origin HEAD` to track it."
|
|
2329
2333
|
},
|
|
2330
2334
|
{
|
|
@@ -2401,7 +2405,7 @@ var init_catalog = __esm(() => {
|
|
|
2401
2405
|
},
|
|
2402
2406
|
{
|
|
2403
2407
|
id: "detached_head",
|
|
2404
|
-
severity: "
|
|
2408
|
+
severity: "medium",
|
|
2405
2409
|
category: "git",
|
|
2406
2410
|
momentary: false,
|
|
2407
2411
|
template: "Detached HEAD. Branch before committing or new commits can vanish.",
|
|
@@ -2419,7 +2423,7 @@ var init_catalog = __esm(() => {
|
|
|
2419
2423
|
},
|
|
2420
2424
|
{
|
|
2421
2425
|
id: "effort_low",
|
|
2422
|
-
severity: "
|
|
2426
|
+
severity: "medium",
|
|
2423
2427
|
category: "workflow",
|
|
2424
2428
|
momentary: false,
|
|
2425
2429
|
template: "Effort on low. Hard problems get shallow answers.",
|
|
@@ -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 <=
|
|
2817
|
+
if (tokens <= PRICING_TIER_THRESHOLD || above === undefined)
|
|
2814
2818
|
return tokens * base;
|
|
2815
|
-
return
|
|
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,
|
|
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
|
|
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:
|
|
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
|
|
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 >=
|
|
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
|
-
|
|
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
|
-
|
|
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"]
|
|
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"],
|
|
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"],
|
|
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"],
|
|
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,
|
|
@@ -5485,7 +5531,7 @@ var init_config = __esm(() => {
|
|
|
5485
5531
|
schema_version: 1,
|
|
5486
5532
|
character: { enabled: true, mode: "random", name: "spiderman", roster: [] },
|
|
5487
5533
|
theme: { name: "character", banding: "solid", mood_shift: false, icons: {} },
|
|
5488
|
-
comments: { character: true, helpful: true, min_severity: "
|
|
5534
|
+
comments: { character: true, helpful: true, min_severity: "medium" },
|
|
5489
5535
|
network: { fx_refresh: false, usage_fetch: false, balance_path: "" },
|
|
5490
5536
|
statusline: { currency: defaultCurrency(), widgets: DEFAULT_WIDGETS }
|
|
5491
5537
|
};
|
|
@@ -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
|
-
|
|
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),
|
|
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",
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
6504
|
+
const gitDirRaw = (probe[1] ?? "").trim();
|
|
6415
6505
|
const gitDir = gitDirRaw === "" ? "" : resolve(cwd, gitDirRaw);
|
|
6416
|
-
const workTreeRoot =
|
|
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);
|
|
@@ -6424,6 +6514,12 @@ function readGit(cwd, run = defaultRunner2(cwd)) {
|
|
|
6424
6514
|
} = parseNumstat(run(["diff", "HEAD", "--numstat"]));
|
|
6425
6515
|
const rollup = countSubmodules(workTreeRoot) > 0 ? readSubmoduleRollup(run, workTreeRoot) : null;
|
|
6426
6516
|
const behindDefault = readBehindDefault(run, status.branch, defaultBranch);
|
|
6517
|
+
const remoteBranchExists = status.branch !== undefined && !status.upstream ? run([
|
|
6518
|
+
"rev-parse",
|
|
6519
|
+
"--verify",
|
|
6520
|
+
"--quiet",
|
|
6521
|
+
`refs/remotes/origin/${status.branch}`
|
|
6522
|
+
]).trim() !== "" : false;
|
|
6427
6523
|
return {
|
|
6428
6524
|
...opt2("branch", status.branch),
|
|
6429
6525
|
...opt2("sha", status.sha),
|
|
@@ -6440,6 +6536,7 @@ function readGit(cwd, run = defaultRunner2(cwd)) {
|
|
|
6440
6536
|
submoduleBranches: rollup?.branches ?? [],
|
|
6441
6537
|
upstream: status.upstream,
|
|
6442
6538
|
upstreamGone: status.upstreamGone,
|
|
6539
|
+
remoteBranchExists,
|
|
6443
6540
|
...opt2("originRepo", originRepo),
|
|
6444
6541
|
...opt2("defaultBranch", defaultBranch),
|
|
6445
6542
|
...opt2("root", workTreeRoot === "" ? undefined : workTreeRoot),
|
|
@@ -6467,7 +6564,7 @@ var init_git = __esm(() => {
|
|
|
6467
6564
|
|
|
6468
6565
|
// src/sources/helpfulEnv.ts
|
|
6469
6566
|
import { readFileSync as readFileSync7 } from "node:fs";
|
|
6470
|
-
import { homedir as
|
|
6567
|
+
import { homedir as homedir3 } from "node:os";
|
|
6471
6568
|
import { delimiter, join as join9 } from "node:path";
|
|
6472
6569
|
function kubeconfigPath() {
|
|
6473
6570
|
const env = process.env["KUBECONFIG"];
|
|
@@ -6476,7 +6573,7 @@ function kubeconfigPath() {
|
|
|
6476
6573
|
if (first !== undefined)
|
|
6477
6574
|
return first;
|
|
6478
6575
|
}
|
|
6479
|
-
return join9(
|
|
6576
|
+
return join9(homedir3(), ".kube", "config");
|
|
6480
6577
|
}
|
|
6481
6578
|
function readKubeContext() {
|
|
6482
6579
|
try {
|
|
@@ -6792,7 +6889,7 @@ var init_state = __esm(() => {
|
|
|
6792
6889
|
});
|
|
6793
6890
|
|
|
6794
6891
|
// src/sources/transcript.ts
|
|
6795
|
-
import { readFileSync as readFileSync8, readdirSync as readdirSync2, statSync as
|
|
6892
|
+
import { closeSync as closeSync2, openSync as openSync2, readFileSync as readFileSync8, readSync, readdirSync as readdirSync2, statSync as statSync4 } from "node:fs";
|
|
6796
6893
|
import { basename as basename2, dirname as dirname5, join as join12 } from "node:path";
|
|
6797
6894
|
function parseTs(s) {
|
|
6798
6895
|
if (s === undefined)
|
|
@@ -7014,7 +7111,7 @@ function reconstructTodos(lines) {
|
|
|
7014
7111
|
}
|
|
7015
7112
|
function statSafe(p) {
|
|
7016
7113
|
try {
|
|
7017
|
-
const s =
|
|
7114
|
+
const s = statSync4(p);
|
|
7018
7115
|
return { mtimeMs: s.mtimeMs, size: s.size };
|
|
7019
7116
|
} catch {
|
|
7020
7117
|
return null;
|
|
@@ -7027,6 +7124,61 @@ function readSafe(p) {
|
|
|
7027
7124
|
return "";
|
|
7028
7125
|
}
|
|
7029
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
|
+
}
|
|
7030
7182
|
function emptyScan(mtime, size) {
|
|
7031
7183
|
return { tokens: EMPTY_TOKENS, messages: 0, compactions: 0, todos: [], burn: [], mtime, size };
|
|
7032
7184
|
}
|
|
@@ -7106,7 +7258,7 @@ function repoRootForCwd(cwd) {
|
|
|
7106
7258
|
let result = cwd;
|
|
7107
7259
|
while (dir !== "" && dir !== dirname5(dir)) {
|
|
7108
7260
|
try {
|
|
7109
|
-
if (
|
|
7261
|
+
if (statSync4(join12(dir, ".git")).isDirectory()) {
|
|
7110
7262
|
result = dir;
|
|
7111
7263
|
break;
|
|
7112
7264
|
}
|
|
@@ -7127,7 +7279,7 @@ function* walkTree(dir, encodedDir) {
|
|
|
7127
7279
|
const full = join12(dir, name);
|
|
7128
7280
|
let st;
|
|
7129
7281
|
try {
|
|
7130
|
-
st =
|
|
7282
|
+
st = statSync4(full);
|
|
7131
7283
|
} catch {
|
|
7132
7284
|
continue;
|
|
7133
7285
|
}
|
|
@@ -7166,22 +7318,16 @@ function scanBounds(lines) {
|
|
|
7166
7318
|
return { sessionId, start, end };
|
|
7167
7319
|
}
|
|
7168
7320
|
function priceFile(path, encodedDir, st, price, resolveProject) {
|
|
7169
|
-
const
|
|
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"));
|
|
7170
7325
|
const { sessionId, start, end } = scanBounds(lines);
|
|
7171
7326
|
const priced = collapseStreaming(lines);
|
|
7172
7327
|
const gate = makeDedupGate();
|
|
7173
7328
|
const costLines = [];
|
|
7174
7329
|
const models = [];
|
|
7175
|
-
const
|
|
7176
|
-
const internModel = (model) => {
|
|
7177
|
-
let i = modelIndex.get(model);
|
|
7178
|
-
if (i === undefined) {
|
|
7179
|
-
i = models.length;
|
|
7180
|
-
models.push(model);
|
|
7181
|
-
modelIndex.set(model, i);
|
|
7182
|
-
}
|
|
7183
|
-
return i;
|
|
7184
|
-
};
|
|
7330
|
+
const internModel = makeInternModel(models);
|
|
7185
7331
|
let total = 0;
|
|
7186
7332
|
let input = 0;
|
|
7187
7333
|
let output = 0;
|
|
@@ -7226,6 +7372,8 @@ function priceFile(path, encodedDir, st, price, resolveProject) {
|
|
|
7226
7372
|
return {
|
|
7227
7373
|
mtime: st.mtimeMs,
|
|
7228
7374
|
size: st.size,
|
|
7375
|
+
byteOffset,
|
|
7376
|
+
headHash,
|
|
7229
7377
|
total,
|
|
7230
7378
|
lines: costLines,
|
|
7231
7379
|
models,
|
|
@@ -7233,6 +7381,138 @@ function priceFile(path, encodedDir, st, price, resolveProject) {
|
|
|
7233
7381
|
record
|
|
7234
7382
|
};
|
|
7235
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
|
+
}
|
|
7236
7516
|
function buildAggregate(files, chat) {
|
|
7237
7517
|
const sessionProject = {};
|
|
7238
7518
|
const all = [];
|
|
@@ -7267,16 +7547,26 @@ function scanCostTree(root, cache, clock, price, resolveProject) {
|
|
|
7267
7547
|
return cache;
|
|
7268
7548
|
const chat = cache.aggregate.chat;
|
|
7269
7549
|
const files = {};
|
|
7550
|
+
let changed = false;
|
|
7270
7551
|
for (const { path, encodedDir } of walkJsonl(root)) {
|
|
7271
7552
|
const st = statSafe(path);
|
|
7272
7553
|
if (!st)
|
|
7273
7554
|
continue;
|
|
7274
7555
|
const cached = cache.files[path];
|
|
7275
|
-
|
|
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
|
+
}
|
|
7276
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 };
|
|
7277
7567
|
return { files, aggregate: buildAggregate(files, chat), lastScanTs: now };
|
|
7278
7568
|
}
|
|
7279
|
-
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;
|
|
7280
7570
|
var init_transcript = __esm(() => {
|
|
7281
7571
|
init_domain();
|
|
7282
7572
|
EMPTY_TOKENS = {
|
|
@@ -7310,15 +7600,31 @@ var init_sources = __esm(() => {
|
|
|
7310
7600
|
});
|
|
7311
7601
|
|
|
7312
7602
|
// src/cli/gc.ts
|
|
7313
|
-
import {
|
|
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";
|
|
7314
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
|
+
}
|
|
7315
7621
|
function newestMtime(dir) {
|
|
7316
7622
|
let newest = 0;
|
|
7317
7623
|
try {
|
|
7318
|
-
newest =
|
|
7624
|
+
newest = statSync5(dir).mtimeMs;
|
|
7319
7625
|
for (const name of readdirSync3(dir)) {
|
|
7320
7626
|
try {
|
|
7321
|
-
const m =
|
|
7627
|
+
const m = statSync5(join13(dir, name)).mtimeMs;
|
|
7322
7628
|
if (m > newest)
|
|
7323
7629
|
newest = m;
|
|
7324
7630
|
} catch {}
|
|
@@ -7338,7 +7644,7 @@ function pruneSessions(root, now) {
|
|
|
7338
7644
|
for (const name of names) {
|
|
7339
7645
|
const dir = join13(sessions, name);
|
|
7340
7646
|
try {
|
|
7341
|
-
if (!
|
|
7647
|
+
if (!statSync5(dir).isDirectory())
|
|
7342
7648
|
continue;
|
|
7343
7649
|
} catch {
|
|
7344
7650
|
continue;
|
|
@@ -7351,36 +7657,62 @@ function pruneSessions(root, now) {
|
|
|
7351
7657
|
function pruneCostCache(root) {
|
|
7352
7658
|
const cache = readCostCache(root);
|
|
7353
7659
|
const kept = {};
|
|
7660
|
+
const liveSessions = new Set;
|
|
7354
7661
|
let dropped = false;
|
|
7355
7662
|
for (const [path, entry] of Object.entries(cache.files)) {
|
|
7356
|
-
if (existsSync2(path))
|
|
7663
|
+
if (existsSync2(path)) {
|
|
7357
7664
|
kept[path] = entry;
|
|
7358
|
-
|
|
7665
|
+
liveSessions.add(String(entry.record.session));
|
|
7666
|
+
} else {
|
|
7359
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
|
+
});
|
|
7360
7684
|
}
|
|
7361
|
-
if (dropped)
|
|
7362
|
-
writeCostCache(root, { ...cache, files: kept });
|
|
7363
7685
|
}
|
|
7364
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;
|
|
7365
7692
|
try {
|
|
7366
|
-
pruneSessions(root,
|
|
7693
|
+
pruneSessions(root, now);
|
|
7367
7694
|
} catch {}
|
|
7368
7695
|
try {
|
|
7369
7696
|
pruneCostCache(root);
|
|
7370
7697
|
} catch {}
|
|
7698
|
+
try {
|
|
7699
|
+
mkdirSync4(join13(root, "cache"), { recursive: true });
|
|
7700
|
+
writeFileSync3(stampPath, String(now));
|
|
7701
|
+
} catch {}
|
|
7371
7702
|
}
|
|
7372
|
-
var DAY_MS = 86400000;
|
|
7703
|
+
var DAY_MS = 86400000, GC_MIN_INTERVAL_MS;
|
|
7373
7704
|
var init_gc = __esm(() => {
|
|
7374
7705
|
init_domain();
|
|
7375
7706
|
init_sources();
|
|
7707
|
+
GC_MIN_INTERVAL_MS = 6 * 60 * 60 * 1000;
|
|
7376
7708
|
});
|
|
7377
7709
|
|
|
7378
7710
|
// src/cli/render.ts
|
|
7379
|
-
import { readFileSync as
|
|
7711
|
+
import { readFileSync as readFileSync10 } from "node:fs";
|
|
7380
7712
|
import { dirname as dirname6, join as join14 } from "node:path";
|
|
7381
7713
|
function readTextSafe(path) {
|
|
7382
7714
|
try {
|
|
7383
|
-
return
|
|
7715
|
+
return readFileSync10(path, "utf8");
|
|
7384
7716
|
} catch {
|
|
7385
7717
|
return "";
|
|
7386
7718
|
}
|
|
@@ -7415,18 +7747,28 @@ function applyThemeIcons(fields, theme, config) {
|
|
|
7415
7747
|
});
|
|
7416
7748
|
}
|
|
7417
7749
|
function readGated(overrides, envInputs, config, root, dir, markers, clock) {
|
|
7418
|
-
const creds = overrides?.creds !== undefined ? overrides.creds : envInputs.hasApiKey || config.network.usage_fetch ?
|
|
7750
|
+
const creds = overrides?.creds !== undefined ? overrides.creds : envInputs.hasApiKey || config.network.usage_fetch ? readCredsCached(root) : null;
|
|
7419
7751
|
const usage = overrides?.usage !== undefined ? overrides.usage : config.network.usage_fetch ? readUsageCached(root) : null;
|
|
7420
7752
|
const balance = overrides?.balance !== undefined ? overrides.balance : config.network.balance_path !== "" ? readBalance(config.network.balance_path, clock) : null;
|
|
7421
7753
|
const helpfulEnv = needsHelpfulEnv(markers) ? readHelpfulEnv(dir) : EMPTY_HELPFUL_ENV;
|
|
7422
7754
|
return { creds, usage, balance, helpfulEnv };
|
|
7423
7755
|
}
|
|
7424
|
-
function
|
|
7425
|
-
const loaded = loadPack(name);
|
|
7756
|
+
function packThemeOf(loaded) {
|
|
7426
7757
|
if (!loaded.ok || loaded.pack.theme === undefined)
|
|
7427
7758
|
return null;
|
|
7428
7759
|
return { displayName: loaded.pack.displayName, ...loaded.pack.theme };
|
|
7429
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
|
+
}
|
|
7430
7772
|
function runRender(stdin, env, term, clock, overrides) {
|
|
7431
7773
|
try {
|
|
7432
7774
|
return build(stdin, env, term, clock, overrides);
|
|
@@ -7488,8 +7830,8 @@ function build(stdin, env, term, clock, overrides) {
|
|
|
7488
7830
|
const stacks = deriveStacks(markers, events);
|
|
7489
7831
|
const fresh = freshestEvent(events);
|
|
7490
7832
|
const stack = pickStack(stacks, fresh?.stack);
|
|
7491
|
-
const familiarity = deriveFamiliarity(attribution, persona, scannedCache, project, clock);
|
|
7492
|
-
const theme = resolveTheme(config,
|
|
7833
|
+
const familiarity = deriveFamiliarity(attribution, persona, scannedCache, project, clock, String(session));
|
|
7834
|
+
const theme = resolveTheme(config, makePackThemeLookup(persona, loaded), persona);
|
|
7493
7835
|
const composeInputs = {
|
|
7494
7836
|
provider,
|
|
7495
7837
|
model,
|
|
@@ -7590,6 +7932,10 @@ function build(stdin, env, term, clock, overrides) {
|
|
|
7590
7932
|
}
|
|
7591
7933
|
swallow(() => {
|
|
7592
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;
|
|
7593
7939
|
const toWrite = !isDefault && payloadChat !== undefined ? {
|
|
7594
7940
|
...scannedCache,
|
|
7595
7941
|
aggregate: {
|
|
@@ -7613,6 +7959,10 @@ function build(stdin, env, term, clock, overrides) {
|
|
|
7613
7959
|
swallow(() => {
|
|
7614
7960
|
readUsage(root, clock, { enabled: config.network.usage_fetch });
|
|
7615
7961
|
});
|
|
7962
|
+
swallow(() => {
|
|
7963
|
+
if (envInputs.hasApiKey || config.network.usage_fetch)
|
|
7964
|
+
refreshCreds(root, clock);
|
|
7965
|
+
});
|
|
7616
7966
|
swallow(() => {
|
|
7617
7967
|
runGc(root, clock);
|
|
7618
7968
|
});
|
|
@@ -7639,7 +7989,7 @@ var init_render2 = __esm(() => {
|
|
|
7639
7989
|
});
|
|
7640
7990
|
|
|
7641
7991
|
// src/cli/settings.ts
|
|
7642
|
-
import { existsSync as existsSync3, readFileSync as
|
|
7992
|
+
import { existsSync as existsSync3, readFileSync as readFileSync11, readdirSync as readdirSync4, renameSync as renameSync3, rmSync as rmSync2, writeFileSync as writeFileSync4 } from "node:fs";
|
|
7643
7993
|
import { basename as basename3, dirname as dirname7, join as join15 } from "node:path";
|
|
7644
7994
|
function asRecord(v) {
|
|
7645
7995
|
return typeof v === "object" && v !== null && !Array.isArray(v) ? v : {};
|
|
@@ -7670,16 +8020,16 @@ function pruneBackups(settingsPath) {
|
|
|
7670
8020
|
}
|
|
7671
8021
|
function safeWriteJson(path, text, restoreText) {
|
|
7672
8022
|
const tmp = `${path}.tmp.${String(process.pid)}.${Math.random().toString(36).slice(2)}`;
|
|
7673
|
-
|
|
7674
|
-
|
|
8023
|
+
writeFileSync4(tmp, text);
|
|
8024
|
+
renameSync3(tmp, path);
|
|
7675
8025
|
let ok = false;
|
|
7676
8026
|
try {
|
|
7677
|
-
ok = parseJsonObject(
|
|
8027
|
+
ok = parseJsonObject(readFileSync11(path, "utf8")) !== null;
|
|
7678
8028
|
} catch {
|
|
7679
8029
|
ok = false;
|
|
7680
8030
|
}
|
|
7681
8031
|
if (!ok) {
|
|
7682
|
-
|
|
8032
|
+
writeFileSync4(path, restoreText);
|
|
7683
8033
|
throw new Error(`ccsidekick: settings write failed verification at ${path}; restored prior content`);
|
|
7684
8034
|
}
|
|
7685
8035
|
}
|
|
@@ -7689,7 +8039,7 @@ function installSettings(opts) {
|
|
|
7689
8039
|
let base = {};
|
|
7690
8040
|
let originalText = "";
|
|
7691
8041
|
if (exists) {
|
|
7692
|
-
originalText =
|
|
8042
|
+
originalText = readFileSync11(settingsPath, "utf8");
|
|
7693
8043
|
const parsed = parseJsonObject(originalText);
|
|
7694
8044
|
if (parsed === null) {
|
|
7695
8045
|
throw new Error(`ccsidekick: refusing to modify unparseable settings at ${settingsPath}`);
|
|
@@ -7714,7 +8064,7 @@ function installSettings(opts) {
|
|
|
7714
8064
|
spinnerVerbs: { mode: "replace", verbs: [...spinnerVerbs] }
|
|
7715
8065
|
};
|
|
7716
8066
|
if (exists) {
|
|
7717
|
-
|
|
8067
|
+
writeFileSync4(`${settingsPath}.ccsidekick-bak.${String(Date.now())}`, originalText);
|
|
7718
8068
|
pruneBackups(settingsPath);
|
|
7719
8069
|
}
|
|
7720
8070
|
safeWriteJson(settingsPath, `${JSON.stringify(merged, null, 2)}
|
|
@@ -7728,11 +8078,11 @@ var init_settings = __esm(() => {
|
|
|
7728
8078
|
init_dist();
|
|
7729
8079
|
init_domain();
|
|
7730
8080
|
init_sources();
|
|
7731
|
-
HOOK_EVENTS = ["PostToolUse", "PostToolUseFailure"
|
|
8081
|
+
HOOK_EVENTS = ["PostToolUse", "PostToolUseFailure"];
|
|
7732
8082
|
});
|
|
7733
8083
|
|
|
7734
8084
|
// src/cli/setup.ts
|
|
7735
|
-
import { readFileSync as
|
|
8085
|
+
import { readFileSync as readFileSync12 } from "node:fs";
|
|
7736
8086
|
import { join as join16 } from "node:path";
|
|
7737
8087
|
function themeNames() {
|
|
7738
8088
|
return [CHARACTER_THEME, ...Object.keys(THEMES), ...PACKS];
|
|
@@ -7884,14 +8234,14 @@ function setupHelp() {
|
|
|
7884
8234
|
"",
|
|
7885
8235
|
"Flags (only the ones you pass are applied):",
|
|
7886
8236
|
` --character <name> ${PACKS.join(", ")}`,
|
|
7887
|
-
` --mode <mode> ${
|
|
8237
|
+
` --mode <mode> ${CHARACTER_MODES.join(", ")}`,
|
|
7888
8238
|
" --roster <a,b,c> characters for random mode (from --character's list)",
|
|
7889
8239
|
` --theme <name> ${themeNames().join(", ")}`,
|
|
7890
8240
|
" --currency <code> e.g. USD, EUR, INR",
|
|
7891
8241
|
" --budget <usd> monthly budget, a number >= 0",
|
|
7892
8242
|
" --comments <on|off> the character's own comment line",
|
|
7893
8243
|
" --helpful <on|off> the helpful-tip line",
|
|
7894
|
-
` --min-severity <sev> ${
|
|
8244
|
+
` --min-severity <sev> ${MIN_SEVERITIES.join(", ")}`,
|
|
7895
8245
|
" --widgets <a,b,c> statusline widgets to enable (others are turned off)",
|
|
7896
8246
|
" --usage-fetch <on|off> account-usage lookup to Anthropic (needed by the pay_as_you_go widget; no tokens)",
|
|
7897
8247
|
" --global | --local save target (default: global)",
|
|
@@ -7904,12 +8254,12 @@ function setupHelp() {
|
|
|
7904
8254
|
}
|
|
7905
8255
|
function defaultReadConfig(path) {
|
|
7906
8256
|
try {
|
|
7907
|
-
return
|
|
8257
|
+
return readFileSync12(path, "utf8");
|
|
7908
8258
|
} catch {
|
|
7909
8259
|
return null;
|
|
7910
8260
|
}
|
|
7911
8261
|
}
|
|
7912
|
-
var WIDGET_IDS,
|
|
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) => {
|
|
7913
8263
|
if (value === "on")
|
|
7914
8264
|
return true;
|
|
7915
8265
|
if (value === "off")
|
|
@@ -7919,12 +8269,11 @@ var WIDGET_IDS, MODES, SEVERITIES, VALUE_FLAGS, BOOL_FLAGS, splitList = (raw) =>
|
|
|
7919
8269
|
}, memberOf = (value, valid) => valid.includes(value), VALIDATORS;
|
|
7920
8270
|
var init_setup = __esm(() => {
|
|
7921
8271
|
init_data();
|
|
8272
|
+
init_domain();
|
|
7922
8273
|
init_packs();
|
|
7923
8274
|
init_render();
|
|
7924
8275
|
init_sources();
|
|
7925
8276
|
WIDGET_IDS = Object.keys(DEFAULT_CONFIG.statusline.widgets);
|
|
7926
|
-
MODES = ["fixed", "random"];
|
|
7927
|
-
SEVERITIES = ["low", "medium", "high", "critical"];
|
|
7928
8277
|
VALUE_FLAGS = new Set([
|
|
7929
8278
|
"character",
|
|
7930
8279
|
"mode",
|
|
@@ -7945,7 +8294,7 @@ var init_setup = __esm(() => {
|
|
|
7945
8294
|
enumInto(v, PACKS, "character", e, (x) => f.character = x);
|
|
7946
8295
|
},
|
|
7947
8296
|
mode: (v, f, e) => {
|
|
7948
|
-
enumInto(v,
|
|
8297
|
+
enumInto(v, CHARACTER_MODES, "mode", e, (x) => f.mode = x);
|
|
7949
8298
|
},
|
|
7950
8299
|
roster: (v, f, e) => {
|
|
7951
8300
|
listInto(v, PACKS, "roster", e, (x) => f.roster = x);
|
|
@@ -7978,7 +8327,7 @@ var init_setup = __esm(() => {
|
|
|
7978
8327
|
f.helpful = b;
|
|
7979
8328
|
},
|
|
7980
8329
|
"min-severity": (v, f, e) => {
|
|
7981
|
-
enumInto(v,
|
|
8330
|
+
enumInto(v, MIN_SEVERITIES, "min-severity", e, (x) => f.minSeverity = x);
|
|
7982
8331
|
},
|
|
7983
8332
|
widgets: (v, f, e) => {
|
|
7984
8333
|
listInto(v, WIDGET_IDS, "widgets", e, (x) => f.widgets = x);
|
|
@@ -7992,7 +8341,7 @@ var init_setup = __esm(() => {
|
|
|
7992
8341
|
});
|
|
7993
8342
|
|
|
7994
8343
|
// src/cli/uninstall.ts
|
|
7995
|
-
import { existsSync as existsSync4, readFileSync as
|
|
8344
|
+
import { existsSync as existsSync4, readFileSync as readFileSync13, readdirSync as readdirSync5 } from "node:fs";
|
|
7996
8345
|
import { basename as basename4, dirname as dirname8, join as join17 } from "node:path";
|
|
7997
8346
|
function asRecord2(v) {
|
|
7998
8347
|
return typeof v === "object" && v !== null && !Array.isArray(v) ? v : {};
|
|
@@ -8039,7 +8388,7 @@ function restoreNewestBackup(settingsPath) {
|
|
|
8039
8388
|
}
|
|
8040
8389
|
if (newest === undefined)
|
|
8041
8390
|
return null;
|
|
8042
|
-
atomicWrite(settingsPath,
|
|
8391
|
+
atomicWrite(settingsPath, readFileSync13(join17(dir, newest.name), "utf8"));
|
|
8043
8392
|
return newest.name;
|
|
8044
8393
|
}
|
|
8045
8394
|
function runRestore(settingsPath, emit) {
|
|
@@ -8093,7 +8442,7 @@ function runUninstall(opts) {
|
|
|
8093
8442
|
return;
|
|
8094
8443
|
}
|
|
8095
8444
|
if (existsSync4(settingsPath)) {
|
|
8096
|
-
const parsed = parseJsonObject2(
|
|
8445
|
+
const parsed = parseJsonObject2(readFileSync13(settingsPath, "utf8"));
|
|
8097
8446
|
if (parsed === null) {
|
|
8098
8447
|
throw new Error(`ccsidekick: refusing to modify unparseable settings at ${settingsPath}`);
|
|
8099
8448
|
}
|
|
@@ -8175,7 +8524,7 @@ var init_metrics = __esm(() => {
|
|
|
8175
8524
|
var ACCENT = "#ffd700", FRAME = "gray", DANGER = "red";
|
|
8176
8525
|
|
|
8177
8526
|
// src/tui/save.ts
|
|
8178
|
-
import { mkdirSync as
|
|
8527
|
+
import { mkdirSync as mkdirSync5 } from "node:fs";
|
|
8179
8528
|
import { join as join19 } from "node:path";
|
|
8180
8529
|
function defaultResolveVerbs(name) {
|
|
8181
8530
|
const loaded = loadPack(name);
|
|
@@ -8212,7 +8561,7 @@ function save(config, scope, dir, renderBin, opts = {}) {
|
|
|
8212
8561
|
writeConfigToml(join19(cwd, ".ccsidekick"), normalized);
|
|
8213
8562
|
if (opts.wireLocalSettings === true) {
|
|
8214
8563
|
const claudeDir = join19(cwd, ".claude");
|
|
8215
|
-
|
|
8564
|
+
mkdirSync5(claudeDir, { recursive: true });
|
|
8216
8565
|
installSettings({
|
|
8217
8566
|
settingsPath: join19(claudeDir, "settings.json"),
|
|
8218
8567
|
renderBin,
|
|
@@ -10910,14 +11259,14 @@ var init_PreviewPanel = __esm(() => {
|
|
|
10910
11259
|
|
|
10911
11260
|
// src/tui/preview/fixture.ts
|
|
10912
11261
|
import { execFileSync } from "node:child_process";
|
|
10913
|
-
import { existsSync as existsSync6, mkdirSync as
|
|
11262
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync6, mkdtempSync, realpathSync, writeFileSync as writeFileSync5 } from "node:fs";
|
|
10914
11263
|
import { tmpdir } from "node:os";
|
|
10915
11264
|
import { join as join20 } from "node:path";
|
|
10916
11265
|
function seedTranscriptFixture(workdir, root) {
|
|
10917
11266
|
const dir = root !== undefined ? join20(root, "projects", workdir.replace(/[/.]/g, "-")) : workdir;
|
|
10918
11267
|
const path = join20(dir, "preview-transcript.jsonl");
|
|
10919
11268
|
try {
|
|
10920
|
-
|
|
11269
|
+
mkdirSync6(dir, { recursive: true });
|
|
10921
11270
|
if (!existsSync6(path)) {
|
|
10922
11271
|
const lines = [
|
|
10923
11272
|
JSON.stringify({ type: "system", subtype: "compact_boundary" }),
|
|
@@ -10941,7 +11290,7 @@ function seedTranscriptFixture(workdir, root) {
|
|
|
10941
11290
|
}
|
|
10942
11291
|
})
|
|
10943
11292
|
];
|
|
10944
|
-
|
|
11293
|
+
writeFileSync5(path, `${lines.join(`
|
|
10945
11294
|
`)}
|
|
10946
11295
|
`);
|
|
10947
11296
|
}
|
|
@@ -10985,7 +11334,7 @@ function gitEnv() {
|
|
|
10985
11334
|
}
|
|
10986
11335
|
function setupGitFixture(workdir) {
|
|
10987
11336
|
try {
|
|
10988
|
-
|
|
11337
|
+
mkdirSync6(workdir, { recursive: true });
|
|
10989
11338
|
if (existsSync6(join20(workdir, ".git")))
|
|
10990
11339
|
return;
|
|
10991
11340
|
const env2 = gitEnv();
|
|
@@ -10996,20 +11345,20 @@ function setupGitFixture(workdir) {
|
|
|
10996
11345
|
git("config", "user.email", "wayne@example.com");
|
|
10997
11346
|
git("config", "user.name", "Bruce Wayne");
|
|
10998
11347
|
git("remote", "add", "origin", "https://github.com/wayne/ccsidekick.git");
|
|
10999
|
-
|
|
11348
|
+
writeFileSync5(join20(workdir, "README.md"), `# ccsidekick
|
|
11000
11349
|
|
|
11001
11350
|
original line
|
|
11002
11351
|
keep me
|
|
11003
11352
|
`);
|
|
11004
11353
|
git("add", "README.md");
|
|
11005
11354
|
git("commit", "-q", "-m", "initial commit");
|
|
11006
|
-
|
|
11355
|
+
writeFileSync5(join20(workdir, "README.md"), `# ccsidekick
|
|
11007
11356
|
|
|
11008
11357
|
edited line
|
|
11009
11358
|
keep me
|
|
11010
11359
|
new line
|
|
11011
11360
|
`);
|
|
11012
|
-
|
|
11361
|
+
writeFileSync5(join20(workdir, "src.ts"), `export const answer = 42;
|
|
11013
11362
|
`);
|
|
11014
11363
|
git("add", "src.ts");
|
|
11015
11364
|
} catch {}
|
|
@@ -11034,11 +11383,11 @@ function seedCostFixture(root, workdir) {
|
|
|
11034
11383
|
const projectsRoot = join20(root, "projects");
|
|
11035
11384
|
const currentProjectDir = join20(projectsRoot, workdir.replace(/[/.]/g, "-"));
|
|
11036
11385
|
const otherProjectDir = join20(projectsRoot, "-other-project");
|
|
11037
|
-
|
|
11038
|
-
|
|
11039
|
-
|
|
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)}
|
|
11040
11389
|
`);
|
|
11041
|
-
|
|
11390
|
+
writeFileSync5(join20(otherProjectDir, "sess-b.jsonl"), `${transcriptLine("sess-b-1", "claude-opus-4-8", 200000, 50000)}
|
|
11042
11391
|
`);
|
|
11043
11392
|
}
|
|
11044
11393
|
function scratchRoot(scratchDir) {
|
|
@@ -11049,7 +11398,7 @@ function scratchRoot(scratchDir) {
|
|
|
11049
11398
|
}
|
|
11050
11399
|
function previewEnv(root, extra) {
|
|
11051
11400
|
const homeBase = join20(root, "home");
|
|
11052
|
-
|
|
11401
|
+
mkdirSync6(homeBase, { recursive: true });
|
|
11053
11402
|
let home = homeBase;
|
|
11054
11403
|
try {
|
|
11055
11404
|
home = realpathSync(homeBase);
|
|
@@ -11258,8 +11607,8 @@ function checkedTargets(targets, checked) {
|
|
|
11258
11607
|
}
|
|
11259
11608
|
|
|
11260
11609
|
// src/tui/sections/configdir.tsx
|
|
11261
|
-
import { existsSync as existsSync7, mkdirSync as
|
|
11262
|
-
import { homedir as
|
|
11610
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync7 } from "node:fs";
|
|
11611
|
+
import { homedir as homedir4 } from "node:os";
|
|
11263
11612
|
import { Box as Box22, Text as Text27, useApp, useInput as useInput7 } from "ink";
|
|
11264
11613
|
import { useState as useState6 } from "react";
|
|
11265
11614
|
import { jsxDEV as jsxDEV14 } from "react/jsx-dev-runtime";
|
|
@@ -11303,7 +11652,7 @@ function handleCustomKey(input, key, d) {
|
|
|
11303
11652
|
function handleConfirmKey(key, d) {
|
|
11304
11653
|
if (key.return && d.pending !== null) {
|
|
11305
11654
|
try {
|
|
11306
|
-
|
|
11655
|
+
mkdirSync7(d.pending, { recursive: true });
|
|
11307
11656
|
d.addDir(d.pending);
|
|
11308
11657
|
} catch {
|
|
11309
11658
|
d.setError(`Could not create ${d.pending}`);
|
|
@@ -11370,7 +11719,7 @@ function ConfigDirPicker({
|
|
|
11370
11719
|
onQuit
|
|
11371
11720
|
}) {
|
|
11372
11721
|
const { exit } = useApp();
|
|
11373
|
-
const home =
|
|
11722
|
+
const home = homedir4();
|
|
11374
11723
|
const [cursor4, setCursor] = useState6(0);
|
|
11375
11724
|
const [mode, setMode] = useState6("list");
|
|
11376
11725
|
const [text, setText] = useState6("");
|
|
@@ -11633,7 +11982,10 @@ function commentsFields(d) {
|
|
|
11633
11982
|
value: d.comments.min_severity,
|
|
11634
11983
|
next: (c) => ({
|
|
11635
11984
|
...c,
|
|
11636
|
-
comments: {
|
|
11985
|
+
comments: {
|
|
11986
|
+
...c.comments,
|
|
11987
|
+
min_severity: step(MIN_SEVERITIES, c.comments.min_severity, 1)
|
|
11988
|
+
}
|
|
11637
11989
|
})
|
|
11638
11990
|
};
|
|
11639
11991
|
return d.comments.helpful ? [characterComments, helpfulComments, minSeverity] : [characterComments, helpfulComments];
|
|
@@ -11681,16 +12033,15 @@ function sectionFields(section2, d) {
|
|
|
11681
12033
|
return [];
|
|
11682
12034
|
}
|
|
11683
12035
|
}
|
|
11684
|
-
var onOff = (b) => b ? "on" : "off", WIDGET_IDS2,
|
|
12036
|
+
var onOff = (b) => b ? "on" : "off", WIDGET_IDS2, step = (arr, cur, dir) => {
|
|
11685
12037
|
const n = arr.length;
|
|
11686
12038
|
const i = arr.indexOf(cur);
|
|
11687
12039
|
return arr[((i + dir) % n + n) % n] ?? cur;
|
|
11688
12040
|
};
|
|
11689
12041
|
var init_forms = __esm(() => {
|
|
12042
|
+
init_domain();
|
|
11690
12043
|
init_sources();
|
|
11691
12044
|
WIDGET_IDS2 = Object.keys(DEFAULT_CONFIG.statusline.widgets);
|
|
11692
|
-
SEVERITIES2 = ["low", "medium", "high", "critical"];
|
|
11693
|
-
BANDINGS = ["solid", "cycle"];
|
|
11694
12045
|
});
|
|
11695
12046
|
|
|
11696
12047
|
// src/tui/sections/FormSection.tsx
|
|
@@ -12831,7 +13182,7 @@ var init_inputRoute = __esm(() => {
|
|
|
12831
13182
|
});
|
|
12832
13183
|
|
|
12833
13184
|
// src/tui/shell/saveTarget.ts
|
|
12834
|
-
import { existsSync as existsSync8, readFileSync as
|
|
13185
|
+
import { existsSync as existsSync8, readFileSync as readFileSync14 } from "node:fs";
|
|
12835
13186
|
import { join as join22 } from "node:path";
|
|
12836
13187
|
function chipFor(targets) {
|
|
12837
13188
|
if (targets.every((t) => t.scope === "global"))
|
|
@@ -12844,7 +13195,7 @@ function alreadyWired(settingsPath) {
|
|
|
12844
13195
|
if (!existsSync8(settingsPath))
|
|
12845
13196
|
return false;
|
|
12846
13197
|
try {
|
|
12847
|
-
const parsed = JSON.parse(
|
|
13198
|
+
const parsed = JSON.parse(readFileSync14(settingsPath, "utf8"));
|
|
12848
13199
|
return isOurStatusLine(parsed.statusLine);
|
|
12849
13200
|
} catch {
|
|
12850
13201
|
return false;
|
|
@@ -13098,7 +13449,7 @@ var init_useMouseWheel = __esm(() => {
|
|
|
13098
13449
|
});
|
|
13099
13450
|
|
|
13100
13451
|
// src/tui/shell/Dashboard.tsx
|
|
13101
|
-
import { readFileSync as
|
|
13452
|
+
import { readFileSync as readFileSync15 } from "node:fs";
|
|
13102
13453
|
import { join as join23 } from "node:path";
|
|
13103
13454
|
import { Box as Box30, Text as Text35, useApp as useApp2, useInput as useInput8 } from "ink";
|
|
13104
13455
|
import { useCallback as useCallback6, useMemo as useMemo9, useState as useState8 } from "react";
|
|
@@ -13897,7 +14248,7 @@ var seed = (configDir2, initial) => {
|
|
|
13897
14248
|
if (initial)
|
|
13898
14249
|
return initial;
|
|
13899
14250
|
try {
|
|
13900
|
-
return loadConfig(
|
|
14251
|
+
return loadConfig(readFileSync15(join23(configDir2, "ccsidekick", "config.toml"), "utf8"));
|
|
13901
14252
|
} catch {
|
|
13902
14253
|
return loadConfig("");
|
|
13903
14254
|
}
|
|
@@ -14540,8 +14891,8 @@ var init_Wizard = __esm(() => {
|
|
|
14540
14891
|
});
|
|
14541
14892
|
|
|
14542
14893
|
// src/tui/shell/App.tsx
|
|
14543
|
-
import { existsSync as existsSync9, readFileSync as
|
|
14544
|
-
import { homedir as
|
|
14894
|
+
import { existsSync as existsSync9, readFileSync as readFileSync16 } from "node:fs";
|
|
14895
|
+
import { homedir as homedir5 } from "node:os";
|
|
14545
14896
|
import { join as join24 } from "node:path";
|
|
14546
14897
|
import { useInput as useInput10 } from "ink";
|
|
14547
14898
|
import { useMemo as useMemo10, useState as useState10 } from "react";
|
|
@@ -14555,7 +14906,7 @@ function welcomeTheme(suggestedDir) {
|
|
|
14555
14906
|
if (suggestedDir === undefined || suggestedDir === "")
|
|
14556
14907
|
return THEMES.houston;
|
|
14557
14908
|
try {
|
|
14558
|
-
const text =
|
|
14909
|
+
const text = readFileSync16(join24(suggestedDir, "ccsidekick", "config.toml"), "utf8");
|
|
14559
14910
|
return pickTheme(loadConfig(text).theme.name);
|
|
14560
14911
|
} catch {
|
|
14561
14912
|
return THEMES.houston;
|
|
@@ -14604,7 +14955,7 @@ function App(props) {
|
|
|
14604
14955
|
const rows = props.rows ?? live.rows;
|
|
14605
14956
|
const atFloor = breakpointFor(columns, rows) === "floor";
|
|
14606
14957
|
useInput10(() => {});
|
|
14607
|
-
const home = props.homeDir ??
|
|
14958
|
+
const home = props.homeDir ?? homedir5();
|
|
14608
14959
|
const cwd = props.cwd ?? process.cwd();
|
|
14609
14960
|
const suggested = props.suggestedDir ?? env2["CLAUDE_CONFIG_DIR"];
|
|
14610
14961
|
const wordmarkHues = useMemo10(() => welcomeTheme(suggested).hues, [suggested]);
|
|
@@ -14715,12 +15066,12 @@ var init_shell = __esm(() => {
|
|
|
14715
15066
|
init_cli();
|
|
14716
15067
|
init_domain();
|
|
14717
15068
|
init_tui();
|
|
14718
|
-
import { homedir as
|
|
15069
|
+
import { homedir as homedir6 } from "node:os";
|
|
14719
15070
|
import { join as join25 } from "node:path";
|
|
14720
15071
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
14721
15072
|
var sub = process.argv[2];
|
|
14722
15073
|
function claudeDir() {
|
|
14723
|
-
return process.env["CLAUDE_CONFIG_DIR"] ?? join25(
|
|
15074
|
+
return process.env["CLAUDE_CONFIG_DIR"] ?? join25(homedir6(), ".claude");
|
|
14724
15075
|
}
|
|
14725
15076
|
function printHelp() {
|
|
14726
15077
|
process.stdout.write([
|
|
@@ -14765,7 +15116,7 @@ async function launchTui() {
|
|
|
14765
15116
|
const renderBin = fileURLToPath2(new URL("ccsidekick-render.js", import.meta.url));
|
|
14766
15117
|
const suggested = process.env["CLAUDE_CONFIG_DIR"];
|
|
14767
15118
|
const instance = render(createElement(App2, {
|
|
14768
|
-
homeDir:
|
|
15119
|
+
homeDir: homedir6(),
|
|
14769
15120
|
renderBin,
|
|
14770
15121
|
...suggested !== undefined ? { suggestedDir: suggested } : {}
|
|
14771
15122
|
}));
|
|
@@ -14798,7 +15149,7 @@ async function main() {
|
|
|
14798
15149
|
renderBin,
|
|
14799
15150
|
readConfig: defaultReadConfig,
|
|
14800
15151
|
cwd: process.cwd(),
|
|
14801
|
-
homeDir:
|
|
15152
|
+
homeDir: homedir6(),
|
|
14802
15153
|
env: process.env,
|
|
14803
15154
|
out: (s) => process.stdout.write(s),
|
|
14804
15155
|
err: (s) => process.stderr.write(s)
|