hillclimb 0.9.13 → 0.9.14

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/main.js CHANGED
@@ -13,8 +13,8 @@ import {
13
13
  } from "./chunk-YBBDXGEY.js";
14
14
 
15
15
  // src/main.ts
16
- import fs28 from "fs";
17
- import path27 from "path";
16
+ import fs29 from "fs";
17
+ import path28 from "path";
18
18
  import * as p6 from "@clack/prompts";
19
19
 
20
20
  // src/commands/init.ts
@@ -2937,9 +2937,9 @@ async function runStatus(args = []) {
2937
2937
  // src/commands/upload.ts
2938
2938
  import { spawn as spawn3 } from "child_process";
2939
2939
  import crypto8 from "crypto";
2940
- import fs21 from "fs";
2941
- import os10 from "os";
2942
- import path20 from "path";
2940
+ import fs22 from "fs";
2941
+ import os11 from "os";
2942
+ import path21 from "path";
2943
2943
 
2944
2944
  // src/agent-pending.ts
2945
2945
  import crypto4 from "crypto";
@@ -16194,21 +16194,163 @@ async function recordDebugLogCompletion(args) {
16194
16194
  // src/git-traces/pending-captures.ts
16195
16195
  import { AsyncLocalStorage } from "async_hooks";
16196
16196
  import crypto7 from "crypto";
16197
- import fs20 from "fs";
16198
- import os9 from "os";
16199
- import path19 from "path";
16197
+ import fs21 from "fs";
16198
+ import os10 from "os";
16199
+ import path20 from "path";
16200
16200
 
16201
16201
  // src/git-traces/git-ops.ts
16202
16202
  import { execFileSync as execFileSync2, spawn as spawn2, spawnSync as spawnSync2 } from "child_process";
16203
- import fs18 from "fs";
16204
- import os7 from "os";
16205
- import path17 from "path";
16203
+ import fs19 from "fs";
16204
+ import os8 from "os";
16205
+ import path18 from "path";
16206
16206
  import { createGzip } from "zlib";
16207
16207
 
16208
- // src/git-traces/object-store-probe.ts
16209
- import { spawnSync } from "child_process";
16208
+ // src/git-traces/git-diagnostics.ts
16210
16209
  import fs17 from "fs";
16210
+ import os7 from "os";
16211
16211
  import path16 from "path";
16212
+ import { Worker as Worker2 } from "worker_threads";
16213
+ var PHASES = /* @__PURE__ */ new Set([
16214
+ "pack-objects/enumerate-objects",
16215
+ "pack-objects/prepare-pack",
16216
+ "pack-objects/write-pack-file",
16217
+ "index/do_read_index",
16218
+ "index/do_write_index"
16219
+ ]);
16220
+ var COUNTERS = /* @__PURE__ */ new Set([
16221
+ "pack-objects/write_pack_file/wrote",
16222
+ "pack-objects/written",
16223
+ "index/read/cache_nr",
16224
+ "index/write/cache_nr"
16225
+ ]);
16226
+ function summarizeGitTrace(trace, partial = false) {
16227
+ const phases = /* @__PURE__ */ new Map();
16228
+ const active = /* @__PURE__ */ new Set();
16229
+ const counts = /* @__PURE__ */ new Map();
16230
+ const text2 = trace?.subarray(0, 1024 * 1024).toString("utf8") ?? "";
16231
+ const lines = text2.split("\n");
16232
+ partial ||= (trace?.length ?? 0) > 1024 * 1024 || lines.length > 4096;
16233
+ for (const line of lines.slice(0, 4096)) {
16234
+ if (!line) continue;
16235
+ if (line.length > 16384) {
16236
+ partial = true;
16237
+ continue;
16238
+ }
16239
+ try {
16240
+ const e = JSON.parse(line);
16241
+ if (!e || typeof e !== "object") continue;
16242
+ const phase = `${e.category}/${e.label}`;
16243
+ if (e.thread === "main" && PHASES.has(phase)) {
16244
+ if (e.event === "region_enter") active.add(phase);
16245
+ if (e.event === "region_leave" && typeof e.t_rel === "number" && Number.isFinite(e.t_rel) && e.t_rel >= 0 && e.t_rel < 86400) {
16246
+ active.delete(phase);
16247
+ phases.set(phase, Math.round(e.t_rel * 1e3));
16248
+ }
16249
+ }
16250
+ const key = `${e.category}/${e.key}`;
16251
+ if (e.event === "data" && COUNTERS.has(key) && /^\d{1,15}$/.test(String(e.value))) {
16252
+ counts.set(key, Number(e.value));
16253
+ }
16254
+ } catch {
16255
+ partial = true;
16256
+ }
16257
+ }
16258
+ return `traceState=${!text2 ? "unavailable" : partial ? "partial" : "available"}, phasesMs=${JSON.stringify(Object.fromEntries(phases))}, activePhases=${JSON.stringify([...active])}, counts=${JSON.stringify(Object.fromEntries(counts))}`;
16259
+ }
16260
+ var SAMPLE = {
16261
+ stop: 0,
16262
+ count: 1,
16263
+ cpuMs: 2,
16264
+ elapsedMs: 3,
16265
+ bytes: 4,
16266
+ growthMs: 5
16267
+ };
16268
+ var TRACE = { ready: 0, length: 1, partial: 2, done: 3 };
16269
+ function bundleBytes(file) {
16270
+ let bytes = 0;
16271
+ if (!file) return bytes;
16272
+ for (const candidate of [file, `${file}.lock`]) {
16273
+ try {
16274
+ bytes = Math.max(bytes, fs17.statSync(candidate).size);
16275
+ } catch {
16276
+ }
16277
+ }
16278
+ return bytes;
16279
+ }
16280
+ function startGitDiagnostics(bundleFile) {
16281
+ const samples = new BigInt64Array(new SharedArrayBuffer(6 * 8));
16282
+ samples[SAMPLE.cpuMs] = -1n;
16283
+ const control = new Int32Array(new SharedArrayBuffer(4 * 4));
16284
+ const trace = Buffer.from(new SharedArrayBuffer(1024 * 1024));
16285
+ let worker;
16286
+ let directory;
16287
+ let socketPath;
16288
+ try {
16289
+ if (process.platform !== "win32") {
16290
+ directory = fs17.mkdtempSync(path16.join(os7.tmpdir(), "hc-git-"));
16291
+ fs17.chmodSync(directory, 448);
16292
+ const candidate = path16.join(directory, "trace.sock");
16293
+ if (Buffer.byteLength(candidate) < 100) socketPath = candidate;
16294
+ }
16295
+ worker = new Worker2(
16296
+ new URL(
16297
+ import.meta.url.endsWith(".ts") ? "./git-diagnostics-worker.ts" : "./git-diagnostics-worker.js",
16298
+ import.meta.url
16299
+ ),
16300
+ {
16301
+ workerData: {
16302
+ samples: samples.buffer,
16303
+ control: control.buffer,
16304
+ trace: trace.buffer,
16305
+ socketPath,
16306
+ parentPid: process.pid,
16307
+ bundleFile,
16308
+ started: Date.now()
16309
+ }
16310
+ }
16311
+ );
16312
+ worker.on("error", () => {
16313
+ });
16314
+ worker.unref();
16315
+ Atomics.wait(control, TRACE.ready, 0, 100);
16316
+ } catch {
16317
+ }
16318
+ return {
16319
+ env: {
16320
+ GIT_TRACE2_EVENT: socketPath && Atomics.load(control, TRACE.ready) === 1 ? `af_unix:stream:${socketPath}` : "0",
16321
+ GIT_TRACE2_EVENT_NESTING: "2",
16322
+ GIT_TRACE2_CONFIG_PARAMS: "",
16323
+ GIT_TRACE2_ENV_VARS: "",
16324
+ GIT_TRACE2_DST_DEBUG: "0"
16325
+ },
16326
+ finish: () => {
16327
+ Atomics.store(samples, SAMPLE.stop, 1n);
16328
+ try {
16329
+ worker?.postMessage("stop");
16330
+ if (worker) Atomics.wait(control, TRACE.done, 0, 100);
16331
+ } catch {
16332
+ }
16333
+ void worker?.terminate().catch(() => {
16334
+ });
16335
+ if (directory) {
16336
+ try {
16337
+ fs17.rmSync(directory, { recursive: true, force: true });
16338
+ } catch {
16339
+ }
16340
+ }
16341
+ const summary = summarizeGitTrace(
16342
+ trace.subarray(0, Atomics.load(control, TRACE.length)),
16343
+ !!Atomics.load(control, TRACE.partial) || !Atomics.load(control, TRACE.done)
16344
+ );
16345
+ return `${summary}, cpuSamples=${Atomics.load(samples, SAMPLE.count)}, sampledCpuMs=${Atomics.load(samples, SAMPLE.cpuMs)}, sampledThroughMs=${Atomics.load(samples, SAMPLE.elapsedMs)}${bundleFile ? `, sampledBundleBytes=${Atomics.load(samples, SAMPLE.bytes)}, lastBundleGrowthMs=${Atomics.load(samples, SAMPLE.growthMs)}, bundleBytes=${bundleBytes(bundleFile)}` : ""}`;
16346
+ }
16347
+ };
16348
+ }
16349
+
16350
+ // src/git-traces/object-store-probe.ts
16351
+ import { spawnSync } from "child_process";
16352
+ import fs18 from "fs";
16353
+ import path17 from "path";
16212
16354
  var probedRepos = /* @__PURE__ */ new Set();
16213
16355
  var SAMPLE_FANOUT_DIRS = 8;
16214
16356
  var SAMPLE_READS = 20;
@@ -16235,8 +16377,8 @@ function probeObjectStore(repoRoot, trigger, stderr, args = []) {
16235
16377
  return;
16236
16378
  }
16237
16379
  fields.push(`objectsDir=${objectsDir}`);
16238
- if (typeof fs17.statfsSync === "function") {
16239
- const stats = fs17.statfsSync(objectsDir);
16380
+ if (typeof fs18.statfsSync === "function") {
16381
+ const stats = fs18.statfsSync(objectsDir);
16240
16382
  fields.push(
16241
16383
  `fsType=${stats.type}`,
16242
16384
  `freeBytes=${stats.bavail * stats.bsize}`
@@ -16244,18 +16386,18 @@ function probeObjectStore(repoRoot, trigger, stderr, args = []) {
16244
16386
  }
16245
16387
  const { estimate, sample } = sampleLooseObjects(objectsDir);
16246
16388
  fields.push(`looseEstimate=${estimate}`);
16247
- const packDir = path16.join(objectsDir, "pack");
16248
- const packFiles = fs17.existsSync(packDir) ? fs17.readdirSync(packDir) : [];
16389
+ const packDir = path17.join(objectsDir, "pack");
16390
+ const packFiles = fs18.existsSync(packDir) ? fs18.readdirSync(packDir) : [];
16249
16391
  fields.push(
16250
16392
  `packs=${packFiles.filter((name) => name.endsWith(".pack")).length}`,
16251
16393
  `promisorPacks=${packFiles.filter((name) => name.endsWith(".promisor")).length}`,
16252
- `alternates=${fs17.existsSync(path16.join(objectsDir, "info", "alternates")) ? "yes" : "no"}`
16394
+ `alternates=${fs18.existsSync(path17.join(objectsDir, "info", "alternates")) ? "yes" : "no"}`
16253
16395
  );
16254
16396
  fields.push(...timeSampleReads(sample));
16255
16397
  const named = namedObjects(stderr);
16256
16398
  for (const sha of named) {
16257
- const loose = path16.join(objectsDir, sha.slice(0, 2), sha.slice(2));
16258
- const stats = fs17.statSync(loose, { throwIfNoEntry: false });
16399
+ const loose = path17.join(objectsDir, sha.slice(0, 2), sha.slice(2));
16400
+ const stats = fs18.statSync(loose, { throwIfNoEntry: false });
16259
16401
  fields.push(
16260
16402
  stats ? `named=${sha.slice(0, 8)}(size=${stats.size},blocks=${stats.blocks})` : `named=${sha.slice(0, 8)}(no loose file)`
16261
16403
  );
@@ -16275,30 +16417,30 @@ function probeObjectStore(repoRoot, trigger, stderr, args = []) {
16275
16417
  }
16276
16418
  }
16277
16419
  function resolveObjectsDir(repoRoot) {
16278
- let gitDir = path16.join(repoRoot, ".git");
16279
- const stats = fs17.statSync(gitDir, { throwIfNoEntry: false });
16420
+ let gitDir = path17.join(repoRoot, ".git");
16421
+ const stats = fs18.statSync(gitDir, { throwIfNoEntry: false });
16280
16422
  if (!stats) return null;
16281
16423
  if (stats.isFile()) {
16282
- const match = /^gitdir:\s*(.+)$/m.exec(fs17.readFileSync(gitDir, "utf-8"));
16424
+ const match = /^gitdir:\s*(.+)$/m.exec(fs18.readFileSync(gitDir, "utf-8"));
16283
16425
  if (!match) return null;
16284
- gitDir = path16.resolve(repoRoot, match[1].trim());
16426
+ gitDir = path17.resolve(repoRoot, match[1].trim());
16285
16427
  }
16286
- const commonDir = path16.join(gitDir, "commondir");
16287
- if (fs17.existsSync(commonDir)) {
16288
- gitDir = path16.resolve(gitDir, fs17.readFileSync(commonDir, "utf-8").trim());
16428
+ const commonDir = path17.join(gitDir, "commondir");
16429
+ if (fs18.existsSync(commonDir)) {
16430
+ gitDir = path17.resolve(gitDir, fs18.readFileSync(commonDir, "utf-8").trim());
16289
16431
  }
16290
- return path16.join(gitDir, "objects");
16432
+ return path17.join(gitDir, "objects");
16291
16433
  }
16292
16434
  function sampleLooseObjects(objectsDir) {
16293
- const fanout = fs17.readdirSync(objectsDir).filter((name) => /^[0-9a-f]{2}$/.test(name)).sort().slice(0, SAMPLE_FANOUT_DIRS);
16435
+ const fanout = fs18.readdirSync(objectsDir).filter((name) => /^[0-9a-f]{2}$/.test(name)).sort().slice(0, SAMPLE_FANOUT_DIRS);
16294
16436
  let counted = 0;
16295
16437
  const sample = [];
16296
16438
  for (const dir of fanout) {
16297
- const files = fs17.readdirSync(path16.join(objectsDir, dir));
16439
+ const files = fs18.readdirSync(path17.join(objectsDir, dir));
16298
16440
  counted += files.length;
16299
16441
  for (const file of files) {
16300
16442
  if (sample.length >= SAMPLE_READS) break;
16301
- sample.push(path16.join(objectsDir, dir, file));
16443
+ sample.push(path17.join(objectsDir, dir, file));
16302
16444
  }
16303
16445
  }
16304
16446
  return {
@@ -16316,14 +16458,14 @@ function timeSampleReads(sample) {
16316
16458
  for (const file of sample) {
16317
16459
  const readStarted = performance.now();
16318
16460
  try {
16319
- const stats = fs17.statSync(file);
16461
+ const stats = fs18.statSync(file);
16320
16462
  if (!stats.isFile()) continue;
16321
16463
  if (stats.size > 0 && stats.blocks === 0) dataless++;
16322
- const fd = fs17.openSync(file, "r");
16464
+ const fd = fs18.openSync(file, "r");
16323
16465
  try {
16324
- fs17.readSync(fd, buffer, 0, SAMPLE_READ_BYTES, 0);
16466
+ fs18.readSync(fd, buffer, 0, SAMPLE_READ_BYTES, 0);
16325
16467
  } finally {
16326
- fs17.closeSync(fd);
16468
+ fs18.closeSync(fd);
16327
16469
  }
16328
16470
  } catch (err) {
16329
16471
  readErrors++;
@@ -16348,7 +16490,7 @@ function namedObjects(stderr) {
16348
16490
  return [...shas].slice(0, MAX_NAMED_OBJECTS);
16349
16491
  }
16350
16492
  function sampleSha(file) {
16351
- const sha = path16.basename(path16.dirname(file)) + path16.basename(file);
16493
+ const sha = path17.basename(path17.dirname(file)) + path17.basename(file);
16352
16494
  return OBJECT_SHA.test(sha) ? sha : null;
16353
16495
  }
16354
16496
  function failedTreeish(args) {
@@ -16425,12 +16567,12 @@ function locateGitRead(fields, repoRoot, objectsDir, treeish, fallback) {
16425
16567
  fields.push(
16426
16568
  `stalledPath=${JSON.stringify(stalled.path.slice(0, MAX_STALLED_PATH_CHARS))}`
16427
16569
  );
16428
- const loose = path16.join(
16570
+ const loose = path17.join(
16429
16571
  objectsDir,
16430
16572
  stalled.sha.slice(0, 2),
16431
16573
  stalled.sha.slice(2)
16432
16574
  );
16433
- const stats = fs17.statSync(loose, { throwIfNoEntry: false });
16575
+ const stats = fs18.statSync(loose, { throwIfNoEntry: false });
16434
16576
  fields.push(
16435
16577
  stats ? `stalledLoose=size=${stats.size},blocks=${stats.blocks}` : "stalledLoose=no"
16436
16578
  );
@@ -16654,8 +16796,8 @@ var EXCLUDED_SNAPSHOT_BASENAMES = /* @__PURE__ */ new Set([
16654
16796
  ]);
16655
16797
  var EMPTY_TREE_SHA = "4b825dc642cb6eb9a060e54bf8d69288fbee4904";
16656
16798
  function isExcludedSnapshotPath(filePath) {
16657
- if (EXCLUDED_SNAPSHOT_BASENAMES.has(path17.basename(filePath))) return true;
16658
- return EXCLUDED_SNAPSHOT_EXTENSIONS.has(path17.extname(filePath).toLowerCase());
16799
+ if (EXCLUDED_SNAPSHOT_BASENAMES.has(path18.basename(filePath))) return true;
16800
+ return EXCLUDED_SNAPSHOT_EXTENSIONS.has(path18.extname(filePath).toLowerCase());
16659
16801
  }
16660
16802
  function isBinaryBuffer(buffer) {
16661
16803
  return buffer.includes(0);
@@ -16676,16 +16818,16 @@ function readTreeBlobHead(repoRoot, sha) {
16676
16818
  function readWorkingFileHead(absPath) {
16677
16819
  let fd = null;
16678
16820
  try {
16679
- fd = fs18.openSync(absPath, "r");
16821
+ fd = fs19.openSync(absPath, "r");
16680
16822
  const buffer = Buffer.alloc(BINARY_SNIFF_BYTES);
16681
- const bytesRead = fs18.readSync(fd, buffer, 0, BINARY_SNIFF_BYTES, 0);
16823
+ const bytesRead = fs19.readSync(fd, buffer, 0, BINARY_SNIFF_BYTES, 0);
16682
16824
  return buffer.subarray(0, bytesRead);
16683
16825
  } catch {
16684
16826
  return null;
16685
16827
  } finally {
16686
16828
  if (fd !== null) {
16687
16829
  try {
16688
- fs18.closeSync(fd);
16830
+ fs19.closeSync(fd);
16689
16831
  } catch {
16690
16832
  }
16691
16833
  }
@@ -16785,6 +16927,44 @@ function gitBuffer(repoRoot, args, options = {}) {
16785
16927
  const detached = options.killProcessGroup && process.platform !== "win32";
16786
16928
  const started = Date.now();
16787
16929
  try {
16930
+ if (options.diagnostics) {
16931
+ const diagnostics = startGitDiagnostics(options.diagnostics.bundleFile);
16932
+ let status = "failed";
16933
+ try {
16934
+ const result = spawnSync2("git", args, {
16935
+ cwd: repoRoot,
16936
+ env: {
16937
+ ...options.env ?? process.env,
16938
+ ...diagnostics.env
16939
+ },
16940
+ input: options.input,
16941
+ stdio: ["pipe", "pipe", "pipe"],
16942
+ ...EXEC_OPTS,
16943
+ timeout,
16944
+ ...detached ? { detached: true } : {}
16945
+ });
16946
+ if (result.error || result.status !== 0) {
16947
+ throw Object.assign(result.error ?? new Error("Git command failed"), {
16948
+ pid: result.pid,
16949
+ status: result.status,
16950
+ signal: result.signal,
16951
+ stdout: result.stdout,
16952
+ stderr: result.stderr
16953
+ });
16954
+ }
16955
+ status = "ok";
16956
+ return result.stdout;
16957
+ } finally {
16958
+ const elapsedMs = Date.now() - started;
16959
+ const summary = diagnostics.finish();
16960
+ if (status === "failed" || elapsedMs >= 1e3) {
16961
+ appendLog(
16962
+ "info",
16963
+ `git-traces: git diagnostics (stage=${options.diagnostics.stage}, status=${status}, elapsedMs=${elapsedMs}, ${summary})`
16964
+ );
16965
+ }
16966
+ }
16967
+ }
16788
16968
  return execFileSync2("git", args, {
16789
16969
  cwd: repoRoot,
16790
16970
  ...options.env ? { env: options.env } : {},
@@ -16850,23 +17030,23 @@ function withPrivateIndex(repoRoot, run) {
16850
17030
  let tmpDir = null;
16851
17031
  let copy = null;
16852
17032
  try {
16853
- const indexPath = path17.resolve(
17033
+ const indexPath = path18.resolve(
16854
17034
  repoRoot,
16855
17035
  git(repoRoot, ["rev-parse", "--git-path", "index"])
16856
17036
  );
16857
- tmpDir = fs18.mkdtempSync(path17.join(os7.tmpdir(), "hillclimb-index-"));
16858
- copy = path17.join(tmpDir, "index");
16859
- const fd = fs18.openSync(indexPath, "r");
17037
+ tmpDir = fs19.mkdtempSync(path18.join(os8.tmpdir(), "hillclimb-index-"));
17038
+ copy = path18.join(tmpDir, "index");
17039
+ const fd = fs19.openSync(indexPath, "r");
16860
17040
  let stat;
16861
17041
  let data;
16862
17042
  try {
16863
- stat = fs18.fstatSync(fd);
16864
- data = fs18.readFileSync(fd);
17043
+ stat = fs19.fstatSync(fd);
17044
+ data = fs19.readFileSync(fd);
16865
17045
  } finally {
16866
- fs18.closeSync(fd);
17046
+ fs19.closeSync(fd);
16867
17047
  }
16868
- fs18.writeFileSync(copy, data);
16869
- fs18.utimesSync(copy, stat.atime, stat.mtime);
17048
+ fs19.writeFileSync(copy, data);
17049
+ fs19.utimesSync(copy, stat.atime, stat.mtime);
16870
17050
  } catch (err) {
16871
17051
  const missing = err.code === "ENOENT";
16872
17052
  appendLog(
@@ -16878,7 +17058,7 @@ function withPrivateIndex(repoRoot, run) {
16878
17058
  try {
16879
17059
  return run(copy ? privateIndexEnv(copy) : null);
16880
17060
  } finally {
16881
- if (tmpDir) fs18.rmSync(tmpDir, { recursive: true, force: true });
17061
+ if (tmpDir) fs19.rmSync(tmpDir, { recursive: true, force: true });
16882
17062
  }
16883
17063
  }
16884
17064
  function privateIndexEnv(indexPath) {
@@ -17295,8 +17475,8 @@ function filterOmittedFilesFromTree(repoRoot, treeSha, options = {}) {
17295
17475
  options.omittedFiles?.push(...omittedFiles);
17296
17476
  return timeCaptureStage(repoRoot, "tracked-tree-filter", () => {
17297
17477
  if (omittedFiles.length === 0) return treeSha;
17298
- const tmpIndex = path17.join(
17299
- os7.tmpdir(),
17478
+ const tmpIndex = path18.join(
17479
+ os8.tmpdir(),
17300
17480
  `hillclimb-filter-${Date.now()}-${process.pid}`
17301
17481
  );
17302
17482
  const env = { ...process.env, GIT_INDEX_FILE: tmpIndex };
@@ -17310,7 +17490,7 @@ function filterOmittedFilesFromTree(repoRoot, treeSha, options = {}) {
17310
17490
  return gitWithEnv(repoRoot, ["write-tree"], env);
17311
17491
  } finally {
17312
17492
  try {
17313
- fs18.unlinkSync(tmpIndex);
17493
+ fs19.unlinkSync(tmpIndex);
17314
17494
  } catch {
17315
17495
  }
17316
17496
  }
@@ -17335,8 +17515,8 @@ function buildUntrackedTree(repoRoot, options = {}, attempt = 1) {
17335
17515
  if (!relPath) continue;
17336
17516
  candidates++;
17337
17517
  try {
17338
- const absPath = path17.join(repoRoot, relPath);
17339
- const stat = fs18.lstatSync(absPath);
17518
+ const absPath = path18.join(repoRoot, relPath);
17519
+ const stat = fs19.lstatSync(absPath);
17340
17520
  const reason = classifyOmission(
17341
17521
  relPath,
17342
17522
  stat.size,
@@ -17367,11 +17547,11 @@ function buildUntrackedTree(repoRoot, options = {}, attempt = 1) {
17367
17547
  `git-traces: untracked inventory (repo=${repoRoot}, candidates=${candidates}, kept=${kept.length}, keptBytes=${keptBytes}, omitted=${omitted}, omittedBytes=${omittedBytes}, skipped=${skipped})`
17368
17548
  );
17369
17549
  if (kept.length === 0) return null;
17370
- const tmpDir = fs18.mkdtempSync(path17.join(os7.tmpdir(), "hillclimb-untracked-"));
17550
+ const tmpDir = fs19.mkdtempSync(path18.join(os8.tmpdir(), "hillclimb-untracked-"));
17371
17551
  const env = {
17372
17552
  ...process.env,
17373
17553
  LC_ALL: "C",
17374
- GIT_INDEX_FILE: path17.join(tmpDir, "index")
17554
+ GIT_INDEX_FILE: path18.join(tmpDir, "index")
17375
17555
  };
17376
17556
  try {
17377
17557
  timeCaptureStage(
@@ -17379,7 +17559,8 @@ function buildUntrackedTree(repoRoot, options = {}, attempt = 1) {
17379
17559
  "untracked-hash-and-index",
17380
17560
  () => gitBuffer(repoRoot, ["update-index", "--add", "-z", "--stdin"], {
17381
17561
  input: `${kept.join("\0")}\0`,
17382
- env
17562
+ env,
17563
+ diagnostics: { stage: "untracked-hash-and-index" }
17383
17564
  })
17384
17565
  );
17385
17566
  return timeCaptureStage(
@@ -17399,7 +17580,7 @@ function buildUntrackedTree(repoRoot, options = {}, attempt = 1) {
17399
17580
  return buildUntrackedTree(repoRoot, options, attempt + 1);
17400
17581
  } finally {
17401
17582
  try {
17402
- fs18.rmSync(tmpDir, { recursive: true, force: true });
17583
+ fs19.rmSync(tmpDir, { recursive: true, force: true });
17403
17584
  } catch {
17404
17585
  }
17405
17586
  }
@@ -17470,8 +17651,8 @@ function mergeSnapshotTrees(repoRoot, filteredTrackedTree, untrackedTree) {
17470
17651
  if (!untrackedTree || untrackedTree === EMPTY_TREE_SHA)
17471
17652
  return filteredTrackedTree;
17472
17653
  if (filteredTrackedTree === EMPTY_TREE_SHA) return untrackedTree;
17473
- const tmpIndex = path17.join(
17474
- os7.tmpdir(),
17654
+ const tmpIndex = path18.join(
17655
+ os8.tmpdir(),
17475
17656
  `hillclimb-index-${Date.now()}-${process.pid}`
17476
17657
  );
17477
17658
  const env = { ...process.env, GIT_INDEX_FILE: tmpIndex };
@@ -17499,7 +17680,7 @@ function mergeSnapshotTrees(repoRoot, filteredTrackedTree, untrackedTree) {
17499
17680
  );
17500
17681
  } finally {
17501
17682
  try {
17502
- fs18.unlinkSync(tmpIndex);
17683
+ fs19.unlinkSync(tmpIndex);
17503
17684
  } catch {
17504
17685
  }
17505
17686
  }
@@ -17514,19 +17695,20 @@ function createBundleFromTree(repoRoot, treeSha, sessionId, label) {
17514
17695
  pinRef(repoRoot, orphanRef, orphanCommit);
17515
17696
  let tmpDir;
17516
17697
  try {
17517
- tmpDir = fs18.mkdtempSync(
17518
- path17.join(os7.tmpdir(), `hillclimb-bundle-${process.pid}-`)
17698
+ tmpDir = fs19.mkdtempSync(
17699
+ path18.join(os8.tmpdir(), `hillclimb-bundle-${process.pid}-`)
17519
17700
  );
17520
- const tmpFile = path17.join(tmpDir, "baseline.bundle");
17701
+ const tmpFile = path18.join(tmpDir, "baseline.bundle");
17521
17702
  timeCaptureStage(
17522
17703
  repoRoot,
17523
17704
  "bundle-create",
17524
17705
  () => gitBuffer(repoRoot, ["bundle", "create", tmpFile, orphanRef], {
17525
17706
  timeoutMs: resolveGitCommandTimeoutMs(DEFAULT_BUNDLE_TIMEOUT_MS),
17526
- killProcessGroup: true
17707
+ killProcessGroup: true,
17708
+ diagnostics: { stage: "bundle-create", bundleFile: tmpFile }
17527
17709
  })
17528
17710
  );
17529
- const bundle = fs18.readFileSync(tmpFile);
17711
+ const bundle = fs19.readFileSync(tmpFile);
17530
17712
  appendLog(
17531
17713
  "info",
17532
17714
  `git-traces: bundle inventory (repo=${repoRoot}, bytes=${bundle.length})`
@@ -17534,7 +17716,7 @@ function createBundleFromTree(repoRoot, treeSha, sessionId, label) {
17534
17716
  return bundle;
17535
17717
  } finally {
17536
17718
  try {
17537
- if (tmpDir) fs18.rmSync(tmpDir, { recursive: true, force: true });
17719
+ if (tmpDir) fs19.rmSync(tmpDir, { recursive: true, force: true });
17538
17720
  } catch {
17539
17721
  }
17540
17722
  deleteRef(repoRoot, orphanRef);
@@ -17753,7 +17935,7 @@ function buildBaselineMetadata(repoRoot, sessionId, tool, baselineSha, cliVersio
17753
17935
  ...omittedFiles.length > 0 ? { omittedFiles } : {}
17754
17936
  },
17755
17937
  author: { name: authorName, email: authorEmail },
17756
- hostname: os7.hostname(),
17938
+ hostname: os8.hostname(),
17757
17939
  cliVersion,
17758
17940
  commits,
17759
17941
  ...lineage,
@@ -17845,9 +18027,9 @@ function parseCommitFiles(repoRoot, sha) {
17845
18027
  oldPath
17846
18028
  });
17847
18029
  } else {
17848
- const path28 = parts[parts.length - 1];
17849
- indexByPath.set(path28, files.length);
17850
- files.push({ path: path28, status, additions: 0, deletions: 0 });
18030
+ const path29 = parts[parts.length - 1];
18031
+ indexByPath.set(path29, files.length);
18032
+ files.push({ path: path29, status, additions: 0, deletions: 0 });
17851
18033
  }
17852
18034
  }
17853
18035
  for (const line of numstat.split("\n")) {
@@ -17931,11 +18113,11 @@ function countScopedTurnTreeRefs(repoRoot, sessionId, epochPrefix3) {
17931
18113
 
17932
18114
  // src/git-traces/session-state.ts
17933
18115
  import crypto6 from "crypto";
17934
- import fs19 from "fs";
17935
- import os8 from "os";
17936
- import path18 from "path";
18116
+ import fs20 from "fs";
18117
+ import os9 from "os";
18118
+ import path19 from "path";
17937
18119
  var CURRENT_SCHEMA_VERSION3 = 3;
17938
- var DEFAULT_STATE_DIR2 = path18.join(os8.homedir(), ".hillclimb", "git-traces");
18120
+ var DEFAULT_STATE_DIR2 = path19.join(os9.homedir(), ".hillclimb", "git-traces");
17939
18121
  var LOCK_RETRIES2 = 120;
17940
18122
  var LOCK_RETRY_DELAY_MS2 = 500;
17941
18123
  var DEFAULT_LIVE_OWNER_MAX_WAIT_MS2 = 10 * 60 * 1e3;
@@ -17960,16 +18142,16 @@ function stateDir3() {
17960
18142
  }
17961
18143
  function stateFileForRepo(repoRoot, tool, sessionId) {
17962
18144
  const hash = crypto6.createHash("sha256").update(
17963
- sessionId ? `${path18.resolve(repoRoot)}\0${tool}\0${sessionId}` : `${path18.resolve(repoRoot)}\0${tool}`
18145
+ sessionId ? `${path19.resolve(repoRoot)}\0${tool}\0${sessionId}` : `${path19.resolve(repoRoot)}\0${tool}`
17964
18146
  ).digest("hex").slice(0, 16);
17965
- return path18.join(stateDir3(), `${hash}.json`);
18147
+ return path19.join(stateDir3(), `${hash}.json`);
17966
18148
  }
17967
18149
  function lockFileForRepo(repoRoot, tool, sessionId) {
17968
18150
  return `${stateFileForRepo(repoRoot, tool, sessionId)}.lock`;
17969
18151
  }
17970
18152
  async function readStateFile(file) {
17971
18153
  try {
17972
- const raw = await fs19.promises.readFile(file, "utf-8");
18154
+ const raw = await fs20.promises.readFile(file, "utf-8");
17973
18155
  const parsed = JSON.parse(raw);
17974
18156
  if (parsed.schemaVersion !== CURRENT_SCHEMA_VERSION3) {
17975
18157
  return null;
@@ -17983,7 +18165,7 @@ async function readStoredStateFile(file) {
17983
18165
  const state = await readStateFile(file);
17984
18166
  if (!state) return null;
17985
18167
  try {
17986
- return { state, mtimeMs: (await fs19.promises.stat(file)).mtimeMs };
18168
+ return { state, mtimeMs: (await fs20.promises.stat(file)).mtimeMs };
17987
18169
  } catch {
17988
18170
  return null;
17989
18171
  }
@@ -17991,26 +18173,26 @@ async function readStoredStateFile(file) {
17991
18173
  async function listScopedSessionStates(repoRoot, tool) {
17992
18174
  let entries;
17993
18175
  try {
17994
- entries = await fs19.promises.readdir(stateDir3(), { withFileTypes: true });
18176
+ entries = await fs20.promises.readdir(stateDir3(), { withFileTypes: true });
17995
18177
  } catch {
17996
18178
  return [];
17997
18179
  }
17998
18180
  const states = [];
17999
18181
  for (const entry of entries) {
18000
18182
  if (!entry.isFile() || !entry.name.endsWith(".json")) continue;
18001
- const file = path18.join(stateDir3(), entry.name);
18183
+ const file = path19.join(stateDir3(), entry.name);
18002
18184
  const state = await readStateFile(file);
18003
18185
  if (!state) continue;
18004
18186
  if (typeof state.repoRoot !== "string" || typeof state.sessionId !== "string") {
18005
18187
  continue;
18006
18188
  }
18007
- if (path18.resolve(state.repoRoot) !== path18.resolve(repoRoot)) continue;
18008
- if (path18.resolve(file) !== path18.resolve(stateFileForRepo(repoRoot, tool, state.sessionId))) {
18189
+ if (path19.resolve(state.repoRoot) !== path19.resolve(repoRoot)) continue;
18190
+ if (path19.resolve(file) !== path19.resolve(stateFileForRepo(repoRoot, tool, state.sessionId))) {
18009
18191
  continue;
18010
18192
  }
18011
18193
  let mtimeMs = 0;
18012
18194
  try {
18013
- mtimeMs = (await fs19.promises.stat(file)).mtimeMs;
18195
+ mtimeMs = (await fs20.promises.stat(file)).mtimeMs;
18014
18196
  } catch {
18015
18197
  continue;
18016
18198
  }
@@ -18021,26 +18203,26 @@ async function listScopedSessionStates(repoRoot, tool) {
18021
18203
  async function listSessionStatesForSession(tool, sessionId) {
18022
18204
  let entries;
18023
18205
  try {
18024
- entries = await fs19.promises.readdir(stateDir3(), { withFileTypes: true });
18206
+ entries = await fs20.promises.readdir(stateDir3(), { withFileTypes: true });
18025
18207
  } catch {
18026
18208
  return [];
18027
18209
  }
18028
18210
  const states = [];
18029
18211
  for (const entry of entries) {
18030
18212
  if (!entry.isFile() || !entry.name.endsWith(".json")) continue;
18031
- const file = path18.join(stateDir3(), entry.name);
18213
+ const file = path19.join(stateDir3(), entry.name);
18032
18214
  const state = await readStateFile(file);
18033
18215
  if (!state) continue;
18034
18216
  if (typeof state.repoRoot !== "string" || typeof state.sessionId !== "string") {
18035
18217
  continue;
18036
18218
  }
18037
18219
  if (state.sessionId !== sessionId) continue;
18038
- if (path18.resolve(file) !== path18.resolve(stateFileForRepo(state.repoRoot, tool, sessionId))) {
18220
+ if (path19.resolve(file) !== path19.resolve(stateFileForRepo(state.repoRoot, tool, sessionId))) {
18039
18221
  continue;
18040
18222
  }
18041
18223
  let mtimeMs = 0;
18042
18224
  try {
18043
- mtimeMs = (await fs19.promises.stat(file)).mtimeMs;
18225
+ mtimeMs = (await fs20.promises.stat(file)).mtimeMs;
18044
18226
  } catch {
18045
18227
  continue;
18046
18228
  }
@@ -18089,17 +18271,17 @@ async function writeLegacySessionState(state, tool) {
18089
18271
  );
18090
18272
  }
18091
18273
  async function writeStateFile(file, state) {
18092
- await fs19.promises.mkdir(stateDir3(), { recursive: true, mode: 448 });
18274
+ await fs20.promises.mkdir(stateDir3(), { recursive: true, mode: 448 });
18093
18275
  const tmp = `${file}.tmp`;
18094
- await fs19.promises.writeFile(tmp, JSON.stringify(state, null, 2), {
18276
+ await fs20.promises.writeFile(tmp, JSON.stringify(state, null, 2), {
18095
18277
  mode: 384
18096
18278
  });
18097
- await fs19.promises.rename(tmp, file);
18279
+ await fs20.promises.rename(tmp, file);
18098
18280
  }
18099
18281
  async function touchSessionState(repoRoot, tool, sessionId) {
18100
18282
  const now = /* @__PURE__ */ new Date();
18101
18283
  try {
18102
- await fs19.promises.utimes(
18284
+ await fs20.promises.utimes(
18103
18285
  stateFileForRepo(repoRoot, tool, sessionId),
18104
18286
  now,
18105
18287
  now
@@ -18109,7 +18291,7 @@ async function touchSessionState(repoRoot, tool, sessionId) {
18109
18291
  }
18110
18292
  async function statSessionStateMtime(repoRoot, tool, sessionId) {
18111
18293
  try {
18112
- const stat = await fs19.promises.stat(
18294
+ const stat = await fs20.promises.stat(
18113
18295
  stateFileForRepo(repoRoot, tool, sessionId)
18114
18296
  );
18115
18297
  return stat.mtimeMs;
@@ -18119,7 +18301,7 @@ async function statSessionStateMtime(repoRoot, tool, sessionId) {
18119
18301
  }
18120
18302
  async function deleteStateFile(file) {
18121
18303
  try {
18122
- await fs19.promises.unlink(file);
18304
+ await fs20.promises.unlink(file);
18123
18305
  } catch {
18124
18306
  }
18125
18307
  }
@@ -18158,7 +18340,7 @@ async function acquireLock3(repoRoot, tool, sessionId, retries, delayMs, options
18158
18340
  }
18159
18341
  async function acquireLockWithoutTiming(repoRoot, tool, sessionId, retries, delayMs, options = {}) {
18160
18342
  const lockPath = lockFileForRepo(repoRoot, tool, sessionId);
18161
- await fs19.promises.mkdir(stateDir3(), { recursive: true, mode: 448 });
18343
+ await fs20.promises.mkdir(stateDir3(), { recursive: true, mode: 448 });
18162
18344
  const bounded = retries !== void 0 || delayMs !== void 0;
18163
18345
  const attempts = retries ?? LOCK_RETRIES2;
18164
18346
  const delay = delayMs ?? LOCK_RETRY_DELAY_MS2;
@@ -18168,9 +18350,9 @@ async function acquireLockWithoutTiming(repoRoot, tool, sessionId, retries, dela
18168
18350
  let extensionLogged = false;
18169
18351
  for (let i = 0; ; i++) {
18170
18352
  try {
18171
- const fd = await fs19.promises.open(
18353
+ const fd = await fs20.promises.open(
18172
18354
  lockPath,
18173
- fs19.constants.O_CREAT | fs19.constants.O_EXCL | fs19.constants.O_WRONLY
18355
+ fs20.constants.O_CREAT | fs20.constants.O_EXCL | fs20.constants.O_WRONLY
18174
18356
  );
18175
18357
  await fd.write(String(process.pid));
18176
18358
  await fd.close();
@@ -18179,7 +18361,7 @@ async function acquireLockWithoutTiming(repoRoot, tool, sessionId, retries, dela
18179
18361
  if (err.code !== "EEXIST") throw err;
18180
18362
  let regularLockFile = false;
18181
18363
  try {
18182
- regularLockFile = (await fs19.promises.lstat(lockPath)).isFile();
18364
+ regularLockFile = (await fs20.promises.lstat(lockPath)).isFile();
18183
18365
  } catch (statErr) {
18184
18366
  if (statErr.code === "ENOENT") {
18185
18367
  i--;
@@ -18241,21 +18423,21 @@ async function acquireLockWithoutTiming(repoRoot, tool, sessionId, retries, dela
18241
18423
  }
18242
18424
  async function releaseLock3(repoRoot, tool, sessionId) {
18243
18425
  try {
18244
- await fs19.promises.unlink(lockFileForRepo(repoRoot, tool, sessionId));
18426
+ await fs20.promises.unlink(lockFileForRepo(repoRoot, tool, sessionId));
18245
18427
  } catch {
18246
18428
  }
18247
18429
  }
18248
18430
  async function sweepStaleLockFiles(ttlMs = STALE_LOCK_TTL_MS3, now = Date.now()) {
18249
18431
  let entries;
18250
18432
  try {
18251
- entries = await fs19.promises.readdir(stateDir3(), { withFileTypes: true });
18433
+ entries = await fs20.promises.readdir(stateDir3(), { withFileTypes: true });
18252
18434
  } catch {
18253
18435
  return 0;
18254
18436
  }
18255
18437
  let removed = 0;
18256
18438
  for (const entry of entries) {
18257
18439
  if (!entry.isFile() || !entry.name.endsWith(".lock")) continue;
18258
- const file = path18.join(stateDir3(), entry.name);
18440
+ const file = path19.join(stateDir3(), entry.name);
18259
18441
  if (await reapLockIfStale(file, {
18260
18442
  maxAgeMs: ttlMs,
18261
18443
  preserveLiveOwner: true,
@@ -18317,12 +18499,12 @@ function withPendingCapture(record, work) {
18317
18499
  return activeCapture.run({ record, save: () => saveCapture(record) }, work);
18318
18500
  }
18319
18501
  function queueDirectory(repoRoot, tool, sessionId) {
18320
- const stateDir4 = process.env.HILLCLIMB_GIT_TRACES_STATE_DIR ?? path19.join(os9.homedir(), ".hillclimb", "git-traces");
18321
- const key = crypto7.createHash("sha256").update(`${path19.resolve(repoRoot)}\0${tool}\0${sessionId}`).digest("hex");
18322
- return path19.join(stateDir4, "pending-v1", key);
18502
+ const stateDir4 = process.env.HILLCLIMB_GIT_TRACES_STATE_DIR ?? path20.join(os10.homedir(), ".hillclimb", "git-traces");
18503
+ const key = crypto7.createHash("sha256").update(`${path20.resolve(repoRoot)}\0${tool}\0${sessionId}`).digest("hex");
18504
+ return path20.join(stateDir4, "pending-v1", key);
18323
18505
  }
18324
18506
  function captureDirectory(record) {
18325
- return path19.join(
18507
+ return path20.join(
18326
18508
  queueDirectory(record.repoRoot, record.tool, record.sessionId),
18327
18509
  record.id
18328
18510
  );
@@ -18330,20 +18512,20 @@ function captureDirectory(record) {
18330
18512
  function writeDurable(file, body) {
18331
18513
  const temporary = `${file}.${process.pid}.tmp`;
18332
18514
  try {
18333
- const descriptor = fs20.openSync(temporary, "w", 384);
18515
+ const descriptor = fs21.openSync(temporary, "w", 384);
18334
18516
  try {
18335
- fs20.writeFileSync(descriptor, body);
18336
- fs20.fsyncSync(descriptor);
18517
+ fs21.writeFileSync(descriptor, body);
18518
+ fs21.fsyncSync(descriptor);
18337
18519
  } finally {
18338
- fs20.closeSync(descriptor);
18520
+ fs21.closeSync(descriptor);
18339
18521
  }
18340
- fs20.renameSync(temporary, file);
18522
+ fs21.renameSync(temporary, file);
18341
18523
  try {
18342
- const directory = fs20.openSync(path19.dirname(file), "r");
18524
+ const directory = fs21.openSync(path20.dirname(file), "r");
18343
18525
  try {
18344
- fs20.fsyncSync(directory);
18526
+ fs21.fsyncSync(directory);
18345
18527
  } finally {
18346
- fs20.closeSync(directory);
18528
+ fs21.closeSync(directory);
18347
18529
  }
18348
18530
  } catch (err) {
18349
18531
  if (!["EINVAL", "ENOTSUP", "EBADF", "EPERM", "EISDIR"].includes(
@@ -18352,7 +18534,7 @@ function writeDurable(file, body) {
18352
18534
  throw err;
18353
18535
  }
18354
18536
  } finally {
18355
- if (fs20.existsSync(temporary)) fs20.unlinkSync(temporary);
18537
+ if (fs21.existsSync(temporary)) fs21.unlinkSync(temporary);
18356
18538
  }
18357
18539
  }
18358
18540
  function saveCapture(record) {
@@ -18363,7 +18545,7 @@ function saveCapture(record) {
18363
18545
  let status = "failed";
18364
18546
  try {
18365
18547
  writeDurable(
18366
- path19.join(captureDirectory(record), "capture.json"),
18548
+ path20.join(captureDirectory(record), "capture.json"),
18367
18549
  JSON.stringify(record)
18368
18550
  );
18369
18551
  status = "ok";
@@ -18375,7 +18557,7 @@ function listPendingCaptures2(repoRoot, tool, sessionId) {
18375
18557
  const directory = queueDirectory(repoRoot, tool, sessionId);
18376
18558
  let entries;
18377
18559
  try {
18378
- entries = fs20.readdirSync(directory, { withFileTypes: true });
18560
+ entries = fs21.readdirSync(directory, { withFileTypes: true });
18379
18561
  } catch (err) {
18380
18562
  if (err.code === "ENOENT") return [];
18381
18563
  throw err;
@@ -18383,10 +18565,10 @@ function listPendingCaptures2(repoRoot, tool, sessionId) {
18383
18565
  const records = [];
18384
18566
  for (const entry of entries) {
18385
18567
  if (!entry.isDirectory() || !/^[0-9a-f-]{36}$/.test(entry.name)) continue;
18386
- const file = path19.join(directory, entry.name, "capture.json");
18387
- if (!fs20.existsSync(file)) continue;
18568
+ const file = path20.join(directory, entry.name, "capture.json");
18569
+ if (!fs21.existsSync(file)) continue;
18388
18570
  const record = JSON.parse(
18389
- fs20.readFileSync(file, "utf-8")
18571
+ fs21.readFileSync(file, "utf-8")
18390
18572
  );
18391
18573
  if (record.version !== 1 || record.id !== entry.name || record.repoRoot !== repoRoot || record.tool !== tool || record.sessionId !== sessionId) {
18392
18574
  throw new Error(`Invalid pending Git capture: ${file}`);
@@ -18399,13 +18581,13 @@ function hasPendingCaptures(repoRoot, tool, sessionId) {
18399
18581
  return listPendingCaptures2(repoRoot, tool, sessionId).length > 0;
18400
18582
  }
18401
18583
  function listPendingQueues() {
18402
- const root = path19.join(
18403
- process.env.HILLCLIMB_GIT_TRACES_STATE_DIR ?? path19.join(os9.homedir(), ".hillclimb", "git-traces"),
18584
+ const root = path20.join(
18585
+ process.env.HILLCLIMB_GIT_TRACES_STATE_DIR ?? path20.join(os10.homedir(), ".hillclimb", "git-traces"),
18404
18586
  "pending-v1"
18405
18587
  );
18406
18588
  let queueDirs;
18407
18589
  try {
18408
- queueDirs = fs20.readdirSync(root, { withFileTypes: true });
18590
+ queueDirs = fs21.readdirSync(root, { withFileTypes: true });
18409
18591
  } catch (err) {
18410
18592
  if (err.code === "ENOENT") return [];
18411
18593
  throw err;
@@ -18413,15 +18595,15 @@ function listPendingQueues() {
18413
18595
  const queues = [];
18414
18596
  for (const queueDir of queueDirs) {
18415
18597
  if (!queueDir.isDirectory()) continue;
18416
- const directory = path19.join(root, queueDir.name);
18598
+ const directory = path20.join(root, queueDir.name);
18417
18599
  let identity = null;
18418
18600
  try {
18419
- for (const entry of fs20.readdirSync(directory, { withFileTypes: true })) {
18601
+ for (const entry of fs21.readdirSync(directory, { withFileTypes: true })) {
18420
18602
  if (!entry.isDirectory()) continue;
18421
- const file = path19.join(directory, entry.name, "capture.json");
18422
- if (!fs20.existsSync(file)) continue;
18603
+ const file = path20.join(directory, entry.name, "capture.json");
18604
+ if (!fs21.existsSync(file)) continue;
18423
18605
  identity = JSON.parse(
18424
- fs20.readFileSync(file, "utf-8")
18606
+ fs21.readFileSync(file, "utf-8")
18425
18607
  );
18426
18608
  break;
18427
18609
  }
@@ -18454,15 +18636,15 @@ function noteReplayFailure(record, error, now = Date.now()) {
18454
18636
  }
18455
18637
  function queueDiskBytes(directory) {
18456
18638
  let diskBytes = 0;
18457
- if (fs20.existsSync(directory)) {
18458
- for (const entry of fs20.readdirSync(directory, { withFileTypes: true })) {
18639
+ if (fs21.existsSync(directory)) {
18640
+ for (const entry of fs21.readdirSync(directory, { withFileTypes: true })) {
18459
18641
  if (!entry.isDirectory()) continue;
18460
- for (const file of fs20.readdirSync(path19.join(directory, entry.name), {
18642
+ for (const file of fs21.readdirSync(path20.join(directory, entry.name), {
18461
18643
  withFileTypes: true
18462
18644
  })) {
18463
18645
  if (file.isFile())
18464
- diskBytes += fs20.statSync(
18465
- path19.join(directory, entry.name, file.name)
18646
+ diskBytes += fs21.statSync(
18647
+ path20.join(directory, entry.name, file.name)
18466
18648
  ).size;
18467
18649
  }
18468
18650
  }
@@ -18478,7 +18660,7 @@ function pendingBytes(records) {
18478
18660
  return queueDiskBytes(
18479
18661
  queueDirectory(first.repoRoot, first.tool, first.sessionId)
18480
18662
  ) + records.reduce(
18481
- (sum, record) => sum + chargedBytes(record) + (fs20.existsSync(path19.join(captureDirectory(record), "capture.json")) ? 0 : Buffer.byteLength(JSON.stringify(record))),
18663
+ (sum, record) => sum + chargedBytes(record) + (fs21.existsSync(path20.join(captureDirectory(record), "capture.json")) ? 0 : Buffer.byteLength(JSON.stringify(record))),
18482
18664
  0
18483
18665
  );
18484
18666
  }
@@ -18643,7 +18825,7 @@ function storePendingCapture(params) {
18643
18825
  );
18644
18826
  }
18645
18827
  const directory = captureDirectory(record);
18646
- fs20.mkdirSync(directory, { recursive: true, mode: 448 });
18828
+ fs21.mkdirSync(directory, { recursive: true, mode: 448 });
18647
18829
  const refRoot = `refs/hillclimb/pending-v1/${record.id}`;
18648
18830
  let published = false;
18649
18831
  try {
@@ -18686,7 +18868,7 @@ function storePendingCapture(params) {
18686
18868
  return record;
18687
18869
  } finally {
18688
18870
  if (!published) {
18689
- fs20.rmSync(directory, { recursive: true, force: true });
18871
+ fs21.rmSync(directory, { recursive: true, force: true });
18690
18872
  for (const label of [
18691
18873
  "snapshot",
18692
18874
  "tree",
@@ -18717,7 +18899,7 @@ async function preparePendingUpload(context, contributionId, filename, buffer) {
18717
18899
  let status = "failed";
18718
18900
  try {
18719
18901
  if (saved) {
18720
- const bytes = fs20.readFileSync(path19.join(directory, saved.file));
18902
+ const bytes = fs21.readFileSync(path20.join(directory, saved.file));
18721
18903
  if (bytes.length !== saved.bytes || crypto7.createHash("sha256").update(bytes).digest("hex") !== saved.sha256) {
18722
18904
  throw new Error(
18723
18905
  `Pending Git artifact is corrupt: ${saved.file}; capture retained`
@@ -18737,7 +18919,7 @@ async function preparePendingUpload(context, contributionId, filename, buffer) {
18737
18919
  );
18738
18920
  }
18739
18921
  const file = `${crypto7.createHash("sha256").update(key).digest("hex")}.artifact`;
18740
- writeDurable(path19.join(directory, file), buffer);
18922
+ writeDurable(path20.join(directory, file), buffer);
18741
18923
  context.record.uploads[key] = {
18742
18924
  file,
18743
18925
  bytes: buffer.byteLength,
@@ -18747,7 +18929,7 @@ async function preparePendingUpload(context, contributionId, filename, buffer) {
18747
18929
  context.save();
18748
18930
  } catch (err) {
18749
18931
  delete context.record.uploads[key];
18750
- fs20.rmSync(path19.join(directory, file), { force: true });
18932
+ fs21.rmSync(path20.join(directory, file), { force: true });
18751
18933
  throw err;
18752
18934
  }
18753
18935
  status = "ok";
@@ -18779,8 +18961,8 @@ function readPreparedCaptureArtifact(context, filename, input) {
18779
18961
  throw new Error(
18780
18962
  `Prepared Git artifact input changed: ${filename}; capture retained`
18781
18963
  );
18782
- const bytes = fs20.readFileSync(
18783
- path19.join(captureDirectory(context.record), saved.file)
18964
+ const bytes = fs21.readFileSync(
18965
+ path20.join(captureDirectory(context.record), saved.file)
18784
18966
  );
18785
18967
  if (bytes.length !== saved.bytes || crypto7.createHash("sha256").update(bytes).digest("hex") !== saved.sha256)
18786
18968
  throw new Error(
@@ -18799,7 +18981,7 @@ function completePendingCapture(record) {
18799
18981
  function removeCompletedCapture(record) {
18800
18982
  if (!record.completed)
18801
18983
  throw new Error("Cannot remove an unacknowledged Git capture");
18802
- fs20.rmSync(captureDirectory(record), { recursive: true, force: true });
18984
+ fs21.rmSync(captureDirectory(record), { recursive: true, force: true });
18803
18985
  for (const label of ["snapshot", "tree", "head", "baseline", "previous"]) {
18804
18986
  deleteRef(
18805
18987
  record.repoRoot,
@@ -18872,7 +19054,7 @@ async function uploadArtifact(client, contributionId, filename, mimeType, buffer
18872
19054
  }
18873
19055
 
18874
19056
  // package.json
18875
- var version = "0.9.13";
19057
+ var version = "0.9.14";
18876
19058
 
18877
19059
  // src/version.ts
18878
19060
  var CLI_VERSION = version;
@@ -18896,7 +19078,7 @@ function lineHasAssistant(line) {
18896
19078
  return line.includes('"type":"assistant"') || line.includes('"role":"assistant"') || line.includes('"type":"assistant.');
18897
19079
  }
18898
19080
  async function hasAssistantMessage(transcriptPath) {
18899
- const stream = fs21.createReadStream(transcriptPath, { encoding: "utf-8" });
19081
+ const stream = fs22.createReadStream(transcriptPath, { encoding: "utf-8" });
18900
19082
  let buffer = "";
18901
19083
  try {
18902
19084
  for await (const chunk of stream) {
@@ -18983,8 +19165,8 @@ function resolveCursorTranscriptPath(payload) {
18983
19165
  const workspace = payload.workspace_roots?.[0];
18984
19166
  if (!id || !workspace) return void 0;
18985
19167
  const encoded = workspace.replace(/^\//, "").replace(/\//g, "-");
18986
- return path20.join(
18987
- os10.homedir(),
19168
+ return path21.join(
19169
+ os11.homedir(),
18988
19170
  ".cursor",
18989
19171
  "projects",
18990
19172
  encoded,
@@ -19039,14 +19221,14 @@ async function sweepClaudeSubagentTranscripts(payload) {
19039
19221
  const cwd = resolveHookCwd(payload);
19040
19222
  if (!parentSessionId || !cwd || !payload.transcript_path) return;
19041
19223
  try {
19042
- const directory = path20.join(
19043
- path20.dirname(path20.resolve(payload.transcript_path)),
19224
+ const directory = path21.join(
19225
+ path21.dirname(path21.resolve(payload.transcript_path)),
19044
19226
  parentSessionId,
19045
19227
  "subagents"
19046
19228
  );
19047
19229
  let names;
19048
19230
  try {
19049
- names = await fs21.promises.readdir(directory);
19231
+ names = await fs22.promises.readdir(directory);
19050
19232
  } catch (err) {
19051
19233
  if (err.code === "ENOENT") return;
19052
19234
  throw err;
@@ -19055,12 +19237,12 @@ async function sweepClaudeSubagentTranscripts(payload) {
19055
19237
  for (const name of names) {
19056
19238
  const match = /^agent-([A-Za-z0-9_-]+)\.jsonl$/.exec(name);
19057
19239
  if (!match) continue;
19058
- const file = path20.join(directory, name);
19240
+ const file = path21.join(directory, name);
19059
19241
  try {
19060
19242
  files.push({
19061
19243
  agentId: match[1],
19062
19244
  file,
19063
- modifiedMs: (await fs21.promises.stat(file)).mtimeMs
19245
+ modifiedMs: (await fs22.promises.stat(file)).mtimeMs
19064
19246
  });
19065
19247
  } catch {
19066
19248
  }
@@ -19226,7 +19408,7 @@ async function sweepCodexForkTranscripts(payload) {
19226
19408
  const parent = await findProjectForCwd(cwd);
19227
19409
  if (!parent) return;
19228
19410
  const registeredRoots = Object.keys((await loadProjects()).projects).map(
19229
- (root) => path20.resolve(root)
19411
+ (root) => path21.resolve(root)
19230
19412
  );
19231
19413
  const discovery = await discoverCodexForkThreads({
19232
19414
  parentThreadId: parentSessionId,
@@ -19367,9 +19549,9 @@ async function runUploadForSession(payload, options) {
19367
19549
  );
19368
19550
  return "skipped";
19369
19551
  }
19370
- const transcriptResolved = path20.resolve(transcriptPath);
19552
+ const transcriptResolved = path21.resolve(transcriptPath);
19371
19553
  try {
19372
- const stat = await fs21.promises.stat(transcriptResolved);
19554
+ const stat = await fs22.promises.stat(transcriptResolved);
19373
19555
  if (!stat.isFile()) {
19374
19556
  appendLog(
19375
19557
  "warn",
@@ -19425,7 +19607,7 @@ function makeTranscriptGroup(repoRoot, sourceTool, transcriptPath, content) {
19425
19607
  };
19426
19608
  return {
19427
19609
  repoPath: repoRoot,
19428
- label: path20.basename(repoRoot),
19610
+ label: path21.basename(repoRoot),
19429
19611
  files: [sourceFile],
19430
19612
  sourceNames: [sourceTool],
19431
19613
  lastModified: /* @__PURE__ */ new Date()
@@ -19436,7 +19618,7 @@ async function buildRedactChain(repoRoot, capturedSecrets = []) {
19436
19618
  "secret-discovery",
19437
19619
  () => discoverEnvFiles(repoRoot)
19438
19620
  );
19439
- const envFilePaths = envFileNames.map((n) => path20.join(repoRoot, n));
19621
+ const envFilePaths = envFileNames.map((n) => path21.join(repoRoot, n));
19440
19622
  const secretResult = await measureAgentStage(
19441
19623
  "secret-collection",
19442
19624
  () => collectSecrets(repoRoot, envFilePaths, [])
@@ -20085,7 +20267,7 @@ async function uploadSession(args) {
20085
20267
  try {
20086
20268
  raw = await measureAgentStage(
20087
20269
  "transcript-read",
20088
- () => captured ? Promise.resolve(captured.bytes) : fs21.promises.readFile(transcriptPath)
20270
+ () => captured ? Promise.resolve(captured.bytes) : fs22.promises.readFile(transcriptPath)
20089
20271
  );
20090
20272
  } catch (err) {
20091
20273
  if (err.code === "ERR_FS_FILE_TOO_LARGE") {
@@ -20456,8 +20638,8 @@ import crypto9 from "crypto";
20456
20638
 
20457
20639
  // src/git-traces/handlers.ts
20458
20640
  import { execFileSync as execFileSync3 } from "child_process";
20459
- import fs22 from "fs";
20460
- import path21 from "path";
20641
+ import fs23 from "fs";
20642
+ import path22 from "path";
20461
20643
  var GIT_TRACES_SLUG = "git-traces";
20462
20644
  var MAX_FORK_SWEEP_THREADS2 = 8;
20463
20645
  var MAX_UPLOAD_BYTES = 500 * 1024 * 1024;
@@ -20465,7 +20647,7 @@ var MAX_BASELINE_BUNDLE_UPLOAD_BYTES = 1024 * 1024 * 1024;
20465
20647
  var STALE_SCOPED_STATE_TTL_MS = 24 * 60 * 60 * 1e3;
20466
20648
  var REBASELINE_AFTER_DIFF_FAILURES = 2;
20467
20649
  async function withRepoCaptureLock(repoRoot, capture) {
20468
- const canonicalRoot = fs22.realpathSync(repoRoot);
20650
+ const canonicalRoot = fs23.realpathSync(repoRoot);
20469
20651
  await acquireLock3(canonicalRoot, "snapshot-index-v1", null);
20470
20652
  try {
20471
20653
  return capture();
@@ -20523,7 +20705,7 @@ var PendingQueueConflictError = class extends Error {
20523
20705
  async function loadConfiguredRepos() {
20524
20706
  const file = await loadProjects();
20525
20707
  return Object.entries(file.projects).map(([repoRoot, config]) => ({
20526
- repoRoot: path21.resolve(repoRoot),
20708
+ repoRoot: path22.resolve(repoRoot),
20527
20709
  config
20528
20710
  })).sort((a, b) => a.repoRoot.localeCompare(b.repoRoot));
20529
20711
  }
@@ -20541,12 +20723,12 @@ async function configuredReposFor(trigger) {
20541
20723
  return repos;
20542
20724
  }
20543
20725
  async function propagateCodexHooks(parentRoot, worktreeRoot) {
20544
- const source = path21.join(parentRoot, ".codex", "hooks.json");
20545
- const target = path21.join(worktreeRoot, ".codex", "hooks.json");
20546
- if (fs22.existsSync(target) || !fs22.existsSync(source)) return;
20726
+ const source = path22.join(parentRoot, ".codex", "hooks.json");
20727
+ const target = path22.join(worktreeRoot, ".codex", "hooks.json");
20728
+ if (fs23.existsSync(target) || !fs23.existsSync(source)) return;
20547
20729
  try {
20548
- fs22.mkdirSync(path21.dirname(target), { recursive: true });
20549
- fs22.copyFileSync(source, target, fs22.constants.COPYFILE_EXCL);
20730
+ fs23.mkdirSync(path22.dirname(target), { recursive: true });
20731
+ fs23.copyFileSync(source, target, fs23.constants.COPYFILE_EXCL);
20550
20732
  } catch (err) {
20551
20733
  appendLog(
20552
20734
  "warn",
@@ -20568,7 +20750,7 @@ async function propagateCodexHooks(parentRoot, worktreeRoot) {
20568
20750
  }
20569
20751
  }
20570
20752
  function repoLabel(repoRoot) {
20571
- return path21.basename(repoRoot) || repoRoot;
20753
+ return path22.basename(repoRoot) || repoRoot;
20572
20754
  }
20573
20755
  function resolveCwd2(payload) {
20574
20756
  return resolveHookCwd(payload);
@@ -22838,7 +23020,7 @@ async function collectStopTargets(tool, sessionId, repos, repoByRoot) {
22838
23020
  let needsLineageCheck = false;
22839
23021
  const storedStates = await listSessionStatesForSession(tool, sessionId);
22840
23022
  for (const { state } of storedStates) {
22841
- const repo = repoByRoot.get(path21.resolve(state.repoRoot));
23023
+ const repo = repoByRoot.get(path22.resolve(state.repoRoot));
22842
23024
  if (!repo) {
22843
23025
  missingConfig++;
22844
23026
  appendLog(
@@ -22870,7 +23052,7 @@ async function lateInitSkipReason(payload, tool) {
22870
23052
  const transcriptPath = payload.transcript_path;
22871
23053
  if (!transcriptPath) return tool === "codex" ? null : "no-transcript-path";
22872
23054
  try {
22873
- const stat = await fs22.promises.stat(path21.resolve(transcriptPath));
23055
+ const stat = await fs23.promises.stat(path22.resolve(transcriptPath));
22874
23056
  return stat.isFile() ? null : "transcript-not-a-file";
22875
23057
  } catch {
22876
23058
  return "transcript-missing";
@@ -23079,9 +23261,9 @@ async function handleSessionEnd(payload, tool) {
23079
23261
  if (sessionId) {
23080
23262
  const states = await listSessionStatesForSession(tool, sessionId);
23081
23263
  for (const { state } of states) {
23082
- const repoRoot = path21.resolve(state.repoRoot);
23264
+ const repoRoot = path22.resolve(state.repoRoot);
23083
23265
  repoRoots.add(repoRoot);
23084
- const canProcess = repoByRoot.has(repoRoot) || project && path21.resolve(project.repoRoot) === repoRoot;
23266
+ const canProcess = repoByRoot.has(repoRoot) || project && path22.resolve(project.repoRoot) === repoRoot;
23085
23267
  if (canProcess && tool === "codex" && state.codexLineageChecked !== true) {
23086
23268
  needsLineageCheck = true;
23087
23269
  }
@@ -23118,7 +23300,7 @@ async function handleSessionEnd(payload, tool) {
23118
23300
  let rejected = 0;
23119
23301
  const finalCaptures = /* @__PURE__ */ new Map();
23120
23302
  for (const repoRoot of repoRoots) {
23121
- const repo = repoByRoot.get(path21.resolve(repoRoot)) ?? (project && path21.resolve(project.repoRoot) === path21.resolve(repoRoot) ? { repoRoot: project.repoRoot, config: project.config } : null);
23303
+ const repo = repoByRoot.get(path22.resolve(repoRoot)) ?? (project && path22.resolve(project.repoRoot) === path22.resolve(repoRoot) ? { repoRoot: project.repoRoot, config: project.config } : null);
23122
23304
  if (repo)
23123
23305
  finalCaptures.set(
23124
23306
  repoRoot,
@@ -23126,7 +23308,7 @@ async function handleSessionEnd(payload, tool) {
23126
23308
  );
23127
23309
  }
23128
23310
  for (const repoRoot of repoRoots) {
23129
- const repo = repoByRoot.get(path21.resolve(repoRoot)) ?? (project && path21.resolve(project.repoRoot) === path21.resolve(repoRoot) ? { repoRoot: project.repoRoot, config: project.config } : null);
23311
+ const repo = repoByRoot.get(path22.resolve(repoRoot)) ?? (project && path22.resolve(project.repoRoot) === path22.resolve(repoRoot) ? { repoRoot: project.repoRoot, config: project.config } : null);
23130
23312
  if (!repo) {
23131
23313
  skipped++;
23132
23314
  appendLog(
@@ -23505,29 +23687,29 @@ ${stack}` : ""}`
23505
23687
  }
23506
23688
 
23507
23689
  // src/outputs/zip.ts
23508
- import fs24 from "fs";
23509
- import path23 from "path";
23690
+ import fs25 from "fs";
23691
+ import path24 from "path";
23510
23692
  import archiver2 from "archiver";
23511
23693
 
23512
23694
  // src/outputs/downloads.ts
23513
23695
  import { execSync as execSync2 } from "child_process";
23514
- import fs23 from "fs";
23515
- import os11 from "os";
23516
- import path22 from "path";
23696
+ import fs24 from "fs";
23697
+ import os12 from "os";
23698
+ import path23 from "path";
23517
23699
  function getDownloadsFolder() {
23518
- const home = os11.homedir();
23700
+ const home = os12.homedir();
23519
23701
  if (process.platform === "linux") {
23520
23702
  try {
23521
23703
  const xdgDir = execSync2("xdg-user-dir DOWNLOAD", {
23522
23704
  encoding: "utf-8",
23523
23705
  timeout: 3e3
23524
23706
  }).trim();
23525
- if (xdgDir && fs23.existsSync(xdgDir)) return xdgDir;
23707
+ if (xdgDir && fs24.existsSync(xdgDir)) return xdgDir;
23526
23708
  } catch {
23527
23709
  }
23528
23710
  }
23529
- const downloads = path22.join(home, "Downloads");
23530
- if (fs23.existsSync(downloads)) return downloads;
23711
+ const downloads = path23.join(home, "Downloads");
23712
+ if (fs24.existsSync(downloads)) return downloads;
23531
23713
  return home;
23532
23714
  }
23533
23715
 
@@ -23536,11 +23718,11 @@ function sanitizeFilename(name) {
23536
23718
  return name.replace(/[^a-zA-Z0-9._-]/g, "_");
23537
23719
  }
23538
23720
  function getUniqueFilename(dir, base, ext) {
23539
- let candidate = path23.join(dir, `${base}${ext}`);
23540
- if (!fs24.existsSync(candidate)) return candidate;
23721
+ let candidate = path24.join(dir, `${base}${ext}`);
23722
+ if (!fs25.existsSync(candidate)) return candidate;
23541
23723
  let i = 1;
23542
- while (fs24.existsSync(candidate)) {
23543
- candidate = path23.join(dir, `${base}-${i}${ext}`);
23724
+ while (fs25.existsSync(candidate)) {
23725
+ candidate = path24.join(dir, `${base}-${i}${ext}`);
23544
23726
  i++;
23545
23727
  }
23546
23728
  return candidate;
@@ -23550,13 +23732,13 @@ var ZipOutput = class {
23550
23732
  label = "Save as .zip to Downloads";
23551
23733
  async emit(group, options) {
23552
23734
  const downloadsDir = getDownloadsFolder();
23553
- const repoName = sanitizeFilename(path23.basename(group.repoPath));
23735
+ const repoName = sanitizeFilename(path24.basename(group.repoPath));
23554
23736
  const timeRange = options.timeRange;
23555
23737
  const rangePart = timeRange?.label ?? "all";
23556
23738
  const epochSeconds = Math.floor(Date.now() / 1e3);
23557
23739
  const baseName = `logs-${repoName}-${rangePart}-${epochSeconds}`;
23558
23740
  const outputPath = getUniqueFilename(downloadsDir, baseName, ".zip");
23559
- const output = fs24.createWriteStream(outputPath);
23741
+ const output = fs25.createWriteStream(outputPath);
23560
23742
  const archive = archiver2("zip", { zlib: { level: 6 } });
23561
23743
  const done = new Promise((resolve, reject) => {
23562
23744
  output.on("close", resolve);
@@ -23750,15 +23932,15 @@ async function confirmExport(group, output) {
23750
23932
  }
23751
23933
 
23752
23934
  // src/sources/claude.ts
23753
- import fs25 from "fs";
23754
- import os12 from "os";
23755
- import path24 from "path";
23935
+ import fs26 from "fs";
23936
+ import os13 from "os";
23937
+ import path25 from "path";
23756
23938
  import readline3 from "readline";
23757
23939
  var SKIP_DIRS = /* @__PURE__ */ new Set(["tool-results"]);
23758
23940
  async function resolveRepoPath(projectDir) {
23759
- const indexPath = path24.join(projectDir, "sessions-index.json");
23941
+ const indexPath = path25.join(projectDir, "sessions-index.json");
23760
23942
  try {
23761
- const raw = await fs25.promises.readFile(indexPath, "utf-8");
23943
+ const raw = await fs26.promises.readFile(indexPath, "utf-8");
23762
23944
  const data = JSON.parse(raw);
23763
23945
  if (data.originalPath && typeof data.originalPath === "string") {
23764
23946
  return data.originalPath;
@@ -23766,12 +23948,12 @@ async function resolveRepoPath(projectDir) {
23766
23948
  } catch {
23767
23949
  }
23768
23950
  const cwdCounts = /* @__PURE__ */ new Map();
23769
- const entries = await fs25.promises.readdir(projectDir, {
23951
+ const entries = await fs26.promises.readdir(projectDir, {
23770
23952
  withFileTypes: true
23771
23953
  });
23772
23954
  for (const entry of entries) {
23773
23955
  if (!entry.isFile() || !entry.name.endsWith(".jsonl")) continue;
23774
- const cwd = await extractCwdFromJsonl(path24.join(projectDir, entry.name));
23956
+ const cwd = await extractCwdFromJsonl(path25.join(projectDir, entry.name));
23775
23957
  if (cwd) {
23776
23958
  cwdCounts.set(cwd, (cwdCounts.get(cwd) ?? 0) + 1);
23777
23959
  }
@@ -23790,7 +23972,7 @@ async function resolveRepoPath(projectDir) {
23790
23972
  return null;
23791
23973
  }
23792
23974
  async function extractCwdFromJsonl(filePath) {
23793
- const stream = fs25.createReadStream(filePath, { encoding: "utf-8" });
23975
+ const stream = fs26.createReadStream(filePath, { encoding: "utf-8" });
23794
23976
  const rl = readline3.createInterface({ input: stream, crlfDelay: Infinity });
23795
23977
  try {
23796
23978
  for await (const line of rl) {
@@ -23812,12 +23994,12 @@ async function extractCwdFromJsonl(filePath) {
23812
23994
  async function collectFiles(dir, baseDir, sourceName, repoPath, results) {
23813
23995
  let entries;
23814
23996
  try {
23815
- entries = await fs25.promises.readdir(dir, { withFileTypes: true });
23997
+ entries = await fs26.promises.readdir(dir, { withFileTypes: true });
23816
23998
  } catch {
23817
23999
  return;
23818
24000
  }
23819
24001
  for (const entry of entries) {
23820
- const fullPath = path24.join(dir, entry.name);
24002
+ const fullPath = path25.join(dir, entry.name);
23821
24003
  if (entry.isDirectory()) {
23822
24004
  if (SKIP_DIRS.has(entry.name)) continue;
23823
24005
  await collectFiles(fullPath, baseDir, sourceName, repoPath, results);
@@ -23839,19 +24021,19 @@ function fallbackDecode(encodedName) {
23839
24021
  var ClaudeSource = class {
23840
24022
  name = "claude";
23841
24023
  async scan() {
23842
- const baseDir = path24.join(os12.homedir(), ".claude", "projects");
24024
+ const baseDir = path25.join(os13.homedir(), ".claude", "projects");
23843
24025
  try {
23844
- await fs25.promises.access(baseDir);
24026
+ await fs26.promises.access(baseDir);
23845
24027
  } catch {
23846
24028
  return [];
23847
24029
  }
23848
- const projectDirs = await fs25.promises.readdir(baseDir, {
24030
+ const projectDirs = await fs26.promises.readdir(baseDir, {
23849
24031
  withFileTypes: true
23850
24032
  });
23851
24033
  const dirEntries = projectDirs.filter((d) => d.isDirectory());
23852
24034
  const resultArrays = await Promise.all(
23853
24035
  dirEntries.map(async (dir) => {
23854
- const projectPath = path24.join(baseDir, dir.name);
24036
+ const projectPath = path25.join(baseDir, dir.name);
23855
24037
  const repoPath = await resolveRepoPath(projectPath) ?? fallbackDecode(dir.name);
23856
24038
  const files = [];
23857
24039
  await collectFiles(
@@ -23869,12 +24051,12 @@ var ClaudeSource = class {
23869
24051
  };
23870
24052
 
23871
24053
  // src/sources/codex.ts
23872
- import fs26 from "fs";
23873
- import os13 from "os";
23874
- import path25 from "path";
24054
+ import fs27 from "fs";
24055
+ import os14 from "os";
24056
+ import path26 from "path";
23875
24057
  import readline4 from "readline";
23876
24058
  async function parseSessionMeta2(filePath) {
23877
- const stream = fs26.createReadStream(filePath, { encoding: "utf-8" });
24059
+ const stream = fs27.createReadStream(filePath, { encoding: "utf-8" });
23878
24060
  const rl = readline4.createInterface({ input: stream, crlfDelay: Infinity });
23879
24061
  try {
23880
24062
  for await (const line of rl) {
@@ -23899,12 +24081,12 @@ async function findJsonlFiles(dir) {
23899
24081
  async function walk(d) {
23900
24082
  let entries;
23901
24083
  try {
23902
- entries = await fs26.promises.readdir(d, { withFileTypes: true });
24084
+ entries = await fs27.promises.readdir(d, { withFileTypes: true });
23903
24085
  } catch {
23904
24086
  return;
23905
24087
  }
23906
24088
  for (const entry of entries) {
23907
- const full = path25.join(d, entry.name);
24089
+ const full = path26.join(d, entry.name);
23908
24090
  if (entry.isDirectory()) {
23909
24091
  await walk(full);
23910
24092
  } else if (entry.isFile() && entry.name.endsWith(".jsonl")) {
@@ -23918,11 +24100,11 @@ async function findJsonlFiles(dir) {
23918
24100
  async function loadHistory(historyPath) {
23919
24101
  const map = /* @__PURE__ */ new Map();
23920
24102
  try {
23921
- await fs26.promises.access(historyPath);
24103
+ await fs27.promises.access(historyPath);
23922
24104
  } catch {
23923
24105
  return map;
23924
24106
  }
23925
- const stream = fs26.createReadStream(historyPath, { encoding: "utf-8" });
24107
+ const stream = fs27.createReadStream(historyPath, { encoding: "utf-8" });
23926
24108
  const rl = readline4.createInterface({ input: stream, crlfDelay: Infinity });
23927
24109
  try {
23928
24110
  for await (const line of rl) {
@@ -23949,14 +24131,14 @@ async function loadHistory(historyPath) {
23949
24131
  var CodexSource = class {
23950
24132
  name = "codex";
23951
24133
  async scan() {
23952
- const codexDir = path25.join(os13.homedir(), ".codex");
23953
- const sessionsDir2 = path25.join(codexDir, "sessions");
24134
+ const codexDir = path26.join(os14.homedir(), ".codex");
24135
+ const sessionsDir2 = path26.join(codexDir, "sessions");
23954
24136
  try {
23955
- await fs26.promises.access(sessionsDir2);
24137
+ await fs27.promises.access(sessionsDir2);
23956
24138
  } catch {
23957
24139
  return [];
23958
24140
  }
23959
- const historyPath = path25.join(codexDir, "history.jsonl");
24141
+ const historyPath = path26.join(codexDir, "history.jsonl");
23960
24142
  const [jsonlFiles, historyMap] = await Promise.all([
23961
24143
  findJsonlFiles(sessionsDir2),
23962
24144
  loadHistory(historyPath)
@@ -23979,8 +24161,8 @@ var CodexSource = class {
23979
24161
  });
23980
24162
  const historyLines = historyMap.get(meta.sessionId);
23981
24163
  if (historyLines) {
23982
- const sessionDir = path25.relative(sessionsDir2, path25.dirname(filePath));
23983
- const historyAbsPath = path25.join(
24164
+ const sessionDir = path26.relative(sessionsDir2, path26.dirname(filePath));
24165
+ const historyAbsPath = path26.join(
23984
24166
  sessionsDir2,
23985
24167
  sessionDir,
23986
24168
  `history-${meta.sessionId}.jsonl`
@@ -24000,18 +24182,18 @@ var CodexSource = class {
24000
24182
  };
24001
24183
 
24002
24184
  // src/sources/copilotChat.ts
24003
- import fs27 from "fs";
24004
- import os14 from "os";
24005
- import path26 from "path";
24185
+ import fs28 from "fs";
24186
+ import os15 from "os";
24187
+ import path27 from "path";
24006
24188
  import { fileURLToPath as fileURLToPath2 } from "url";
24007
24189
  function vsCodeUserDirs() {
24008
- const home = os14.homedir();
24190
+ const home = os15.homedir();
24009
24191
  const dirs = [
24010
- path26.join(home, "Library", "Application Support", "Code", "User"),
24011
- path26.join(home, ".config", "Code", "User")
24192
+ path27.join(home, "Library", "Application Support", "Code", "User"),
24193
+ path27.join(home, ".config", "Code", "User")
24012
24194
  ];
24013
24195
  if (process.env.APPDATA) {
24014
- dirs.push(path26.join(process.env.APPDATA, "Code", "User"));
24196
+ dirs.push(path27.join(process.env.APPDATA, "Code", "User"));
24015
24197
  }
24016
24198
  return dirs;
24017
24199
  }
@@ -24026,7 +24208,7 @@ function uriToFsPath(uri) {
24026
24208
  async function readWorkspaceFolder(workspaceJsonPath) {
24027
24209
  let raw;
24028
24210
  try {
24029
- raw = await fs27.promises.readFile(workspaceJsonPath, "utf-8");
24211
+ raw = await fs28.promises.readFile(workspaceJsonPath, "utf-8");
24030
24212
  } catch {
24031
24213
  return null;
24032
24214
  }
@@ -24048,10 +24230,10 @@ var CopilotChatSource = class {
24048
24230
  async scan() {
24049
24231
  const results = [];
24050
24232
  for (const userDir of vsCodeUserDirs()) {
24051
- const workspaceStorage = path26.join(userDir, "workspaceStorage");
24233
+ const workspaceStorage = path27.join(userDir, "workspaceStorage");
24052
24234
  let hashDirs;
24053
24235
  try {
24054
- hashDirs = await fs27.promises.readdir(workspaceStorage, {
24236
+ hashDirs = await fs28.promises.readdir(workspaceStorage, {
24055
24237
  withFileTypes: true
24056
24238
  });
24057
24239
  } catch {
@@ -24059,22 +24241,22 @@ var CopilotChatSource = class {
24059
24241
  }
24060
24242
  for (const hash of hashDirs) {
24061
24243
  if (!hash.isDirectory()) continue;
24062
- const wsRoot = path26.join(workspaceStorage, hash.name);
24063
- const transcriptsDir = path26.join(
24244
+ const wsRoot = path27.join(workspaceStorage, hash.name);
24245
+ const transcriptsDir = path27.join(
24064
24246
  wsRoot,
24065
24247
  "GitHub.copilot-chat",
24066
24248
  "transcripts"
24067
24249
  );
24068
24250
  let transcriptEntries;
24069
24251
  try {
24070
- transcriptEntries = await fs27.promises.readdir(transcriptsDir, {
24252
+ transcriptEntries = await fs28.promises.readdir(transcriptsDir, {
24071
24253
  withFileTypes: true
24072
24254
  });
24073
24255
  } catch {
24074
24256
  continue;
24075
24257
  }
24076
24258
  const repoPath = await readWorkspaceFolder(
24077
- path26.join(wsRoot, "workspace.json")
24259
+ path27.join(wsRoot, "workspace.json")
24078
24260
  );
24079
24261
  if (!repoPath) continue;
24080
24262
  for (const entry of transcriptEntries) {
@@ -24082,7 +24264,7 @@ var CopilotChatSource = class {
24082
24264
  const sessionId = entry.name.slice(0, -".jsonl".length);
24083
24265
  results.push({
24084
24266
  sourceName: this.name,
24085
- absolutePath: path26.join(transcriptsDir, entry.name),
24267
+ absolutePath: path27.join(transcriptsDir, entry.name),
24086
24268
  repoPath,
24087
24269
  metadata: { sessionId }
24088
24270
  });
@@ -24122,7 +24304,7 @@ function reportRedactionStats(noun, stats) {
24122
24304
  async function filterByTimeRange(group, range) {
24123
24305
  const results = await Promise.all(
24124
24306
  group.files.map(
24125
- (f) => fs28.promises.stat(f.absolutePath).then((s) => ({ file: f, stat: s })).catch(() => null)
24307
+ (f) => fs29.promises.stat(f.absolutePath).then((s) => ({ file: f, stat: s })).catch(() => null)
24126
24308
  )
24127
24309
  );
24128
24310
  const filtered = [];
@@ -24149,10 +24331,10 @@ async function runInteractive() {
24149
24331
  s.start(`Scanning ${source.name} logs...`);
24150
24332
  const allFiles = await source.scan();
24151
24333
  const allGroups = await mergeByRepo(allFiles);
24152
- const repoRoot = path27.resolve(repo.root);
24334
+ const repoRoot = path28.resolve(repo.root);
24153
24335
  const matching = allGroups.filter((g) => {
24154
- const resolved = path27.resolve(g.repoPath);
24155
- return resolved === repoRoot || resolved.startsWith(repoRoot + path27.sep);
24336
+ const resolved = path28.resolve(g.repoPath);
24337
+ return resolved === repoRoot || resolved.startsWith(repoRoot + path28.sep);
24156
24338
  });
24157
24339
  if (matching.length === 0) {
24158
24340
  s.stop(`No ${source.name} logs found for ${repo.name}.`);
@@ -24183,7 +24365,7 @@ async function runInteractive() {
24183
24365
  }
24184
24366
  }
24185
24367
  const envFileNames = await discoverEnvFiles(repoRoot);
24186
- const envFilePaths = envFileNames.map((n) => path27.join(repoRoot, n));
24368
+ const envFilePaths = envFileNames.map((n) => path28.join(repoRoot, n));
24187
24369
  const additionalFiles = await promptSecretFiles(envFileNames);
24188
24370
  const secretResult = await collectSecrets(
24189
24371
  repoRoot,