exe-watcher 0.2.52 → 0.2.53
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/cli.js +336 -153
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -27,9 +27,13 @@ var init_cache_dir = __esm({
|
|
|
27
27
|
// src/resource-budget.ts
|
|
28
28
|
import { getHeapStatistics } from "v8";
|
|
29
29
|
import { AsyncLocalStorage } from "async_hooks";
|
|
30
|
+
function effectiveMaxBytes(requested) {
|
|
31
|
+
const override = Number(process.env["EXE_WATCHER_MAX_SCAN_BYTES"]);
|
|
32
|
+
return Number.isFinite(override) && override > 0 ? Math.min(requested, override) : requested;
|
|
33
|
+
}
|
|
30
34
|
function withScanBudget(fn, maxRecords = 2e5, signal, maxBytes = 1024 * 1024 * 1024) {
|
|
31
35
|
if (scans.getStore()) return fn();
|
|
32
|
-
return scans.run({ bytes: 0, records: 0, started: Date.now(), maxRecords, signal, maxBytes }, fn);
|
|
36
|
+
return scans.run({ bytes: 0, records: 0, started: Date.now(), maxRecords, signal, maxBytes: effectiveMaxBytes(maxBytes) }, fn);
|
|
33
37
|
}
|
|
34
38
|
function chargeRead(bytes) {
|
|
35
39
|
const budget = scans.getStore();
|
|
@@ -632,17 +636,17 @@ var init_bash_utils = __esm({
|
|
|
632
636
|
});
|
|
633
637
|
|
|
634
638
|
// src/cursor-cache.ts
|
|
635
|
-
import { readFile as
|
|
639
|
+
import { readFile as readFile8, mkdir as mkdir9, stat as stat10, rename as rename7 } from "fs/promises";
|
|
636
640
|
import { open as open6 } from "fs/promises";
|
|
637
|
-
import { join as
|
|
641
|
+
import { join as join14 } from "path";
|
|
638
642
|
import { randomBytes as randomBytes4 } from "crypto";
|
|
639
643
|
function getCachePath2() {
|
|
640
|
-
return
|
|
644
|
+
return join14(getCacheDir(), CACHE_FILE);
|
|
641
645
|
}
|
|
642
646
|
async function getDbFingerprint(dbPath) {
|
|
643
647
|
try {
|
|
644
|
-
const s = await
|
|
645
|
-
const wal = await
|
|
648
|
+
const s = await stat10(dbPath);
|
|
649
|
+
const wal = await stat10(`${dbPath}-wal`).catch(() => null);
|
|
646
650
|
return [dbPath, s.dev, s.ino, s.ctimeMs, s.mtimeMs, s.size, wal?.ino, wal?.ctimeMs, wal?.mtimeMs, wal?.size, getPricingFingerprint()].join(":");
|
|
647
651
|
} catch {
|
|
648
652
|
return null;
|
|
@@ -652,10 +656,10 @@ async function readCachedResults(dbPath) {
|
|
|
652
656
|
try {
|
|
653
657
|
const fp = await getDbFingerprint(dbPath);
|
|
654
658
|
if (!fp) return null;
|
|
655
|
-
const size = (await
|
|
659
|
+
const size = (await stat10(getCachePath2())).size;
|
|
656
660
|
if (size > 16 * 1024 * 1024) return null;
|
|
657
661
|
chargeRead(size);
|
|
658
|
-
const raw = await
|
|
662
|
+
const raw = await readFile8(getCachePath2(), "utf-8");
|
|
659
663
|
const cache = JSON.parse(raw);
|
|
660
664
|
if (cache.fingerprint === fp) {
|
|
661
665
|
return cache.calls;
|
|
@@ -671,7 +675,7 @@ async function writeCachedResults(dbPath, calls, expected) {
|
|
|
671
675
|
const fp = await getDbFingerprint(dbPath);
|
|
672
676
|
if (!fp || expected !== void 0 && expected !== fp) return;
|
|
673
677
|
const dir = getCacheDir();
|
|
674
|
-
await
|
|
678
|
+
await mkdir9(dir, { recursive: true });
|
|
675
679
|
const cache = {
|
|
676
680
|
fingerprint: fp,
|
|
677
681
|
calls
|
|
@@ -687,7 +691,7 @@ async function writeCachedResults(dbPath, calls, expected) {
|
|
|
687
691
|
} finally {
|
|
688
692
|
await handle.close();
|
|
689
693
|
}
|
|
690
|
-
await
|
|
694
|
+
await rename7(tmpPath, finalPath);
|
|
691
695
|
} catch {
|
|
692
696
|
}
|
|
693
697
|
}
|
|
@@ -786,16 +790,16 @@ __export(cursor_exports, {
|
|
|
786
790
|
cursor: () => cursor
|
|
787
791
|
});
|
|
788
792
|
import { existsSync } from "fs";
|
|
789
|
-
import { join as
|
|
793
|
+
import { join as join15 } from "path";
|
|
790
794
|
import { homedir as homedir8 } from "os";
|
|
791
795
|
function getCursorDbPath() {
|
|
792
796
|
if (process.platform === "darwin") {
|
|
793
|
-
return
|
|
797
|
+
return join15(homedir8(), "Library", "Application Support", "Cursor", "User", "globalStorage", "state.vscdb");
|
|
794
798
|
}
|
|
795
799
|
if (process.platform === "win32") {
|
|
796
|
-
return
|
|
800
|
+
return join15(homedir8(), "AppData", "Roaming", "Cursor", "User", "globalStorage", "state.vscdb");
|
|
797
801
|
}
|
|
798
|
-
return
|
|
802
|
+
return join15(homedir8(), ".config", "Cursor", "User", "globalStorage", "state.vscdb");
|
|
799
803
|
}
|
|
800
804
|
function extractLanguages(codeBlocksJson) {
|
|
801
805
|
if (!codeBlocksJson) return [];
|
|
@@ -1037,19 +1041,19 @@ __export(opencode_exports, {
|
|
|
1037
1041
|
opencode: () => opencode
|
|
1038
1042
|
});
|
|
1039
1043
|
import { readdir as readdir8 } from "fs/promises";
|
|
1040
|
-
import { join as
|
|
1044
|
+
import { join as join16 } from "path";
|
|
1041
1045
|
import { homedir as homedir9 } from "os";
|
|
1042
|
-
function
|
|
1046
|
+
function sanitize2(dir) {
|
|
1043
1047
|
return dir.replace(/^\//, "").replace(/\//g, "-");
|
|
1044
1048
|
}
|
|
1045
1049
|
function getDataDir(dataDir) {
|
|
1046
|
-
const base = dataDir ?? process.env["XDG_DATA_HOME"] ??
|
|
1047
|
-
return
|
|
1050
|
+
const base = dataDir ?? process.env["XDG_DATA_HOME"] ?? join16(homedir9(), ".local", "share");
|
|
1051
|
+
return join16(base, "opencode");
|
|
1048
1052
|
}
|
|
1049
1053
|
async function findDbFiles(dir) {
|
|
1050
1054
|
try {
|
|
1051
1055
|
const entries = await readdir8(dir);
|
|
1052
|
-
return entries.filter((f) => f.startsWith("opencode") && f.endsWith(".db")).map((f) =>
|
|
1056
|
+
return entries.filter((f) => f.startsWith("opencode") && f.endsWith(".db")).map((f) => join16(dir, f));
|
|
1053
1057
|
} catch (error) {
|
|
1054
1058
|
if (error instanceof ResourceBudgetError) throw error;
|
|
1055
1059
|
return [];
|
|
@@ -1204,7 +1208,7 @@ async function discoverFromDb(dbPath) {
|
|
|
1204
1208
|
);
|
|
1205
1209
|
return rows.map((row) => ({
|
|
1206
1210
|
path: `${dbPath}:${row.id}`,
|
|
1207
|
-
project: row.directory ?
|
|
1211
|
+
project: row.directory ? sanitize2(row.directory) : sanitize2(row.title),
|
|
1208
1212
|
provider: "opencode"
|
|
1209
1213
|
}));
|
|
1210
1214
|
} catch (error) {
|
|
@@ -1275,18 +1279,18 @@ __export(cursor_agent_exports, {
|
|
|
1275
1279
|
});
|
|
1276
1280
|
import { createHash as createHash3 } from "crypto";
|
|
1277
1281
|
import { existsSync as existsSync2 } from "fs";
|
|
1278
|
-
import { readdir as readdir9, stat as
|
|
1279
|
-
import { join as
|
|
1282
|
+
import { readdir as readdir9, stat as stat11 } from "fs/promises";
|
|
1283
|
+
import { join as join17, basename as basename7 } from "path";
|
|
1280
1284
|
import { homedir as homedir10 } from "os";
|
|
1281
1285
|
function getCursorAgentBaseDir(baseDirOverride) {
|
|
1282
1286
|
if (baseDirOverride) return baseDirOverride;
|
|
1283
|
-
return
|
|
1287
|
+
return join17(homedir10(), ".cursor");
|
|
1284
1288
|
}
|
|
1285
1289
|
function getProjectsDir2(baseDir) {
|
|
1286
|
-
return
|
|
1290
|
+
return join17(baseDir, "projects");
|
|
1287
1291
|
}
|
|
1288
1292
|
function getAttributionDbPath(baseDir) {
|
|
1289
|
-
return
|
|
1293
|
+
return join17(baseDir, "ai-tracking", "ai-code-tracking.db");
|
|
1290
1294
|
}
|
|
1291
1295
|
function estimateTokens(charCount) {
|
|
1292
1296
|
if (charCount <= 0) return 0;
|
|
@@ -1476,7 +1480,7 @@ function createParser6(source, seenKeys, dbPath, summariesByConversationId) {
|
|
|
1476
1480
|
}
|
|
1477
1481
|
let timestamp = summary?.updatedAt ?? null;
|
|
1478
1482
|
if (!timestamp) {
|
|
1479
|
-
const fileStat = await
|
|
1483
|
+
const fileStat = await stat11(source.path);
|
|
1480
1484
|
timestamp = fileStat.mtime.toISOString();
|
|
1481
1485
|
}
|
|
1482
1486
|
const model = resolveModel3(summary?.model ?? null);
|
|
@@ -1545,13 +1549,13 @@ function createCursorAgentProvider(baseDirOverride) {
|
|
|
1545
1549
|
for (const entry of projectEntries) {
|
|
1546
1550
|
if (!entry.isDirectory()) continue;
|
|
1547
1551
|
const projectId = prettifyProjectId(entry.name);
|
|
1548
|
-
const transcriptDir =
|
|
1552
|
+
const transcriptDir = join17(projectsDir, entry.name, "agent-transcripts");
|
|
1549
1553
|
if (!existsSync2(transcriptDir)) continue;
|
|
1550
1554
|
const transcriptEntries = await readdir9(transcriptDir, { withFileTypes: true });
|
|
1551
1555
|
for (const transcript of transcriptEntries) {
|
|
1552
1556
|
if (!transcript.isFile()) continue;
|
|
1553
1557
|
if (!transcript.name.endsWith(".txt")) continue;
|
|
1554
|
-
const transcriptPath =
|
|
1558
|
+
const transcriptPath = join17(transcriptDir, transcript.name);
|
|
1555
1559
|
sources.push({
|
|
1556
1560
|
path: transcriptPath,
|
|
1557
1561
|
project: projectId,
|
|
@@ -1681,7 +1685,7 @@ async function acquireFileLock(path, timeoutMs = 15e3) {
|
|
|
1681
1685
|
// src/cli.ts
|
|
1682
1686
|
init_resource_budget();
|
|
1683
1687
|
import { existsSync as existsSync7, readFileSync as readFileSync2, statSync as statSync3 } from "fs";
|
|
1684
|
-
import { join as
|
|
1688
|
+
import { join as join25 } from "path";
|
|
1685
1689
|
import { homedir as homedir14 } from "os";
|
|
1686
1690
|
import { Command } from "commander";
|
|
1687
1691
|
|
|
@@ -3135,8 +3139,8 @@ async function cachedFileParse(path, kind, parse, warnings) {
|
|
|
3135
3139
|
init_resource_budget();
|
|
3136
3140
|
init_fs_utils();
|
|
3137
3141
|
init_models();
|
|
3138
|
-
import { readdir as readdir10, stat as
|
|
3139
|
-
import { basename as basename8, join as
|
|
3142
|
+
import { readdir as readdir10, stat as stat13 } from "fs/promises";
|
|
3143
|
+
import { basename as basename8, join as join19 } from "path";
|
|
3140
3144
|
|
|
3141
3145
|
// src/providers/index.ts
|
|
3142
3146
|
init_resource_budget();
|
|
@@ -3236,12 +3240,133 @@ var claude = {
|
|
|
3236
3240
|
}
|
|
3237
3241
|
};
|
|
3238
3242
|
|
|
3243
|
+
// src/providers/codex.ts
|
|
3244
|
+
import { readdir as readdir5, stat as stat7 } from "fs/promises";
|
|
3245
|
+
import { basename as basename3, join as join11 } from "path";
|
|
3246
|
+
import { homedir as homedir5 } from "os";
|
|
3247
|
+
|
|
3248
|
+
// src/discovery-cache.ts
|
|
3249
|
+
init_cache_dir();
|
|
3250
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
3251
|
+
import { mkdir as mkdir8, readFile as readFile7, rename as rename6, stat as stat6, unlink as unlink5, writeFile as writeFile3 } from "fs/promises";
|
|
3252
|
+
import { join as join10 } from "path";
|
|
3253
|
+
init_resource_budget();
|
|
3254
|
+
var VERSION2 = 1;
|
|
3255
|
+
var MANIFEST = "discovery-index.json";
|
|
3256
|
+
var MAX_MANIFEST_BYTES = 16 * 1024 * 1024;
|
|
3257
|
+
var MAX_ENTRIES = 2e4;
|
|
3258
|
+
var MAX_AGE_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
3259
|
+
var TOUCH_INTERVAL_MS = 6 * 60 * 60 * 1e3;
|
|
3260
|
+
var loaded = null;
|
|
3261
|
+
var loadedFrom = "";
|
|
3262
|
+
var dirty = false;
|
|
3263
|
+
function manifestPath() {
|
|
3264
|
+
return join10(getCacheDir(), MANIFEST);
|
|
3265
|
+
}
|
|
3266
|
+
function fingerprintOf(s) {
|
|
3267
|
+
return [s.dev, s.ino, s.size, s.mtimeMs, s.ctimeMs].join(":");
|
|
3268
|
+
}
|
|
3269
|
+
function emptyManifest() {
|
|
3270
|
+
return { v: VERSION2, e: /* @__PURE__ */ Object.create(null) };
|
|
3271
|
+
}
|
|
3272
|
+
function sanitize(raw) {
|
|
3273
|
+
if (!raw || typeof raw !== "object") return emptyManifest();
|
|
3274
|
+
const parsed = raw;
|
|
3275
|
+
if (parsed.v !== VERSION2 || !parsed.e || typeof parsed.e !== "object") return emptyManifest();
|
|
3276
|
+
const out = emptyManifest();
|
|
3277
|
+
for (const [key, value] of Object.entries(parsed.e)) {
|
|
3278
|
+
if (key === "__proto__" || key === "constructor" || key === "prototype") continue;
|
|
3279
|
+
if (!value || typeof value !== "object") continue;
|
|
3280
|
+
const entry = value;
|
|
3281
|
+
if (typeof entry.f !== "string" || typeof entry.t !== "number") continue;
|
|
3282
|
+
out.e[key] = { f: entry.f, r: entry.r ?? null, t: entry.t };
|
|
3283
|
+
}
|
|
3284
|
+
return out;
|
|
3285
|
+
}
|
|
3286
|
+
async function load() {
|
|
3287
|
+
const path = manifestPath();
|
|
3288
|
+
if (loaded && loadedFrom === path) return loaded;
|
|
3289
|
+
loaded = emptyManifest();
|
|
3290
|
+
loadedFrom = path;
|
|
3291
|
+
dirty = false;
|
|
3292
|
+
try {
|
|
3293
|
+
const s = await stat6(path);
|
|
3294
|
+
if (s.size > MAX_MANIFEST_BYTES) return loaded;
|
|
3295
|
+
chargeRead(s.size);
|
|
3296
|
+
loaded = sanitize(JSON.parse(await readFile7(path, "utf8")));
|
|
3297
|
+
} catch (err) {
|
|
3298
|
+
if (err instanceof ResourceBudgetError) throw err;
|
|
3299
|
+
}
|
|
3300
|
+
return loaded;
|
|
3301
|
+
}
|
|
3302
|
+
function evict(manifest) {
|
|
3303
|
+
const cutoff = Date.now() - MAX_AGE_MS;
|
|
3304
|
+
for (const [key, entry] of Object.entries(manifest.e)) {
|
|
3305
|
+
if (entry.t < cutoff) delete manifest.e[key];
|
|
3306
|
+
}
|
|
3307
|
+
const keys = Object.keys(manifest.e);
|
|
3308
|
+
if (keys.length <= MAX_ENTRIES) return;
|
|
3309
|
+
keys.sort((a, b) => manifest.e[a].t - manifest.e[b].t);
|
|
3310
|
+
for (const key of keys.slice(0, keys.length - MAX_ENTRIES)) delete manifest.e[key];
|
|
3311
|
+
}
|
|
3312
|
+
async function cachedDiscoveryHeader(kind, path, stats, compute) {
|
|
3313
|
+
const manifest = await load();
|
|
3314
|
+
const key = `${kind}\0${path}`;
|
|
3315
|
+
const fingerprint2 = fingerprintOf(stats);
|
|
3316
|
+
const hit = manifest.e[key];
|
|
3317
|
+
if (hit && hit.f === fingerprint2) {
|
|
3318
|
+
const now = Date.now();
|
|
3319
|
+
if (now - hit.t > TOUCH_INTERVAL_MS) {
|
|
3320
|
+
hit.t = now;
|
|
3321
|
+
dirty = true;
|
|
3322
|
+
}
|
|
3323
|
+
return hit.r;
|
|
3324
|
+
}
|
|
3325
|
+
const value = await compute();
|
|
3326
|
+
const after = await stat6(path).catch(() => null);
|
|
3327
|
+
if (!after || fingerprintOf(after) !== fingerprint2) return value;
|
|
3328
|
+
manifest.e[key] = { f: fingerprint2, r: value ?? null, t: Date.now() };
|
|
3329
|
+
dirty = true;
|
|
3330
|
+
return value;
|
|
3331
|
+
}
|
|
3332
|
+
async function flushDiscoveryCache() {
|
|
3333
|
+
if (!dirty || !loaded) return;
|
|
3334
|
+
const mine = loaded;
|
|
3335
|
+
dirty = false;
|
|
3336
|
+
const path = manifestPath();
|
|
3337
|
+
const dir = getCacheDir();
|
|
3338
|
+
try {
|
|
3339
|
+
await mkdir8(dir, { recursive: true });
|
|
3340
|
+
const release = await acquireFileLock(join10(dir, "discovery-index.lock"));
|
|
3341
|
+
try {
|
|
3342
|
+
const onDisk = await readFile7(path, "utf8").then((raw) => sanitize(JSON.parse(raw))).catch(() => emptyManifest());
|
|
3343
|
+
for (const [key, entry] of Object.entries(mine.e)) {
|
|
3344
|
+
const other = onDisk.e[key];
|
|
3345
|
+
if (!other || other.t <= entry.t) onDisk.e[key] = entry;
|
|
3346
|
+
}
|
|
3347
|
+
evict(onDisk);
|
|
3348
|
+
const payload = JSON.stringify(onDisk);
|
|
3349
|
+
if (Buffer.byteLength(payload) > MAX_MANIFEST_BYTES) return;
|
|
3350
|
+
const tmp = `${path}.${randomUUID2()}.tmp`;
|
|
3351
|
+
try {
|
|
3352
|
+
await writeFile3(tmp, payload, { mode: 384 });
|
|
3353
|
+
await rename6(tmp, path);
|
|
3354
|
+
} finally {
|
|
3355
|
+
await unlink5(tmp).catch(() => {
|
|
3356
|
+
});
|
|
3357
|
+
}
|
|
3358
|
+
loaded = onDisk;
|
|
3359
|
+
} finally {
|
|
3360
|
+
await release();
|
|
3361
|
+
}
|
|
3362
|
+
} catch {
|
|
3363
|
+
dirty = true;
|
|
3364
|
+
}
|
|
3365
|
+
}
|
|
3366
|
+
|
|
3239
3367
|
// src/providers/codex.ts
|
|
3240
3368
|
init_fs_utils();
|
|
3241
3369
|
init_models();
|
|
3242
|
-
import { readdir as readdir5, stat as stat6 } from "fs/promises";
|
|
3243
|
-
import { basename as basename3, join as join10 } from "path";
|
|
3244
|
-
import { homedir as homedir5 } from "os";
|
|
3245
3370
|
var modelDisplayNames = {
|
|
3246
3371
|
"gpt-5.5": "GPT-5.5",
|
|
3247
3372
|
"gpt-5.4": "GPT-5.4",
|
|
@@ -3265,7 +3390,7 @@ var toolNameMap = {
|
|
|
3265
3390
|
read_dir: "Glob"
|
|
3266
3391
|
};
|
|
3267
3392
|
function getCodexDir(override) {
|
|
3268
|
-
return override ?? process.env["CODEX_HOME"] ??
|
|
3393
|
+
return override ?? process.env["CODEX_HOME"] ?? join11(homedir5(), ".codex");
|
|
3269
3394
|
}
|
|
3270
3395
|
function sanitizeProject(cwd) {
|
|
3271
3396
|
return cwd.replace(/^\//, "").replace(/\//g, "-");
|
|
@@ -3279,14 +3404,14 @@ async function readFirstLine(filePath) {
|
|
|
3279
3404
|
return null;
|
|
3280
3405
|
}
|
|
3281
3406
|
}
|
|
3282
|
-
async function
|
|
3407
|
+
async function readCodexHeader(filePath) {
|
|
3283
3408
|
const entry = await readFirstLine(filePath);
|
|
3284
|
-
|
|
3285
|
-
|
|
3286
|
-
return {
|
|
3409
|
+
const valid = entry?.type === "session_meta" && typeof entry.payload?.originator === "string" && entry.payload.originator.toLowerCase().startsWith("codex");
|
|
3410
|
+
if (!valid) return null;
|
|
3411
|
+
return { project: sanitizeProject(entry.payload?.cwd ?? "unknown") };
|
|
3287
3412
|
}
|
|
3288
3413
|
async function discoverSessionsInDir(codexDir) {
|
|
3289
|
-
const sessionsDir =
|
|
3414
|
+
const sessionsDir = join11(codexDir, "sessions");
|
|
3290
3415
|
const sources = [];
|
|
3291
3416
|
let years;
|
|
3292
3417
|
try {
|
|
@@ -3296,25 +3421,24 @@ async function discoverSessionsInDir(codexDir) {
|
|
|
3296
3421
|
}
|
|
3297
3422
|
for (const year of years) {
|
|
3298
3423
|
if (!/^\d{4}$/.test(year)) continue;
|
|
3299
|
-
const yearDir =
|
|
3424
|
+
const yearDir = join11(sessionsDir, year);
|
|
3300
3425
|
const months = await readdir5(yearDir).catch(() => []);
|
|
3301
3426
|
for (const month of months) {
|
|
3302
3427
|
if (!/^\d{2}$/.test(month)) continue;
|
|
3303
|
-
const monthDir =
|
|
3428
|
+
const monthDir = join11(yearDir, month);
|
|
3304
3429
|
const days = await readdir5(monthDir).catch(() => []);
|
|
3305
3430
|
for (const day of days) {
|
|
3306
3431
|
if (!/^\d{2}$/.test(day)) continue;
|
|
3307
|
-
const dayDir =
|
|
3432
|
+
const dayDir = join11(monthDir, day);
|
|
3308
3433
|
const files = await readdir5(dayDir).catch(() => []);
|
|
3309
3434
|
for (const file of files) {
|
|
3310
3435
|
if (!file.startsWith("rollout-") || !file.endsWith(".jsonl")) continue;
|
|
3311
|
-
const filePath =
|
|
3312
|
-
const s = await
|
|
3436
|
+
const filePath = join11(dayDir, file);
|
|
3437
|
+
const s = await stat7(filePath).catch(() => null);
|
|
3313
3438
|
if (!s?.isFile()) continue;
|
|
3314
|
-
const
|
|
3315
|
-
if (!
|
|
3316
|
-
|
|
3317
|
-
sources.push({ path: filePath, project: sanitizeProject(cwd), provider: "codex" });
|
|
3439
|
+
const header = await cachedDiscoveryHeader("codex", filePath, s, () => readCodexHeader(filePath));
|
|
3440
|
+
if (!header) continue;
|
|
3441
|
+
sources.push({ path: filePath, project: header.project, provider: "codex" });
|
|
3318
3442
|
}
|
|
3319
3443
|
}
|
|
3320
3444
|
}
|
|
@@ -3471,7 +3595,11 @@ function createCodexProvider(codexDir) {
|
|
|
3471
3595
|
return toolNameMap[rawTool] ?? rawTool;
|
|
3472
3596
|
},
|
|
3473
3597
|
async discoverSessions() {
|
|
3474
|
-
|
|
3598
|
+
try {
|
|
3599
|
+
return await discoverSessionsInDir(dir);
|
|
3600
|
+
} finally {
|
|
3601
|
+
await flushDiscoveryCache();
|
|
3602
|
+
}
|
|
3475
3603
|
},
|
|
3476
3604
|
createSessionParser(source, seenKeys) {
|
|
3477
3605
|
return createParser(source, seenKeys);
|
|
@@ -3483,8 +3611,8 @@ var codex = createCodexProvider();
|
|
|
3483
3611
|
// src/providers/copilot.ts
|
|
3484
3612
|
init_fs_utils();
|
|
3485
3613
|
init_models();
|
|
3486
|
-
import { readdir as readdir6, stat as
|
|
3487
|
-
import { basename as basename4, dirname as dirname3, join as
|
|
3614
|
+
import { readdir as readdir6, stat as stat8 } from "fs/promises";
|
|
3615
|
+
import { basename as basename4, dirname as dirname3, join as join12 } from "path";
|
|
3488
3616
|
import { homedir as homedir6 } from "os";
|
|
3489
3617
|
var modelDisplayNames2 = {
|
|
3490
3618
|
"gpt-4.1-nano": "GPT-4.1 Nano",
|
|
@@ -3517,7 +3645,7 @@ var toolNameMap2 = {
|
|
|
3517
3645
|
};
|
|
3518
3646
|
var modelDisplayEntries2 = Object.entries(modelDisplayNames2).sort((a, b) => b[0].length - a[0].length);
|
|
3519
3647
|
function getCopilotSessionStateDir(override) {
|
|
3520
|
-
return override ??
|
|
3648
|
+
return override ?? join12(homedir6(), ".copilot", "session-state");
|
|
3521
3649
|
}
|
|
3522
3650
|
function parseCwd(yaml) {
|
|
3523
3651
|
const match = yaml.match(/^cwd:\s*(.+)$/m);
|
|
@@ -3592,11 +3720,11 @@ async function discoverSessionsInDir2(sessionStateDir) {
|
|
|
3592
3720
|
return sources;
|
|
3593
3721
|
}
|
|
3594
3722
|
for (const sessionId of sessionDirs) {
|
|
3595
|
-
const eventsPath =
|
|
3596
|
-
const s = await
|
|
3723
|
+
const eventsPath = join12(sessionStateDir, sessionId, "events.jsonl");
|
|
3724
|
+
const s = await stat8(eventsPath).catch(() => null);
|
|
3597
3725
|
if (!s?.isFile()) continue;
|
|
3598
3726
|
let project = sessionId;
|
|
3599
|
-
const yaml = await readSessionFile(
|
|
3727
|
+
const yaml = await readSessionFile(join12(sessionStateDir, sessionId, "workspace.yaml"));
|
|
3600
3728
|
if (yaml !== null) {
|
|
3601
3729
|
const cwd = parseCwd(yaml);
|
|
3602
3730
|
if (cwd) project = basename4(cwd);
|
|
@@ -3633,8 +3761,8 @@ var copilot = createCopilotProvider();
|
|
|
3633
3761
|
init_fs_utils();
|
|
3634
3762
|
init_models();
|
|
3635
3763
|
init_bash_utils();
|
|
3636
|
-
import { readdir as readdir7, stat as
|
|
3637
|
-
import { basename as basename6, join as
|
|
3764
|
+
import { readdir as readdir7, stat as stat9 } from "fs/promises";
|
|
3765
|
+
import { basename as basename6, join as join13 } from "path";
|
|
3638
3766
|
import { homedir as homedir7 } from "os";
|
|
3639
3767
|
var modelDisplayNames3 = {
|
|
3640
3768
|
"gpt-5.4": "GPT-5.4",
|
|
@@ -3659,10 +3787,10 @@ var toolNameMap3 = {
|
|
|
3659
3787
|
};
|
|
3660
3788
|
var modelDisplayEntries3 = Object.entries(modelDisplayNames3).sort((a, b) => b[0].length - a[0].length);
|
|
3661
3789
|
function getPiSessionsDir(override) {
|
|
3662
|
-
return override ??
|
|
3790
|
+
return override ?? join13(homedir7(), ".pi", "agent", "sessions");
|
|
3663
3791
|
}
|
|
3664
3792
|
function getOmpSessionsDir(override) {
|
|
3665
|
-
return override ??
|
|
3793
|
+
return override ?? join13(homedir7(), ".omp", "agent", "sessions");
|
|
3666
3794
|
}
|
|
3667
3795
|
async function readFirstEntry(filePath) {
|
|
3668
3796
|
const line = await readSessionFirstLine(filePath);
|
|
@@ -3682,8 +3810,8 @@ async function discoverSessionsInDir3(sessionsDir, providerName) {
|
|
|
3682
3810
|
return sources;
|
|
3683
3811
|
}
|
|
3684
3812
|
for (const dirName of projectDirs) {
|
|
3685
|
-
const dirPath =
|
|
3686
|
-
const dirStat = await
|
|
3813
|
+
const dirPath = join13(sessionsDir, dirName);
|
|
3814
|
+
const dirStat = await stat9(dirPath).catch(() => null);
|
|
3687
3815
|
if (!dirStat?.isDirectory()) continue;
|
|
3688
3816
|
let files;
|
|
3689
3817
|
try {
|
|
@@ -3693,8 +3821,8 @@ async function discoverSessionsInDir3(sessionsDir, providerName) {
|
|
|
3693
3821
|
}
|
|
3694
3822
|
for (const file of files) {
|
|
3695
3823
|
if (!file.endsWith(".jsonl")) continue;
|
|
3696
|
-
const filePath =
|
|
3697
|
-
const fileStat = await
|
|
3824
|
+
const filePath = join13(dirPath, file);
|
|
3825
|
+
const fileStat = await stat9(filePath).catch(() => null);
|
|
3698
3826
|
if (!fileStat?.isFile()) continue;
|
|
3699
3827
|
const first = await readFirstEntry(filePath);
|
|
3700
3828
|
if (!first || first.type !== "session") continue;
|
|
@@ -4056,10 +4184,10 @@ function classifyTurn(turn) {
|
|
|
4056
4184
|
|
|
4057
4185
|
// src/session-index.ts
|
|
4058
4186
|
init_cache_dir();
|
|
4059
|
-
import { stat as
|
|
4187
|
+
import { stat as stat12 } from "fs/promises";
|
|
4060
4188
|
import { existsSync as existsSync3, unlinkSync } from "fs";
|
|
4061
|
-
import { readFile as
|
|
4062
|
-
import { join as
|
|
4189
|
+
import { readFile as readFile10, mkdir as mkdir10, rename as rename8, unlink as unlink6, open as open7 } from "fs/promises";
|
|
4190
|
+
import { join as join18 } from "path";
|
|
4063
4191
|
import { randomBytes as randomBytes5 } from "crypto";
|
|
4064
4192
|
var SESSION_INDEX_VERSION = 1;
|
|
4065
4193
|
var INDEX_FILENAME = "session-index.json";
|
|
@@ -4068,12 +4196,12 @@ function emptyIndex() {
|
|
|
4068
4196
|
return { v: SESSION_INDEX_VERSION, e: {} };
|
|
4069
4197
|
}
|
|
4070
4198
|
function indexPath() {
|
|
4071
|
-
return
|
|
4199
|
+
return join18(getCacheDir(), INDEX_FILENAME);
|
|
4072
4200
|
}
|
|
4073
4201
|
async function loadSessionIndex() {
|
|
4074
4202
|
if (currentIndex) return currentIndex;
|
|
4075
4203
|
try {
|
|
4076
|
-
const raw = await
|
|
4204
|
+
const raw = await readFile10(indexPath(), "utf-8");
|
|
4077
4205
|
const parsed = JSON.parse(raw);
|
|
4078
4206
|
if (parsed.v !== SESSION_INDEX_VERSION) {
|
|
4079
4207
|
currentIndex = emptyIndex();
|
|
@@ -4088,7 +4216,7 @@ async function loadSessionIndex() {
|
|
|
4088
4216
|
}
|
|
4089
4217
|
async function saveSessionIndex(index) {
|
|
4090
4218
|
const dir = getCacheDir();
|
|
4091
|
-
if (!existsSync3(dir)) await
|
|
4219
|
+
if (!existsSync3(dir)) await mkdir10(dir, { recursive: true });
|
|
4092
4220
|
const finalPath = indexPath();
|
|
4093
4221
|
const tempPath = `${finalPath}.${randomBytes5(8).toString("hex")}.tmp`;
|
|
4094
4222
|
const payload = JSON.stringify(index);
|
|
@@ -4100,10 +4228,10 @@ async function saveSessionIndex(index) {
|
|
|
4100
4228
|
await handle.close();
|
|
4101
4229
|
}
|
|
4102
4230
|
try {
|
|
4103
|
-
await
|
|
4231
|
+
await rename8(tempPath, finalPath);
|
|
4104
4232
|
} catch (err) {
|
|
4105
4233
|
try {
|
|
4106
|
-
await
|
|
4234
|
+
await unlink6(tempPath);
|
|
4107
4235
|
} catch {
|
|
4108
4236
|
}
|
|
4109
4237
|
throw err;
|
|
@@ -4115,7 +4243,7 @@ async function checkSessionFile(filePath, index, dateRange) {
|
|
|
4115
4243
|
if (!entry) return "parse";
|
|
4116
4244
|
let fileStat;
|
|
4117
4245
|
try {
|
|
4118
|
-
fileStat = await
|
|
4246
|
+
fileStat = await stat12(filePath);
|
|
4119
4247
|
} catch {
|
|
4120
4248
|
return "parse";
|
|
4121
4249
|
}
|
|
@@ -4369,7 +4497,7 @@ function buildSessionSummary(sessionId, project, turns) {
|
|
|
4369
4497
|
async function parseSessionFile(filePath, project, seenMsgIds, dateRange) {
|
|
4370
4498
|
if (dateRange) {
|
|
4371
4499
|
try {
|
|
4372
|
-
const s = await
|
|
4500
|
+
const s = await stat13(filePath);
|
|
4373
4501
|
if (s.mtimeMs < dateRange.start.getTime()) return null;
|
|
4374
4502
|
} catch {
|
|
4375
4503
|
}
|
|
@@ -4401,13 +4529,13 @@ async function parseSessionFile(filePath, project, seenMsgIds, dateRange) {
|
|
|
4401
4529
|
}
|
|
4402
4530
|
async function collectJsonlFiles2(dirPath) {
|
|
4403
4531
|
const files = await readdir10(dirPath).catch(() => []);
|
|
4404
|
-
const jsonlFiles = files.filter((f) => f.endsWith(".jsonl")).map((f) =>
|
|
4532
|
+
const jsonlFiles = files.filter((f) => f.endsWith(".jsonl")).map((f) => join19(dirPath, f));
|
|
4405
4533
|
for (const entry of files) {
|
|
4406
4534
|
if (entry.endsWith(".jsonl")) continue;
|
|
4407
|
-
const subagentsPath =
|
|
4535
|
+
const subagentsPath = join19(dirPath, entry, "subagents");
|
|
4408
4536
|
const subFiles = await readdir10(subagentsPath).catch(() => []);
|
|
4409
4537
|
for (const sf of subFiles) {
|
|
4410
|
-
if (sf.endsWith(".jsonl")) jsonlFiles.push(
|
|
4538
|
+
if (sf.endsWith(".jsonl")) jsonlFiles.push(join19(subagentsPath, sf));
|
|
4411
4539
|
}
|
|
4412
4540
|
}
|
|
4413
4541
|
return jsonlFiles;
|
|
@@ -4425,7 +4553,7 @@ async function scanProjectDirs(dirs, seenMsgIds, dateRange) {
|
|
|
4425
4553
|
if (action === "skip") continue;
|
|
4426
4554
|
let preStat = null;
|
|
4427
4555
|
try {
|
|
4428
|
-
const s = await
|
|
4556
|
+
const s = await stat13(filePath);
|
|
4429
4557
|
preStat = { size: s.size, mtimeMs: s.mtimeMs };
|
|
4430
4558
|
} catch {
|
|
4431
4559
|
}
|
|
@@ -4433,7 +4561,7 @@ async function scanProjectDirs(dirs, seenMsgIds, dateRange) {
|
|
|
4433
4561
|
const hasApiCalls = session !== null && session.apiCalls > 0;
|
|
4434
4562
|
if (preStat && (hasApiCalls || !dateRange)) {
|
|
4435
4563
|
try {
|
|
4436
|
-
const postStat = await
|
|
4564
|
+
const postStat = await stat13(filePath);
|
|
4437
4565
|
if (postStat.size === preStat.size && postStat.mtimeMs === preStat.mtimeMs) {
|
|
4438
4566
|
indexDirty = recordParseResult(filePath, sessionIndex, preStat.size, preStat.mtimeMs, hasApiCalls) || indexDirty;
|
|
4439
4567
|
}
|
|
@@ -4501,8 +4629,8 @@ async function parseProviderSources(providerName, sources, seenKeys, dateRange)
|
|
|
4501
4629
|
for (const source of sources) {
|
|
4502
4630
|
if (dateRange) {
|
|
4503
4631
|
try {
|
|
4504
|
-
const s = await
|
|
4505
|
-
const wal = await
|
|
4632
|
+
const s = await stat13(source.path);
|
|
4633
|
+
const wal = await stat13(`${source.path}-wal`).catch(() => null);
|
|
4506
4634
|
if (Math.max(s.mtimeMs, wal?.mtimeMs ?? 0) < dateRange.start.getTime()) continue;
|
|
4507
4635
|
} catch {
|
|
4508
4636
|
}
|
|
@@ -4757,12 +4885,12 @@ function renderStatusBar(projects) {
|
|
|
4757
4885
|
|
|
4758
4886
|
// src/menubar-json.ts
|
|
4759
4887
|
import { readdirSync, readFileSync } from "fs";
|
|
4760
|
-
import { join as
|
|
4888
|
+
import { join as join20, dirname as dirname4 } from "path";
|
|
4761
4889
|
init_models();
|
|
4762
4890
|
import { fileURLToPath } from "url";
|
|
4763
4891
|
function getCliVersion() {
|
|
4764
4892
|
try {
|
|
4765
|
-
const pkgPath =
|
|
4893
|
+
const pkgPath = join20(dirname4(fileURLToPath(import.meta.url)), "..", "package.json");
|
|
4766
4894
|
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
4767
4895
|
return pkg.version ?? "0.0.0";
|
|
4768
4896
|
} catch {
|
|
@@ -4917,16 +5045,16 @@ function buildMenubarPayload(current, providers, optimize, dailyHistory, agentSt
|
|
|
4917
5045
|
init_cache_dir();
|
|
4918
5046
|
import { createHash as createHash4, randomBytes as randomBytes6 } from "crypto";
|
|
4919
5047
|
import { existsSync as existsSync4 } from "fs";
|
|
4920
|
-
import { mkdir as
|
|
4921
|
-
import { join as
|
|
5048
|
+
import { mkdir as mkdir11, open as open8, readFile as readFile11, rename as rename9, unlink as unlink7 } from "fs/promises";
|
|
5049
|
+
import { join as join21 } from "path";
|
|
4922
5050
|
var DAILY_CACHE_VERSION = 6;
|
|
4923
5051
|
var DEFAULT_DAILY_CACHE_SCOPE = "global";
|
|
4924
5052
|
var DAILY_CACHE_FILENAME = "daily-cache.json";
|
|
4925
5053
|
function getCachePath3(scopeKey) {
|
|
4926
5054
|
if (scopeKey === DEFAULT_DAILY_CACHE_SCOPE) {
|
|
4927
|
-
return
|
|
5055
|
+
return join21(getCacheDir(), DAILY_CACHE_FILENAME);
|
|
4928
5056
|
}
|
|
4929
|
-
return
|
|
5057
|
+
return join21(getCacheDir(), `daily-cache-${scopeKey}.json`);
|
|
4930
5058
|
}
|
|
4931
5059
|
function emptyCache(scopeKey) {
|
|
4932
5060
|
return { version: DAILY_CACHE_VERSION, scopeKey, lastComputedDate: null, days: [] };
|
|
@@ -4955,7 +5083,7 @@ async function loadDailyCache(scopeKey = DEFAULT_DAILY_CACHE_SCOPE) {
|
|
|
4955
5083
|
const path = getCachePath3(scopeKey);
|
|
4956
5084
|
if (!existsSync4(path)) return emptyCache(scopeKey);
|
|
4957
5085
|
try {
|
|
4958
|
-
const raw = await
|
|
5086
|
+
const raw = await readFile11(path, "utf-8");
|
|
4959
5087
|
const parsed = JSON.parse(raw);
|
|
4960
5088
|
if (!isValidCache(parsed)) return emptyCache(scopeKey);
|
|
4961
5089
|
if (parsed.scopeKey !== scopeKey) return emptyCache(scopeKey);
|
|
@@ -4966,12 +5094,12 @@ async function loadDailyCache(scopeKey = DEFAULT_DAILY_CACHE_SCOPE) {
|
|
|
4966
5094
|
}
|
|
4967
5095
|
async function saveDailyCache(cache) {
|
|
4968
5096
|
const dir = getCacheDir();
|
|
4969
|
-
if (!existsSync4(dir)) await
|
|
5097
|
+
if (!existsSync4(dir)) await mkdir11(dir, { recursive: true });
|
|
4970
5098
|
const scopeKey = cache.scopeKey || DEFAULT_DAILY_CACHE_SCOPE;
|
|
4971
5099
|
const finalPath = getCachePath3(scopeKey);
|
|
4972
5100
|
const tempPath = `${finalPath}.${randomBytes6(8).toString("hex")}.tmp`;
|
|
4973
5101
|
const payload = JSON.stringify({ ...cache, scopeKey });
|
|
4974
|
-
if (await
|
|
5102
|
+
if (await readFile11(finalPath, "utf8").catch(() => "") === payload) return;
|
|
4975
5103
|
const handle = await open8(tempPath, "w", 384);
|
|
4976
5104
|
try {
|
|
4977
5105
|
await handle.writeFile(payload, { encoding: "utf-8" });
|
|
@@ -4980,10 +5108,10 @@ async function saveDailyCache(cache) {
|
|
|
4980
5108
|
await handle.close();
|
|
4981
5109
|
}
|
|
4982
5110
|
try {
|
|
4983
|
-
await
|
|
5111
|
+
await rename9(tempPath, finalPath);
|
|
4984
5112
|
} catch (err) {
|
|
4985
5113
|
try {
|
|
4986
|
-
await
|
|
5114
|
+
await unlink7(tempPath);
|
|
4987
5115
|
} catch {
|
|
4988
5116
|
}
|
|
4989
5117
|
throw err;
|
|
@@ -5002,7 +5130,9 @@ function addNewDays(cache, incoming, newestDate, options = {}) {
|
|
|
5002
5130
|
scopeKey: cache.scopeKey || DEFAULT_DAILY_CACHE_SCOPE,
|
|
5003
5131
|
lastComputedDate: nextLast,
|
|
5004
5132
|
days: merged,
|
|
5005
|
-
revalidatedAt: cache.revalidatedAt
|
|
5133
|
+
revalidatedAt: cache.revalidatedAt,
|
|
5134
|
+
backfill: cache.backfill,
|
|
5135
|
+
partialDates: cache.partialDates
|
|
5006
5136
|
};
|
|
5007
5137
|
}
|
|
5008
5138
|
function getDaysInRange(cache, start, end) {
|
|
@@ -5028,7 +5158,7 @@ var LatestLoad = class {
|
|
|
5028
5158
|
this.generation++;
|
|
5029
5159
|
this.controller?.abort();
|
|
5030
5160
|
}
|
|
5031
|
-
async run(
|
|
5161
|
+
async run(load2) {
|
|
5032
5162
|
const generation = ++this.generation;
|
|
5033
5163
|
this.controller?.abort();
|
|
5034
5164
|
const previous = this.tail;
|
|
@@ -5040,7 +5170,7 @@ var LatestLoad = class {
|
|
|
5040
5170
|
const controller = new AbortController();
|
|
5041
5171
|
this.controller = controller;
|
|
5042
5172
|
try {
|
|
5043
|
-
const result = await
|
|
5173
|
+
const result = await load2(controller.signal);
|
|
5044
5174
|
return generation === this.generation ? result : void 0;
|
|
5045
5175
|
} catch (error) {
|
|
5046
5176
|
if (generation !== this.generation) return void 0;
|
|
@@ -5068,9 +5198,9 @@ init_models();
|
|
|
5068
5198
|
// src/optimize.ts
|
|
5069
5199
|
init_fs_utils();
|
|
5070
5200
|
import chalk2 from "chalk";
|
|
5071
|
-
import { readdir as readdir11, stat as
|
|
5201
|
+
import { readdir as readdir11, stat as stat14 } from "fs/promises";
|
|
5072
5202
|
import { existsSync as existsSync5, statSync as statSync2 } from "fs";
|
|
5073
|
-
import { basename as basename9, join as
|
|
5203
|
+
import { basename as basename9, join as join22 } from "path";
|
|
5074
5204
|
import { homedir as homedir11 } from "os";
|
|
5075
5205
|
var ORANGE = "#F59E0B";
|
|
5076
5206
|
var DIM = "#666666";
|
|
@@ -5154,13 +5284,13 @@ var DEFAULT_TREND_PERIOD_MS = DEFAULT_TREND_PERIOD_DAYS * 24 * 60 * 60 * 1e3;
|
|
|
5154
5284
|
var IMPROVING_THRESHOLD = 0.5;
|
|
5155
5285
|
async function collectJsonlFiles3(dirPath) {
|
|
5156
5286
|
const files = await readdir11(dirPath).catch(() => []);
|
|
5157
|
-
const result = files.filter((f) => f.endsWith(".jsonl")).map((f) =>
|
|
5287
|
+
const result = files.filter((f) => f.endsWith(".jsonl")).map((f) => join22(dirPath, f));
|
|
5158
5288
|
for (const entry of files) {
|
|
5159
5289
|
if (entry.endsWith(".jsonl")) continue;
|
|
5160
|
-
const subPath =
|
|
5290
|
+
const subPath = join22(dirPath, entry, "subagents");
|
|
5161
5291
|
const subFiles = await readdir11(subPath).catch(() => []);
|
|
5162
5292
|
for (const sf of subFiles) {
|
|
5163
|
-
if (sf.endsWith(".jsonl")) result.push(
|
|
5293
|
+
if (sf.endsWith(".jsonl")) result.push(join22(subPath, sf));
|
|
5164
5294
|
}
|
|
5165
5295
|
}
|
|
5166
5296
|
return result;
|
|
@@ -5168,7 +5298,7 @@ async function collectJsonlFiles3(dirPath) {
|
|
|
5168
5298
|
async function isFileStaleForRange(filePath, range) {
|
|
5169
5299
|
if (!range) return false;
|
|
5170
5300
|
try {
|
|
5171
|
-
const s = await
|
|
5301
|
+
const s = await stat14(filePath);
|
|
5172
5302
|
return s.mtimeMs < range.start.getTime();
|
|
5173
5303
|
} catch {
|
|
5174
5304
|
return false;
|
|
@@ -5298,13 +5428,13 @@ function isReadTool(name) {
|
|
|
5298
5428
|
function loadMcpConfigs(projectCwds) {
|
|
5299
5429
|
const servers = /* @__PURE__ */ new Map();
|
|
5300
5430
|
const configPaths = [
|
|
5301
|
-
|
|
5302
|
-
|
|
5431
|
+
join22(homedir11(), ".claude", "settings.json"),
|
|
5432
|
+
join22(homedir11(), ".claude", "settings.local.json")
|
|
5303
5433
|
];
|
|
5304
5434
|
for (const cwd of projectCwds) {
|
|
5305
|
-
configPaths.push(
|
|
5306
|
-
configPaths.push(
|
|
5307
|
-
configPaths.push(
|
|
5435
|
+
configPaths.push(join22(cwd, ".mcp.json"));
|
|
5436
|
+
configPaths.push(join22(cwd, ".claude", "settings.json"));
|
|
5437
|
+
configPaths.push(join22(cwd, ".claude", "settings.local.json"));
|
|
5308
5438
|
}
|
|
5309
5439
|
for (const p of configPaths) {
|
|
5310
5440
|
if (!existsSync5(p)) continue;
|
|
@@ -5457,12 +5587,12 @@ function expandImports(filePath, seen, depth) {
|
|
|
5457
5587
|
if (content === null) return { totalLines: 0, importedFiles: 0 };
|
|
5458
5588
|
let totalLines = content.split("\n").length;
|
|
5459
5589
|
let importedFiles = 0;
|
|
5460
|
-
const dir =
|
|
5590
|
+
const dir = join22(filePath, "..");
|
|
5461
5591
|
IMPORT_PATTERN.lastIndex = 0;
|
|
5462
5592
|
for (const match of content.matchAll(IMPORT_PATTERN)) {
|
|
5463
5593
|
const rawPath = match[1];
|
|
5464
5594
|
if (!rawPath) continue;
|
|
5465
|
-
const resolved = rawPath.startsWith("/") ? rawPath :
|
|
5595
|
+
const resolved = rawPath.startsWith("/") ? rawPath : join22(dir, rawPath);
|
|
5466
5596
|
if (!existsSync5(resolved)) continue;
|
|
5467
5597
|
const nested = expandImports(resolved, seen, depth + 1);
|
|
5468
5598
|
totalLines += nested.totalLines;
|
|
@@ -5474,7 +5604,7 @@ function detectBloatedClaudeMd(projectCwds) {
|
|
|
5474
5604
|
const bloated = [];
|
|
5475
5605
|
for (const cwd of projectCwds) {
|
|
5476
5606
|
for (const name of ["CLAUDE.md", ".claude/CLAUDE.md"]) {
|
|
5477
|
-
const fullPath =
|
|
5607
|
+
const fullPath = join22(cwd, name);
|
|
5478
5608
|
if (!existsSync5(fullPath)) continue;
|
|
5479
5609
|
const { totalLines, importedFiles } = expandImports(fullPath, /* @__PURE__ */ new Set(), 0);
|
|
5480
5610
|
if (totalLines > CLAUDEMD_HEALTHY_LINES) {
|
|
@@ -5617,7 +5747,7 @@ async function listSkillDirs(dir) {
|
|
|
5617
5747
|
const entries = await readdir11(dir);
|
|
5618
5748
|
const names = [];
|
|
5619
5749
|
for (const entry of entries) {
|
|
5620
|
-
if (existsSync5(
|
|
5750
|
+
if (existsSync5(join22(dir, entry, "SKILL.md"))) names.push(entry);
|
|
5621
5751
|
}
|
|
5622
5752
|
return names;
|
|
5623
5753
|
} catch {
|
|
@@ -5625,7 +5755,7 @@ async function listSkillDirs(dir) {
|
|
|
5625
5755
|
}
|
|
5626
5756
|
}
|
|
5627
5757
|
async function detectGhostAgents(calls) {
|
|
5628
|
-
const defined = await listMarkdownFiles(
|
|
5758
|
+
const defined = await listMarkdownFiles(join22(homedir11(), ".claude", "agents"));
|
|
5629
5759
|
if (defined.length === 0) return null;
|
|
5630
5760
|
const invoked = /* @__PURE__ */ new Set();
|
|
5631
5761
|
for (const call of calls) {
|
|
@@ -5650,7 +5780,7 @@ async function detectGhostAgents(calls) {
|
|
|
5650
5780
|
};
|
|
5651
5781
|
}
|
|
5652
5782
|
async function detectGhostSkills(calls) {
|
|
5653
|
-
const defined = await listSkillDirs(
|
|
5783
|
+
const defined = await listSkillDirs(join22(homedir11(), ".claude", "skills"));
|
|
5654
5784
|
if (defined.length === 0) return null;
|
|
5655
5785
|
const invoked = /* @__PURE__ */ new Set();
|
|
5656
5786
|
for (const call of calls) {
|
|
@@ -5675,7 +5805,7 @@ async function detectGhostSkills(calls) {
|
|
|
5675
5805
|
};
|
|
5676
5806
|
}
|
|
5677
5807
|
async function detectGhostCommands(userMessages) {
|
|
5678
|
-
const defined = await listMarkdownFiles(
|
|
5808
|
+
const defined = await listMarkdownFiles(join22(homedir11(), ".claude", "commands"));
|
|
5679
5809
|
if (defined.length === 0) return null;
|
|
5680
5810
|
const invoked = /* @__PURE__ */ new Set();
|
|
5681
5811
|
for (const msg of userMessages) {
|
|
@@ -5703,7 +5833,7 @@ async function detectGhostCommands(userMessages) {
|
|
|
5703
5833
|
}
|
|
5704
5834
|
function readShellProfileLimit() {
|
|
5705
5835
|
for (const profile of SHELL_PROFILES) {
|
|
5706
|
-
const path =
|
|
5836
|
+
const path = join22(homedir11(), profile);
|
|
5707
5837
|
if (!existsSync5(path)) continue;
|
|
5708
5838
|
const content = readSessionFileSync(path);
|
|
5709
5839
|
if (content === null) continue;
|
|
@@ -5927,7 +6057,7 @@ async function runOptimize(projects, periodLabel, dateRange, preScanned) {
|
|
|
5927
6057
|
init_fs_utils();
|
|
5928
6058
|
import { readdir as readdir12 } from "fs/promises";
|
|
5929
6059
|
import { existsSync as existsSync6 } from "fs";
|
|
5930
|
-
import { join as
|
|
6060
|
+
import { join as join23 } from "path";
|
|
5931
6061
|
import { homedir as homedir12 } from "os";
|
|
5932
6062
|
var CHARS_PER_TOKEN2 = 4;
|
|
5933
6063
|
var SYSTEM_BASE_TOKENS = 10400;
|
|
@@ -5949,13 +6079,13 @@ async function readConfigFile(path) {
|
|
|
5949
6079
|
async function countMcpTools(projectPath) {
|
|
5950
6080
|
const home = homedir12();
|
|
5951
6081
|
const configPaths = [
|
|
5952
|
-
|
|
5953
|
-
|
|
6082
|
+
join23(home, ".claude", "settings.json"),
|
|
6083
|
+
join23(home, ".claude", "settings.local.json")
|
|
5954
6084
|
];
|
|
5955
6085
|
if (projectPath) {
|
|
5956
|
-
configPaths.push(
|
|
5957
|
-
configPaths.push(
|
|
5958
|
-
configPaths.push(
|
|
6086
|
+
configPaths.push(join23(projectPath, ".mcp.json"));
|
|
6087
|
+
configPaths.push(join23(projectPath, ".claude", "settings.json"));
|
|
6088
|
+
configPaths.push(join23(projectPath, ".claude", "settings.local.json"));
|
|
5959
6089
|
}
|
|
5960
6090
|
const servers = /* @__PURE__ */ new Set();
|
|
5961
6091
|
let toolCount = 0;
|
|
@@ -5972,15 +6102,15 @@ async function countMcpTools(projectPath) {
|
|
|
5972
6102
|
return toolCount;
|
|
5973
6103
|
}
|
|
5974
6104
|
async function countSkills(projectPath) {
|
|
5975
|
-
const dirs = [
|
|
5976
|
-
if (projectPath) dirs.push(
|
|
6105
|
+
const dirs = [join23(homedir12(), ".claude", "skills")];
|
|
6106
|
+
if (projectPath) dirs.push(join23(projectPath, ".claude", "skills"));
|
|
5977
6107
|
let count = 0;
|
|
5978
6108
|
for (const dir of dirs) {
|
|
5979
6109
|
if (!existsSync6(dir)) continue;
|
|
5980
6110
|
try {
|
|
5981
6111
|
const entries = await readdir12(dir);
|
|
5982
6112
|
for (const entry of entries) {
|
|
5983
|
-
const skillFile =
|
|
6113
|
+
const skillFile = join23(dir, entry, "SKILL.md");
|
|
5984
6114
|
if (existsSync6(skillFile)) count++;
|
|
5985
6115
|
}
|
|
5986
6116
|
} catch {
|
|
@@ -5993,12 +6123,12 @@ async function scanMemoryFiles(projectPath) {
|
|
|
5993
6123
|
const home = homedir12();
|
|
5994
6124
|
const files = [];
|
|
5995
6125
|
const paths = [
|
|
5996
|
-
{ path:
|
|
6126
|
+
{ path: join23(home, ".claude", "CLAUDE.md"), name: "~/.claude/CLAUDE.md" }
|
|
5997
6127
|
];
|
|
5998
6128
|
if (projectPath) {
|
|
5999
|
-
paths.push({ path:
|
|
6000
|
-
paths.push({ path:
|
|
6001
|
-
paths.push({ path:
|
|
6129
|
+
paths.push({ path: join23(projectPath, "CLAUDE.md"), name: "CLAUDE.md" });
|
|
6130
|
+
paths.push({ path: join23(projectPath, ".claude", "CLAUDE.md"), name: ".claude/CLAUDE.md" });
|
|
6131
|
+
paths.push({ path: join23(projectPath, "CLAUDE.local.md"), name: "CLAUDE.local.md" });
|
|
6002
6132
|
}
|
|
6003
6133
|
for (const { path, name } of paths) {
|
|
6004
6134
|
if (!existsSync6(path)) continue;
|
|
@@ -6035,7 +6165,7 @@ async function discoverProjectCwd(sessionDir) {
|
|
|
6035
6165
|
if (files.length === 0) return null;
|
|
6036
6166
|
const firstFile = files[0];
|
|
6037
6167
|
if (!firstFile) return null;
|
|
6038
|
-
const content = await readSessionFile(
|
|
6168
|
+
const content = await readSessionFile(join23(sessionDir, firstFile));
|
|
6039
6169
|
if (content === null) return null;
|
|
6040
6170
|
for (const line of content.split("\n")) {
|
|
6041
6171
|
if (!line.trim()) continue;
|
|
@@ -6632,7 +6762,7 @@ function planDisplayName(id) {
|
|
|
6632
6762
|
}
|
|
6633
6763
|
|
|
6634
6764
|
// src/dashboard.tsx
|
|
6635
|
-
import { join as
|
|
6765
|
+
import { join as join24 } from "path";
|
|
6636
6766
|
import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
6637
6767
|
var PERIODS = ["today", "week", "30days", "month", "all"];
|
|
6638
6768
|
var PERIOD_LABELS = {
|
|
@@ -7388,11 +7518,11 @@ function InteractiveDashboard({ initialPeriod, initialProvider, refreshSeconds,
|
|
|
7388
7518
|
useEffect2(() => {
|
|
7389
7519
|
let cancelled = false;
|
|
7390
7520
|
async function loadBudgets() {
|
|
7391
|
-
const claudeDir =
|
|
7521
|
+
const claudeDir = join24(homedir13(), ".claude", "projects");
|
|
7392
7522
|
const budgets = /* @__PURE__ */ new Map();
|
|
7393
7523
|
for (const project of projects.slice(0, 8)) {
|
|
7394
7524
|
if (cancelled) return;
|
|
7395
|
-
const cwd = await discoverProjectCwd(
|
|
7525
|
+
const cwd = await discoverProjectCwd(join24(claudeDir, project.project));
|
|
7396
7526
|
if (!cwd) continue;
|
|
7397
7527
|
budgets.set(project.project, await estimateContextBudget(cwd));
|
|
7398
7528
|
}
|
|
@@ -7670,6 +7800,29 @@ function computeProgressiveBackfillStart({
|
|
|
7670
7800
|
}
|
|
7671
7801
|
return gapStart;
|
|
7672
7802
|
}
|
|
7803
|
+
var DEFAULT_BACKFILL_CHUNK_DAYS = 1;
|
|
7804
|
+
var MAX_BLOCKED_CHUNK_ATTEMPTS = 2;
|
|
7805
|
+
function planBackfillChunks(start, end, chunkDays = DEFAULT_BACKFILL_CHUNK_DAYS) {
|
|
7806
|
+
const size = Math.max(1, Math.floor(chunkDays));
|
|
7807
|
+
const chunks = [];
|
|
7808
|
+
if (start.getTime() > end.getTime()) return chunks;
|
|
7809
|
+
let cursor2 = new Date(start.getFullYear(), start.getMonth(), start.getDate());
|
|
7810
|
+
if (cursor2.getTime() < start.getTime()) cursor2 = new Date(start.getTime());
|
|
7811
|
+
while (cursor2.getTime() <= end.getTime()) {
|
|
7812
|
+
const dayStart = new Date(cursor2.getFullYear(), cursor2.getMonth(), cursor2.getDate());
|
|
7813
|
+
const lastDay = new Date(dayStart.getFullYear(), dayStart.getMonth(), dayStart.getDate() + size - 1);
|
|
7814
|
+
const chunkEnd = new Date(lastDay.getFullYear(), lastDay.getMonth(), lastDay.getDate() + 1).getTime() - 1;
|
|
7815
|
+
chunks.push({ start: new Date(cursor2.getTime()), end: new Date(Math.min(chunkEnd, end.getTime())) });
|
|
7816
|
+
cursor2 = new Date(dayStart.getFullYear(), dayStart.getMonth(), dayStart.getDate() + size);
|
|
7817
|
+
}
|
|
7818
|
+
return chunks;
|
|
7819
|
+
}
|
|
7820
|
+
function nextBackfillCursor(prev, date) {
|
|
7821
|
+
return prev && prev.blockedDate === date ? { blockedDate: date, attempts: prev.attempts + 1 } : { blockedDate: date, attempts: 1 };
|
|
7822
|
+
}
|
|
7823
|
+
function shouldSkipBlockedChunk(cursor2, date, maxAttempts = MAX_BLOCKED_CHUNK_ATTEMPTS) {
|
|
7824
|
+
return !!cursor2 && cursor2.blockedDate === date && cursor2.attempts >= maxAttempts;
|
|
7825
|
+
}
|
|
7673
7826
|
|
|
7674
7827
|
// src/cli.ts
|
|
7675
7828
|
import { createInterface } from "readline";
|
|
@@ -7963,6 +8116,49 @@ program.command("status").description("Compact status output (today + week + mon
|
|
|
7963
8116
|
const yesterdayStr = toDateString(new Date(todayStart.getTime() - MS_PER_DAY4));
|
|
7964
8117
|
const isAllProviders = pf === "all";
|
|
7965
8118
|
const dailyCacheScope = buildDailyCacheScopeKey(opts.project, opts.exclude);
|
|
8119
|
+
const backfillWarnings = [];
|
|
8120
|
+
const runChunkedBackfill = async (cache2, start, end, order) => {
|
|
8121
|
+
let c = cache2;
|
|
8122
|
+
const chunks = planBackfillChunks(start, end);
|
|
8123
|
+
if (order === "newest-first") chunks.reverse();
|
|
8124
|
+
for (const chunk of chunks) {
|
|
8125
|
+
const chunkDate = toDateString(chunk.start);
|
|
8126
|
+
const coveredThrough = toDateString(chunk.end);
|
|
8127
|
+
if (shouldSkipBlockedChunk(c.backfill, chunkDate)) {
|
|
8128
|
+
const blanks = fillMissingDays(chunk.start, chunk.end, []);
|
|
8129
|
+
c = addNewDays(c, blanks, yesterdayStr, { coveredThrough });
|
|
8130
|
+
c.partialDates = [.../* @__PURE__ */ new Set([...c.partialDates ?? [], ...blanks.map((d) => d.date)])];
|
|
8131
|
+
c.backfill = null;
|
|
8132
|
+
c.revalidatedAt = Date.now();
|
|
8133
|
+
await saveDailyCache(c);
|
|
8134
|
+
const warn2 = `history for ${chunkDate} exceeded the scan budget ${MAX_BLOCKED_CHUNK_ATTEMPTS} times; recorded as incomplete so newer days can load`;
|
|
8135
|
+
backfillWarnings.push(warn2);
|
|
8136
|
+
process.stderr.write(`[exe-watcher] WARNING: ${warn2}
|
|
8137
|
+
`);
|
|
8138
|
+
continue;
|
|
8139
|
+
}
|
|
8140
|
+
try {
|
|
8141
|
+
const chunkRange = { start: chunk.start, end: chunk.end };
|
|
8142
|
+
const chunkProjects = filterProjectsByName(await parseAllSessions(chunkRange, "all"), opts.project, opts.exclude);
|
|
8143
|
+
const chunkDays = fillMissingDays(chunk.start, chunk.end, aggregateProjectsIntoDays(chunkProjects));
|
|
8144
|
+
c = addNewDays(c, chunkDays, yesterdayStr, { coveredThrough });
|
|
8145
|
+
c.backfill = null;
|
|
8146
|
+
c.revalidatedAt = Date.now();
|
|
8147
|
+
await saveDailyCache(c);
|
|
8148
|
+
} catch (err) {
|
|
8149
|
+
if (!(err instanceof ResourceBudgetError)) throw err;
|
|
8150
|
+
c.backfill = nextBackfillCursor(c.backfill, chunkDate);
|
|
8151
|
+
c.revalidatedAt = Date.now();
|
|
8152
|
+
await saveDailyCache(c);
|
|
8153
|
+
const warn2 = `history backfill paused at ${chunkDate} (attempt ${c.backfill.attempts}): ${err.message}`;
|
|
8154
|
+
backfillWarnings.push(warn2);
|
|
8155
|
+
process.stderr.write(`[exe-watcher] WARNING: ${warn2}
|
|
8156
|
+
`);
|
|
8157
|
+
break;
|
|
8158
|
+
}
|
|
8159
|
+
}
|
|
8160
|
+
return c;
|
|
8161
|
+
};
|
|
7966
8162
|
const cache = await withDailyCacheLock(async () => {
|
|
7967
8163
|
let c = await loadDailyCache(dailyCacheScope);
|
|
7968
8164
|
const hadYesterday = c.days.some((d) => d.date >= yesterdayStr);
|
|
@@ -7982,13 +8178,7 @@ program.command("status").description("Compact status output (today + week + mon
|
|
|
7982
8178
|
});
|
|
7983
8179
|
const gapEnd = oldestCachedDate && effectiveGapStart.getTime() < new Date(oldestCachedDate).getTime() ? new Date(new Date(oldestCachedDate).getTime() - MS_PER_DAY4) : yesterdayEnd;
|
|
7984
8180
|
if (effectiveGapStart.getTime() <= gapEnd.getTime()) {
|
|
7985
|
-
|
|
7986
|
-
const gapProjects = filterProjectsByName(await parseAllSessions(gapRange, "all"), opts.project, opts.exclude);
|
|
7987
|
-
const gapDays = fillMissingDays(gapRange.start, gapRange.end, aggregateProjectsIntoDays(gapProjects));
|
|
7988
|
-
const coveredThrough = gapEnd.getTime() >= yesterdayEnd.getTime() ? yesterdayStr : void 0;
|
|
7989
|
-
c = addNewDays(c, gapDays, yesterdayStr, coveredThrough ? { coveredThrough } : void 0);
|
|
7990
|
-
c.revalidatedAt = Date.now();
|
|
7991
|
-
await saveDailyCache(c);
|
|
8181
|
+
c = await runChunkedBackfill(c, effectiveGapStart, gapEnd, "oldest-first");
|
|
7992
8182
|
}
|
|
7993
8183
|
const updatedOldest = c.days.length > 0 ? c.days[0]?.date ?? null : null;
|
|
7994
8184
|
const neededStart = new Date(todayStart.getTime() - (selectedPeriodHistoryDays - 1) * MS_PER_DAY4);
|
|
@@ -7997,12 +8187,7 @@ program.command("status").description("Compact status output (today + week + mon
|
|
|
7997
8187
|
if (updatedOldest && clampedNeeded.getTime() < new Date(updatedOldest).getTime()) {
|
|
7998
8188
|
const backEnd = new Date(new Date(updatedOldest).getTime() - MS_PER_DAY4);
|
|
7999
8189
|
if (clampedNeeded.getTime() <= backEnd.getTime()) {
|
|
8000
|
-
|
|
8001
|
-
const backProjects = filterProjectsByName(await parseAllSessions(backRange, "all"), opts.project, opts.exclude);
|
|
8002
|
-
const backDays = fillMissingDays(backRange.start, backRange.end, aggregateProjectsIntoDays(backProjects));
|
|
8003
|
-
c = addNewDays(c, backDays, yesterdayStr);
|
|
8004
|
-
c.revalidatedAt = Date.now();
|
|
8005
|
-
await saveDailyCache(c);
|
|
8190
|
+
c = await runChunkedBackfill(c, clampedNeeded, backEnd, "newest-first");
|
|
8006
8191
|
}
|
|
8007
8192
|
}
|
|
8008
8193
|
return c;
|
|
@@ -8010,7 +8195,7 @@ program.command("status").description("Compact status output (today + week + mon
|
|
|
8010
8195
|
let currentData;
|
|
8011
8196
|
const scanRange = periodInfo.range;
|
|
8012
8197
|
const todayRange = { start: todayStart, end: periodInfo.range.end };
|
|
8013
|
-
const warnings = [];
|
|
8198
|
+
const warnings = [...backfillWarnings];
|
|
8014
8199
|
const parseStart = Date.now();
|
|
8015
8200
|
let todayProjects;
|
|
8016
8201
|
let allTodayProjects;
|
|
@@ -8018,7 +8203,6 @@ program.command("status").description("Compact status output (today + week + mon
|
|
|
8018
8203
|
allTodayProjects = await parseAllSessions(todayRange, "all");
|
|
8019
8204
|
todayProjects = fp(allTodayProjects);
|
|
8020
8205
|
} catch (err) {
|
|
8021
|
-
if (err instanceof ResourceBudgetError) throw err;
|
|
8022
8206
|
const msg = err instanceof Error ? err.message : String(err);
|
|
8023
8207
|
const warn2 = `parseAllSessions failed: ${msg}`;
|
|
8024
8208
|
warnings.push(warn2);
|
|
@@ -8127,7 +8311,6 @@ program.command("status").description("Compact status output (today + week + mon
|
|
|
8127
8311
|
const optimizeProjects = isAllProviders && opts.period === "today" ? todayProjects : fp(await parseAllSessions(scanRange, isAllProviders ? "all" : pf));
|
|
8128
8312
|
optimize = await scanAndDetect(optimizeProjects, scanRange);
|
|
8129
8313
|
} catch (err) {
|
|
8130
|
-
if (err instanceof ResourceBudgetError) throw err;
|
|
8131
8314
|
const msg = err instanceof Error ? err.message : String(err);
|
|
8132
8315
|
const warn2 = `optimize parse failed: ${msg}`;
|
|
8133
8316
|
warnings.push(warn2);
|
|
@@ -8136,12 +8319,12 @@ program.command("status").description("Compact status output (today + week + mon
|
|
|
8136
8319
|
optimize = await scanAndDetect(todayProjects, scanRange);
|
|
8137
8320
|
}
|
|
8138
8321
|
}
|
|
8139
|
-
const exeOsDir =
|
|
8322
|
+
const exeOsDir = join25(homedir14(), ".exe-os");
|
|
8140
8323
|
const exeOsDetected = existsSync7(exeOsDir);
|
|
8141
8324
|
let agentStats = null;
|
|
8142
8325
|
let statsFileAge = null;
|
|
8143
8326
|
try {
|
|
8144
|
-
const statsPath =
|
|
8327
|
+
const statsPath = join25(exeOsDir, "agent-stats.json");
|
|
8145
8328
|
if (existsSync7(statsPath)) {
|
|
8146
8329
|
statsFileAge = Math.round((Date.now() - statSync3(statsPath).mtimeMs) / 1e3);
|
|
8147
8330
|
const parsed = JSON.parse(readFileSync2(statsPath, "utf-8"));
|
|
@@ -8490,9 +8673,9 @@ program.command("compare").description("Compare two AI models side-by-side").opt
|
|
|
8490
8673
|
await renderCompare(range, opts.provider);
|
|
8491
8674
|
});
|
|
8492
8675
|
program.command("agent-status").description("Diagnose exe-os agent data pipeline").action(() => {
|
|
8493
|
-
const exeOsDir =
|
|
8494
|
-
const statsPath =
|
|
8495
|
-
const pidPath =
|
|
8676
|
+
const exeOsDir = join25(homedir14(), ".exe-os");
|
|
8677
|
+
const statsPath = join25(exeOsDir, "agent-stats.json");
|
|
8678
|
+
const pidPath = join25(exeOsDir, "exed.pid");
|
|
8496
8679
|
const dirExists = existsSync7(exeOsDir);
|
|
8497
8680
|
const statsExists = dirExists && existsSync7(statsPath);
|
|
8498
8681
|
const pidExists = dirExists && existsSync7(pidPath);
|
|
@@ -8526,7 +8709,7 @@ program.command("agent-status").description("Diagnose exe-os agent data pipeline
|
|
|
8526
8709
|
});
|
|
8527
8710
|
var releaseScan;
|
|
8528
8711
|
program.hook("preAction", async (_root, command) => {
|
|
8529
|
-
if (command.name() === "status") releaseScan = await acquireFileLock(
|
|
8712
|
+
if (command.name() === "status") releaseScan = await acquireFileLock(join25(getCacheDir(), "scan.lock"));
|
|
8530
8713
|
});
|
|
8531
8714
|
program.hook("postAction", async () => {
|
|
8532
8715
|
await releaseScan?.();
|
package/package.json
CHANGED