hillclimb 0.9.4 → 0.9.5

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.
Files changed (2) hide show
  1. package/dist/main.js +359 -202
  2. package/package.json +1 -1
package/dist/main.js CHANGED
@@ -13,8 +13,8 @@ import {
13
13
  } from "./chunk-UPXMA7RC.js";
14
14
 
15
15
  // src/main.ts
16
- import fs27 from "fs";
17
- import path26 from "path";
16
+ import fs28 from "fs";
17
+ import path27 from "path";
18
18
  import * as p6 from "@clack/prompts";
19
19
 
20
20
  // src/commands/init.ts
@@ -2714,9 +2714,9 @@ async function runStatus(args = []) {
2714
2714
  // src/commands/upload.ts
2715
2715
  import { spawn as spawn3 } from "child_process";
2716
2716
  import crypto8 from "crypto";
2717
- import fs20 from "fs";
2717
+ import fs21 from "fs";
2718
2718
  import os10 from "os";
2719
- import path19 from "path";
2719
+ import path20 from "path";
2720
2720
 
2721
2721
  // src/agent-pending.ts
2722
2722
  import crypto4 from "crypto";
@@ -15178,16 +15178,144 @@ async function recordDebugLogCompletion(args) {
15178
15178
  // src/git-traces/pending-captures.ts
15179
15179
  import { AsyncLocalStorage } from "async_hooks";
15180
15180
  import crypto7 from "crypto";
15181
- import fs19 from "fs";
15181
+ import fs20 from "fs";
15182
15182
  import os9 from "os";
15183
- import path18 from "path";
15183
+ import path19 from "path";
15184
15184
 
15185
15185
  // src/git-traces/git-ops.ts
15186
15186
  import { execFileSync as execFileSync2, spawn as spawn2, spawnSync } from "child_process";
15187
- import fs17 from "fs";
15187
+ import fs18 from "fs";
15188
15188
  import os7 from "os";
15189
- import path16 from "path";
15189
+ import path17 from "path";
15190
15190
  import { createGzip } from "zlib";
15191
+
15192
+ // src/git-traces/object-store-probe.ts
15193
+ import fs17 from "fs";
15194
+ import path16 from "path";
15195
+ var probedRepos = /* @__PURE__ */ new Set();
15196
+ var SAMPLE_FANOUT_DIRS = 8;
15197
+ var SAMPLE_READS = 20;
15198
+ var SAMPLE_READ_BYTES = 4096;
15199
+ var MAX_NAMED_OBJECTS = 3;
15200
+ function probeObjectStore(repoRoot, trigger, stderr) {
15201
+ if (probedRepos.has(repoRoot)) return;
15202
+ probedRepos.add(repoRoot);
15203
+ const started = Date.now();
15204
+ const fields = [`repo=${repoRoot}`, `trigger=${trigger}`];
15205
+ try {
15206
+ const objectsDir = resolveObjectsDir(repoRoot);
15207
+ if (!objectsDir) {
15208
+ fields.push("objectsDir=unresolved");
15209
+ return;
15210
+ }
15211
+ fields.push(`objectsDir=${objectsDir}`);
15212
+ if (typeof fs17.statfsSync === "function") {
15213
+ const stats = fs17.statfsSync(objectsDir);
15214
+ fields.push(
15215
+ `fsType=${stats.type}`,
15216
+ `freeBytes=${stats.bavail * stats.bsize}`
15217
+ );
15218
+ }
15219
+ const { estimate, sample } = sampleLooseObjects(objectsDir);
15220
+ fields.push(`looseEstimate=${estimate}`);
15221
+ const packDir = path16.join(objectsDir, "pack");
15222
+ const packFiles = fs17.existsSync(packDir) ? fs17.readdirSync(packDir) : [];
15223
+ fields.push(
15224
+ `packs=${packFiles.filter((name) => name.endsWith(".pack")).length}`,
15225
+ `promisorPacks=${packFiles.filter((name) => name.endsWith(".promisor")).length}`,
15226
+ `alternates=${fs17.existsSync(path16.join(objectsDir, "info", "alternates")) ? "yes" : "no"}`
15227
+ );
15228
+ fields.push(...timeSampleReads(sample));
15229
+ for (const sha of namedObjects(stderr)) {
15230
+ const loose = path16.join(objectsDir, sha.slice(0, 2), sha.slice(2));
15231
+ const stats = fs17.statSync(loose, { throwIfNoEntry: false });
15232
+ fields.push(
15233
+ stats ? `named=${sha.slice(0, 8)}(size=${stats.size},blocks=${stats.blocks})` : `named=${sha.slice(0, 8)}(no loose file)`
15234
+ );
15235
+ }
15236
+ } catch (err) {
15237
+ fields.push(
15238
+ `probeError=${err instanceof Error ? err.message : String(err)}`
15239
+ );
15240
+ } finally {
15241
+ fields.push(`probeMs=${Date.now() - started}`);
15242
+ appendLog("info", `git-traces: object store probe (${fields.join(", ")})`);
15243
+ }
15244
+ }
15245
+ function resolveObjectsDir(repoRoot) {
15246
+ let gitDir = path16.join(repoRoot, ".git");
15247
+ const stats = fs17.statSync(gitDir, { throwIfNoEntry: false });
15248
+ if (!stats) return null;
15249
+ if (stats.isFile()) {
15250
+ const match = /^gitdir:\s*(.+)$/m.exec(fs17.readFileSync(gitDir, "utf-8"));
15251
+ if (!match) return null;
15252
+ gitDir = path16.resolve(repoRoot, match[1].trim());
15253
+ }
15254
+ const commonDir = path16.join(gitDir, "commondir");
15255
+ if (fs17.existsSync(commonDir)) {
15256
+ gitDir = path16.resolve(gitDir, fs17.readFileSync(commonDir, "utf-8").trim());
15257
+ }
15258
+ return path16.join(gitDir, "objects");
15259
+ }
15260
+ function sampleLooseObjects(objectsDir) {
15261
+ const fanout = fs17.readdirSync(objectsDir).filter((name) => /^[0-9a-f]{2}$/.test(name)).sort().slice(0, SAMPLE_FANOUT_DIRS);
15262
+ let counted = 0;
15263
+ const sample = [];
15264
+ for (const dir of fanout) {
15265
+ const files = fs17.readdirSync(path16.join(objectsDir, dir));
15266
+ counted += files.length;
15267
+ for (const file of files) {
15268
+ if (sample.length >= SAMPLE_READS) break;
15269
+ sample.push(path16.join(objectsDir, dir, file));
15270
+ }
15271
+ }
15272
+ return {
15273
+ estimate: fanout.length ? Math.round(counted / fanout.length * 256) : 0,
15274
+ sample
15275
+ };
15276
+ }
15277
+ function timeSampleReads(sample) {
15278
+ let dataless = 0;
15279
+ let readErrors = 0;
15280
+ let firstReadError;
15281
+ let totalMs = 0;
15282
+ let maxMs = 0;
15283
+ const buffer = Buffer.alloc(SAMPLE_READ_BYTES);
15284
+ for (const file of sample) {
15285
+ const readStarted = performance.now();
15286
+ try {
15287
+ const stats = fs17.statSync(file);
15288
+ if (stats.size > 0 && stats.blocks === 0) dataless++;
15289
+ const fd = fs17.openSync(file, "r");
15290
+ try {
15291
+ fs17.readSync(fd, buffer, 0, SAMPLE_READ_BYTES, 0);
15292
+ } finally {
15293
+ fs17.closeSync(fd);
15294
+ }
15295
+ } catch (err) {
15296
+ readErrors++;
15297
+ firstReadError ??= err instanceof Error ? err.message : String(err);
15298
+ }
15299
+ const elapsed = performance.now() - readStarted;
15300
+ totalMs += elapsed;
15301
+ maxMs = Math.max(maxMs, elapsed);
15302
+ }
15303
+ const fields = [
15304
+ `sampled=${sample.length}`,
15305
+ `dataless=${dataless}`,
15306
+ `readMsMax=${maxMs.toFixed(1)}`,
15307
+ `readMsTotal=${totalMs.toFixed(1)}`,
15308
+ `readErrors=${readErrors}`
15309
+ ];
15310
+ if (firstReadError) fields.push(`firstReadError=${firstReadError}`);
15311
+ return fields;
15312
+ }
15313
+ function namedObjects(stderr) {
15314
+ const shas = new Set(stderr?.match(/\b[0-9a-f]{40}\b/g) ?? []);
15315
+ return [...shas].slice(0, MAX_NAMED_OBJECTS);
15316
+ }
15317
+
15318
+ // src/git-traces/git-ops.ts
15191
15319
  var DEFAULT_GIT_COMMAND_TIMEOUT_MS = 12e4;
15192
15320
  var DEFAULT_BUNDLE_TIMEOUT_MS = 3e5;
15193
15321
  var FULL_TREE_SCAN_TIMEOUT_MS = 3e5;
@@ -15378,8 +15506,8 @@ var EXCLUDED_SNAPSHOT_BASENAMES = /* @__PURE__ */ new Set([
15378
15506
  ]);
15379
15507
  var EMPTY_TREE_SHA = "4b825dc642cb6eb9a060e54bf8d69288fbee4904";
15380
15508
  function isExcludedSnapshotPath(filePath) {
15381
- if (EXCLUDED_SNAPSHOT_BASENAMES.has(path16.basename(filePath))) return true;
15382
- return EXCLUDED_SNAPSHOT_EXTENSIONS.has(path16.extname(filePath).toLowerCase());
15509
+ if (EXCLUDED_SNAPSHOT_BASENAMES.has(path17.basename(filePath))) return true;
15510
+ return EXCLUDED_SNAPSHOT_EXTENSIONS.has(path17.extname(filePath).toLowerCase());
15383
15511
  }
15384
15512
  function isBinaryBuffer(buffer) {
15385
15513
  return buffer.includes(0);
@@ -15400,16 +15528,16 @@ function readTreeBlobHead(repoRoot, sha) {
15400
15528
  function readWorkingFileHead(absPath) {
15401
15529
  let fd = null;
15402
15530
  try {
15403
- fd = fs17.openSync(absPath, "r");
15531
+ fd = fs18.openSync(absPath, "r");
15404
15532
  const buffer = Buffer.alloc(BINARY_SNIFF_BYTES);
15405
- const bytesRead = fs17.readSync(fd, buffer, 0, BINARY_SNIFF_BYTES, 0);
15533
+ const bytesRead = fs18.readSync(fd, buffer, 0, BINARY_SNIFF_BYTES, 0);
15406
15534
  return buffer.subarray(0, bytesRead);
15407
15535
  } catch {
15408
15536
  return null;
15409
15537
  } finally {
15410
15538
  if (fd !== null) {
15411
15539
  try {
15412
- fs17.closeSync(fd);
15540
+ fs18.closeSync(fd);
15413
15541
  } catch {
15414
15542
  }
15415
15543
  }
@@ -15462,11 +15590,19 @@ function formatGitFailure(command, failure, err) {
15462
15590
  }
15463
15591
  return `${command} failed${details.length ? ` (${details.join("; ")})` : ""}`;
15464
15592
  }
15465
- function createGitError(args, err, timeoutMs = GIT_COMMAND_TIMEOUT_MS) {
15593
+ function formatGitTimeout(command, failure, timeoutMs, elapsedMs) {
15594
+ const details = [];
15595
+ if (elapsedMs !== void 0) details.push(`elapsed=${elapsedMs}ms`);
15596
+ if (failure.signal) details.push(`signal=${failure.signal}`);
15597
+ const stderr = outputToString(failure.stderr);
15598
+ if (stderr) details.push(`stderr=${truncateOutput(stderr)}`);
15599
+ return `${command} timed out after ${timeoutMs}ms${details.length ? ` (${details.join("; ")})` : ""}`;
15600
+ }
15601
+ function createGitError(args, err, timeoutMs = GIT_COMMAND_TIMEOUT_MS, elapsedMs) {
15466
15602
  const failure = err;
15467
15603
  const command = formatGitCommand(args);
15468
15604
  const isTimeout = failure.code === "ETIMEDOUT";
15469
- const message = isTimeout ? `${command} timed out after ${timeoutMs}ms` : failure.code === "ENOBUFS" ? `${command} output exceeded the ${formatSize(failure.maxBufferBytes ?? EXEC_OPTS.maxBuffer)} limit` : formatGitFailure(command, failure, err);
15605
+ const message = isTimeout ? formatGitTimeout(command, failure, timeoutMs, elapsedMs) : failure.code === "ENOBUFS" ? `${command} output exceeded the ${formatSize(failure.maxBufferBytes ?? EXEC_OPTS.maxBuffer)} limit` : formatGitFailure(command, failure, err);
15470
15606
  const wrapped = new Error(message);
15471
15607
  wrapped.isGitTimeout = isTimeout;
15472
15608
  wrapped.gitExitStatus = failure.code || failure.signal ? void 0 : failure.status;
@@ -15498,6 +15634,7 @@ function isGitTimeoutError(err) {
15498
15634
  }
15499
15635
  function gitBuffer(repoRoot, args, options = {}) {
15500
15636
  const timeout = options.timeoutMs ?? EXEC_OPTS.timeout;
15637
+ const started = Date.now();
15501
15638
  try {
15502
15639
  return execFileSync2("git", args, {
15503
15640
  cwd: repoRoot,
@@ -15508,7 +15645,15 @@ function gitBuffer(repoRoot, args, options = {}) {
15508
15645
  timeout
15509
15646
  });
15510
15647
  } catch (err) {
15511
- throw createGitError(args, err, timeout);
15648
+ const wrapped = createGitError(args, err, timeout, Date.now() - started);
15649
+ if (wrapped.isGitTimeout || wrapped.isGitMissingObject) {
15650
+ probeObjectStore(
15651
+ repoRoot,
15652
+ wrapped.isGitTimeout ? "timeout" : "missing-object",
15653
+ wrapped.gitStderr
15654
+ );
15655
+ }
15656
+ throw wrapped;
15512
15657
  }
15513
15658
  }
15514
15659
  function git(repoRoot, args) {
@@ -15546,23 +15691,23 @@ function withPrivateIndex(repoRoot, run) {
15546
15691
  let tmpDir = null;
15547
15692
  let copy = null;
15548
15693
  try {
15549
- const indexPath = path16.resolve(
15694
+ const indexPath = path17.resolve(
15550
15695
  repoRoot,
15551
15696
  git(repoRoot, ["rev-parse", "--git-path", "index"])
15552
15697
  );
15553
- tmpDir = fs17.mkdtempSync(path16.join(os7.tmpdir(), "hillclimb-index-"));
15554
- copy = path16.join(tmpDir, "index");
15555
- const fd = fs17.openSync(indexPath, "r");
15698
+ tmpDir = fs18.mkdtempSync(path17.join(os7.tmpdir(), "hillclimb-index-"));
15699
+ copy = path17.join(tmpDir, "index");
15700
+ const fd = fs18.openSync(indexPath, "r");
15556
15701
  let stat;
15557
15702
  let data;
15558
15703
  try {
15559
- stat = fs17.fstatSync(fd);
15560
- data = fs17.readFileSync(fd);
15704
+ stat = fs18.fstatSync(fd);
15705
+ data = fs18.readFileSync(fd);
15561
15706
  } finally {
15562
- fs17.closeSync(fd);
15707
+ fs18.closeSync(fd);
15563
15708
  }
15564
- fs17.writeFileSync(copy, data);
15565
- fs17.utimesSync(copy, stat.atime, stat.mtime);
15709
+ fs18.writeFileSync(copy, data);
15710
+ fs18.utimesSync(copy, stat.atime, stat.mtime);
15566
15711
  } catch (err) {
15567
15712
  const missing = err.code === "ENOENT";
15568
15713
  appendLog(
@@ -15574,7 +15719,7 @@ function withPrivateIndex(repoRoot, run) {
15574
15719
  try {
15575
15720
  return run(copy ? privateIndexEnv(copy) : null);
15576
15721
  } finally {
15577
- if (tmpDir) fs17.rmSync(tmpDir, { recursive: true, force: true });
15722
+ if (tmpDir) fs18.rmSync(tmpDir, { recursive: true, force: true });
15578
15723
  }
15579
15724
  }
15580
15725
  function privateIndexEnv(indexPath) {
@@ -15904,7 +16049,7 @@ function filterOmittedFilesFromTree(repoRoot, treeSha, options = {}) {
15904
16049
  options.omittedFiles?.push(...omittedFiles);
15905
16050
  return timeCaptureStage(repoRoot, "tracked-tree-filter", () => {
15906
16051
  if (omittedFiles.length === 0) return treeSha;
15907
- const tmpIndex = path16.join(
16052
+ const tmpIndex = path17.join(
15908
16053
  os7.tmpdir(),
15909
16054
  `hillclimb-filter-${Date.now()}-${process.pid}`
15910
16055
  );
@@ -15919,7 +16064,7 @@ function filterOmittedFilesFromTree(repoRoot, treeSha, options = {}) {
15919
16064
  return gitWithEnv(repoRoot, ["write-tree"], env);
15920
16065
  } finally {
15921
16066
  try {
15922
- fs17.unlinkSync(tmpIndex);
16067
+ fs18.unlinkSync(tmpIndex);
15923
16068
  } catch {
15924
16069
  }
15925
16070
  }
@@ -15944,8 +16089,8 @@ function buildUntrackedTree(repoRoot, options = {}, attempt = 1) {
15944
16089
  if (!relPath) continue;
15945
16090
  candidates++;
15946
16091
  try {
15947
- const absPath = path16.join(repoRoot, relPath);
15948
- const stat = fs17.lstatSync(absPath);
16092
+ const absPath = path17.join(repoRoot, relPath);
16093
+ const stat = fs18.lstatSync(absPath);
15949
16094
  const reason = classifyOmission(
15950
16095
  relPath,
15951
16096
  stat.size,
@@ -15976,11 +16121,11 @@ function buildUntrackedTree(repoRoot, options = {}, attempt = 1) {
15976
16121
  `git-traces: untracked inventory (repo=${repoRoot}, candidates=${candidates}, kept=${kept.length}, keptBytes=${keptBytes}, omitted=${omitted}, omittedBytes=${omittedBytes}, skipped=${skipped})`
15977
16122
  );
15978
16123
  if (kept.length === 0) return null;
15979
- const tmpDir = fs17.mkdtempSync(path16.join(os7.tmpdir(), "hillclimb-untracked-"));
16124
+ const tmpDir = fs18.mkdtempSync(path17.join(os7.tmpdir(), "hillclimb-untracked-"));
15980
16125
  const env = {
15981
16126
  ...process.env,
15982
16127
  LC_ALL: "C",
15983
- GIT_INDEX_FILE: path16.join(tmpDir, "index")
16128
+ GIT_INDEX_FILE: path17.join(tmpDir, "index")
15984
16129
  };
15985
16130
  try {
15986
16131
  timeCaptureStage(
@@ -16008,7 +16153,7 @@ function buildUntrackedTree(repoRoot, options = {}, attempt = 1) {
16008
16153
  return buildUntrackedTree(repoRoot, options, attempt + 1);
16009
16154
  } finally {
16010
16155
  try {
16011
- fs17.rmSync(tmpDir, { recursive: true, force: true });
16156
+ fs18.rmSync(tmpDir, { recursive: true, force: true });
16012
16157
  } catch {
16013
16158
  }
16014
16159
  }
@@ -16054,10 +16199,10 @@ function buildSnapshotTree(repoRoot, stashSha, options = {}) {
16054
16199
  `git-traces: tree assembly reused (repo=${repoRoot})`
16055
16200
  );
16056
16201
  return previous.snapshotTreeSha;
16057
- } catch {
16202
+ } catch (err) {
16058
16203
  appendLog(
16059
16204
  "info",
16060
- `git-traces: tree assembly cache unavailable; rebuilding (repo=${repoRoot})`
16205
+ `git-traces: tree assembly cache unavailable; rebuilding (repo=${repoRoot}): ${err instanceof Error ? err.message : String(err)}`
16061
16206
  );
16062
16207
  }
16063
16208
  }
@@ -16079,7 +16224,7 @@ function mergeSnapshotTrees(repoRoot, filteredTrackedTree, untrackedTree) {
16079
16224
  if (!untrackedTree || untrackedTree === EMPTY_TREE_SHA)
16080
16225
  return filteredTrackedTree;
16081
16226
  if (filteredTrackedTree === EMPTY_TREE_SHA) return untrackedTree;
16082
- const tmpIndex = path16.join(
16227
+ const tmpIndex = path17.join(
16083
16228
  os7.tmpdir(),
16084
16229
  `hillclimb-index-${Date.now()}-${process.pid}`
16085
16230
  );
@@ -16108,7 +16253,7 @@ function mergeSnapshotTrees(repoRoot, filteredTrackedTree, untrackedTree) {
16108
16253
  );
16109
16254
  } finally {
16110
16255
  try {
16111
- fs17.unlinkSync(tmpIndex);
16256
+ fs18.unlinkSync(tmpIndex);
16112
16257
  } catch {
16113
16258
  }
16114
16259
  }
@@ -16123,10 +16268,10 @@ function createBundleFromTree(repoRoot, treeSha, sessionId, label) {
16123
16268
  pinRef(repoRoot, orphanRef, orphanCommit);
16124
16269
  let tmpDir;
16125
16270
  try {
16126
- tmpDir = fs17.mkdtempSync(
16127
- path16.join(os7.tmpdir(), `hillclimb-bundle-${process.pid}-`)
16271
+ tmpDir = fs18.mkdtempSync(
16272
+ path17.join(os7.tmpdir(), `hillclimb-bundle-${process.pid}-`)
16128
16273
  );
16129
- const tmpFile = path16.join(tmpDir, "baseline.bundle");
16274
+ const tmpFile = path17.join(tmpDir, "baseline.bundle");
16130
16275
  timeCaptureStage(
16131
16276
  repoRoot,
16132
16277
  "bundle-create",
@@ -16134,7 +16279,7 @@ function createBundleFromTree(repoRoot, treeSha, sessionId, label) {
16134
16279
  timeoutMs: resolveGitCommandTimeoutMs(DEFAULT_BUNDLE_TIMEOUT_MS)
16135
16280
  })
16136
16281
  );
16137
- const bundle = fs17.readFileSync(tmpFile);
16282
+ const bundle = fs18.readFileSync(tmpFile);
16138
16283
  appendLog(
16139
16284
  "info",
16140
16285
  `git-traces: bundle inventory (repo=${repoRoot}, bytes=${bundle.length})`
@@ -16142,7 +16287,7 @@ function createBundleFromTree(repoRoot, treeSha, sessionId, label) {
16142
16287
  return bundle;
16143
16288
  } finally {
16144
16289
  try {
16145
- if (tmpDir) fs17.rmSync(tmpDir, { recursive: true, force: true });
16290
+ if (tmpDir) fs18.rmSync(tmpDir, { recursive: true, force: true });
16146
16291
  } catch {
16147
16292
  }
16148
16293
  deleteRef(repoRoot, orphanRef);
@@ -16151,6 +16296,7 @@ function createBundleFromTree(repoRoot, treeSha, sessionId, label) {
16151
16296
  var MAX_PATCH_GZ_BYTES = 500 * 1024 * 1024;
16152
16297
  function gitGzipStream(repoRoot, args, maxGzBytes) {
16153
16298
  return new Promise((resolve, reject) => {
16299
+ const started = Date.now();
16154
16300
  const child = spawn2("git", args, {
16155
16301
  cwd: repoRoot,
16156
16302
  stdio: ["ignore", "pipe", "pipe"],
@@ -16179,7 +16325,18 @@ function gitGzipStream(repoRoot, args, maxGzBytes) {
16179
16325
  settled = true;
16180
16326
  clearTimeout(timer);
16181
16327
  if (timedOut) {
16182
- reject(createGitError(args, { code: "ETIMEDOUT" }));
16328
+ const wrapped = createGitError(
16329
+ args,
16330
+ {
16331
+ code: "ETIMEDOUT",
16332
+ signal: exitSignal,
16333
+ stderr: Buffer.concat(stderrChunks)
16334
+ },
16335
+ GIT_COMMAND_TIMEOUT_MS,
16336
+ Date.now() - started
16337
+ );
16338
+ probeObjectStore(repoRoot, "timeout", wrapped.gitStderr);
16339
+ reject(wrapped);
16183
16340
  } else if (overflowed) {
16184
16341
  reject(
16185
16342
  createGitError(args, { code: "ENOBUFS", maxBufferBytes: maxGzBytes })
@@ -16441,9 +16598,9 @@ function parseCommitFiles(repoRoot, sha) {
16441
16598
  oldPath
16442
16599
  });
16443
16600
  } else {
16444
- const path27 = parts[parts.length - 1];
16445
- indexByPath.set(path27, files.length);
16446
- files.push({ path: path27, status, additions: 0, deletions: 0 });
16601
+ const path28 = parts[parts.length - 1];
16602
+ indexByPath.set(path28, files.length);
16603
+ files.push({ path: path28, status, additions: 0, deletions: 0 });
16447
16604
  }
16448
16605
  }
16449
16606
  for (const line of numstat.split("\n")) {
@@ -16527,11 +16684,11 @@ function countScopedTurnTreeRefs(repoRoot, sessionId, epochPrefix3) {
16527
16684
 
16528
16685
  // src/git-traces/session-state.ts
16529
16686
  import crypto6 from "crypto";
16530
- import fs18 from "fs";
16687
+ import fs19 from "fs";
16531
16688
  import os8 from "os";
16532
- import path17 from "path";
16689
+ import path18 from "path";
16533
16690
  var CURRENT_SCHEMA_VERSION3 = 3;
16534
- var DEFAULT_STATE_DIR2 = path17.join(os8.homedir(), ".hillclimb", "git-traces");
16691
+ var DEFAULT_STATE_DIR2 = path18.join(os8.homedir(), ".hillclimb", "git-traces");
16535
16692
  var LOCK_RETRIES2 = 120;
16536
16693
  var LOCK_RETRY_DELAY_MS2 = 500;
16537
16694
  var DEFAULT_LIVE_OWNER_MAX_WAIT_MS2 = 10 * 60 * 1e3;
@@ -16556,16 +16713,16 @@ function stateDir3() {
16556
16713
  }
16557
16714
  function stateFileForRepo(repoRoot, tool, sessionId) {
16558
16715
  const hash = crypto6.createHash("sha256").update(
16559
- sessionId ? `${path17.resolve(repoRoot)}\0${tool}\0${sessionId}` : `${path17.resolve(repoRoot)}\0${tool}`
16716
+ sessionId ? `${path18.resolve(repoRoot)}\0${tool}\0${sessionId}` : `${path18.resolve(repoRoot)}\0${tool}`
16560
16717
  ).digest("hex").slice(0, 16);
16561
- return path17.join(stateDir3(), `${hash}.json`);
16718
+ return path18.join(stateDir3(), `${hash}.json`);
16562
16719
  }
16563
16720
  function lockFileForRepo(repoRoot, tool, sessionId) {
16564
16721
  return `${stateFileForRepo(repoRoot, tool, sessionId)}.lock`;
16565
16722
  }
16566
16723
  async function readStateFile(file) {
16567
16724
  try {
16568
- const raw = await fs18.promises.readFile(file, "utf-8");
16725
+ const raw = await fs19.promises.readFile(file, "utf-8");
16569
16726
  const parsed = JSON.parse(raw);
16570
16727
  if (parsed.schemaVersion !== CURRENT_SCHEMA_VERSION3) {
16571
16728
  return null;
@@ -16579,7 +16736,7 @@ async function readStoredStateFile(file) {
16579
16736
  const state = await readStateFile(file);
16580
16737
  if (!state) return null;
16581
16738
  try {
16582
- return { state, mtimeMs: (await fs18.promises.stat(file)).mtimeMs };
16739
+ return { state, mtimeMs: (await fs19.promises.stat(file)).mtimeMs };
16583
16740
  } catch {
16584
16741
  return null;
16585
16742
  }
@@ -16587,26 +16744,26 @@ async function readStoredStateFile(file) {
16587
16744
  async function listScopedSessionStates(repoRoot, tool) {
16588
16745
  let entries;
16589
16746
  try {
16590
- entries = await fs18.promises.readdir(stateDir3(), { withFileTypes: true });
16747
+ entries = await fs19.promises.readdir(stateDir3(), { withFileTypes: true });
16591
16748
  } catch {
16592
16749
  return [];
16593
16750
  }
16594
16751
  const states = [];
16595
16752
  for (const entry of entries) {
16596
16753
  if (!entry.isFile() || !entry.name.endsWith(".json")) continue;
16597
- const file = path17.join(stateDir3(), entry.name);
16754
+ const file = path18.join(stateDir3(), entry.name);
16598
16755
  const state = await readStateFile(file);
16599
16756
  if (!state) continue;
16600
16757
  if (typeof state.repoRoot !== "string" || typeof state.sessionId !== "string") {
16601
16758
  continue;
16602
16759
  }
16603
- if (path17.resolve(state.repoRoot) !== path17.resolve(repoRoot)) continue;
16604
- if (path17.resolve(file) !== path17.resolve(stateFileForRepo(repoRoot, tool, state.sessionId))) {
16760
+ if (path18.resolve(state.repoRoot) !== path18.resolve(repoRoot)) continue;
16761
+ if (path18.resolve(file) !== path18.resolve(stateFileForRepo(repoRoot, tool, state.sessionId))) {
16605
16762
  continue;
16606
16763
  }
16607
16764
  let mtimeMs = 0;
16608
16765
  try {
16609
- mtimeMs = (await fs18.promises.stat(file)).mtimeMs;
16766
+ mtimeMs = (await fs19.promises.stat(file)).mtimeMs;
16610
16767
  } catch {
16611
16768
  continue;
16612
16769
  }
@@ -16617,26 +16774,26 @@ async function listScopedSessionStates(repoRoot, tool) {
16617
16774
  async function listSessionStatesForSession(tool, sessionId) {
16618
16775
  let entries;
16619
16776
  try {
16620
- entries = await fs18.promises.readdir(stateDir3(), { withFileTypes: true });
16777
+ entries = await fs19.promises.readdir(stateDir3(), { withFileTypes: true });
16621
16778
  } catch {
16622
16779
  return [];
16623
16780
  }
16624
16781
  const states = [];
16625
16782
  for (const entry of entries) {
16626
16783
  if (!entry.isFile() || !entry.name.endsWith(".json")) continue;
16627
- const file = path17.join(stateDir3(), entry.name);
16784
+ const file = path18.join(stateDir3(), entry.name);
16628
16785
  const state = await readStateFile(file);
16629
16786
  if (!state) continue;
16630
16787
  if (typeof state.repoRoot !== "string" || typeof state.sessionId !== "string") {
16631
16788
  continue;
16632
16789
  }
16633
16790
  if (state.sessionId !== sessionId) continue;
16634
- if (path17.resolve(file) !== path17.resolve(stateFileForRepo(state.repoRoot, tool, sessionId))) {
16791
+ if (path18.resolve(file) !== path18.resolve(stateFileForRepo(state.repoRoot, tool, sessionId))) {
16635
16792
  continue;
16636
16793
  }
16637
16794
  let mtimeMs = 0;
16638
16795
  try {
16639
- mtimeMs = (await fs18.promises.stat(file)).mtimeMs;
16796
+ mtimeMs = (await fs19.promises.stat(file)).mtimeMs;
16640
16797
  } catch {
16641
16798
  continue;
16642
16799
  }
@@ -16685,17 +16842,17 @@ async function writeLegacySessionState(state, tool) {
16685
16842
  );
16686
16843
  }
16687
16844
  async function writeStateFile(file, state) {
16688
- await fs18.promises.mkdir(stateDir3(), { recursive: true, mode: 448 });
16845
+ await fs19.promises.mkdir(stateDir3(), { recursive: true, mode: 448 });
16689
16846
  const tmp = `${file}.tmp`;
16690
- await fs18.promises.writeFile(tmp, JSON.stringify(state, null, 2), {
16847
+ await fs19.promises.writeFile(tmp, JSON.stringify(state, null, 2), {
16691
16848
  mode: 384
16692
16849
  });
16693
- await fs18.promises.rename(tmp, file);
16850
+ await fs19.promises.rename(tmp, file);
16694
16851
  }
16695
16852
  async function touchSessionState(repoRoot, tool, sessionId) {
16696
16853
  const now = /* @__PURE__ */ new Date();
16697
16854
  try {
16698
- await fs18.promises.utimes(
16855
+ await fs19.promises.utimes(
16699
16856
  stateFileForRepo(repoRoot, tool, sessionId),
16700
16857
  now,
16701
16858
  now
@@ -16705,7 +16862,7 @@ async function touchSessionState(repoRoot, tool, sessionId) {
16705
16862
  }
16706
16863
  async function statSessionStateMtime(repoRoot, tool, sessionId) {
16707
16864
  try {
16708
- const stat = await fs18.promises.stat(
16865
+ const stat = await fs19.promises.stat(
16709
16866
  stateFileForRepo(repoRoot, tool, sessionId)
16710
16867
  );
16711
16868
  return stat.mtimeMs;
@@ -16715,7 +16872,7 @@ async function statSessionStateMtime(repoRoot, tool, sessionId) {
16715
16872
  }
16716
16873
  async function deleteStateFile(file) {
16717
16874
  try {
16718
- await fs18.promises.unlink(file);
16875
+ await fs19.promises.unlink(file);
16719
16876
  } catch {
16720
16877
  }
16721
16878
  }
@@ -16754,7 +16911,7 @@ async function acquireLock3(repoRoot, tool, sessionId, retries, delayMs, options
16754
16911
  }
16755
16912
  async function acquireLockWithoutTiming(repoRoot, tool, sessionId, retries, delayMs, options = {}) {
16756
16913
  const lockPath = lockFileForRepo(repoRoot, tool, sessionId);
16757
- await fs18.promises.mkdir(stateDir3(), { recursive: true, mode: 448 });
16914
+ await fs19.promises.mkdir(stateDir3(), { recursive: true, mode: 448 });
16758
16915
  const bounded = retries !== void 0 || delayMs !== void 0;
16759
16916
  const attempts = retries ?? LOCK_RETRIES2;
16760
16917
  const delay = delayMs ?? LOCK_RETRY_DELAY_MS2;
@@ -16764,9 +16921,9 @@ async function acquireLockWithoutTiming(repoRoot, tool, sessionId, retries, dela
16764
16921
  let extensionLogged = false;
16765
16922
  for (let i = 0; ; i++) {
16766
16923
  try {
16767
- const fd = await fs18.promises.open(
16924
+ const fd = await fs19.promises.open(
16768
16925
  lockPath,
16769
- fs18.constants.O_CREAT | fs18.constants.O_EXCL | fs18.constants.O_WRONLY
16926
+ fs19.constants.O_CREAT | fs19.constants.O_EXCL | fs19.constants.O_WRONLY
16770
16927
  );
16771
16928
  await fd.write(String(process.pid));
16772
16929
  await fd.close();
@@ -16775,7 +16932,7 @@ async function acquireLockWithoutTiming(repoRoot, tool, sessionId, retries, dela
16775
16932
  if (err.code !== "EEXIST") throw err;
16776
16933
  let regularLockFile = false;
16777
16934
  try {
16778
- regularLockFile = (await fs18.promises.lstat(lockPath)).isFile();
16935
+ regularLockFile = (await fs19.promises.lstat(lockPath)).isFile();
16779
16936
  } catch (statErr) {
16780
16937
  if (statErr.code === "ENOENT") {
16781
16938
  i--;
@@ -16837,21 +16994,21 @@ async function acquireLockWithoutTiming(repoRoot, tool, sessionId, retries, dela
16837
16994
  }
16838
16995
  async function releaseLock3(repoRoot, tool, sessionId) {
16839
16996
  try {
16840
- await fs18.promises.unlink(lockFileForRepo(repoRoot, tool, sessionId));
16997
+ await fs19.promises.unlink(lockFileForRepo(repoRoot, tool, sessionId));
16841
16998
  } catch {
16842
16999
  }
16843
17000
  }
16844
17001
  async function sweepStaleLockFiles(ttlMs = STALE_LOCK_TTL_MS3, now = Date.now()) {
16845
17002
  let entries;
16846
17003
  try {
16847
- entries = await fs18.promises.readdir(stateDir3(), { withFileTypes: true });
17004
+ entries = await fs19.promises.readdir(stateDir3(), { withFileTypes: true });
16848
17005
  } catch {
16849
17006
  return 0;
16850
17007
  }
16851
17008
  let removed = 0;
16852
17009
  for (const entry of entries) {
16853
17010
  if (!entry.isFile() || !entry.name.endsWith(".lock")) continue;
16854
- const file = path17.join(stateDir3(), entry.name);
17011
+ const file = path18.join(stateDir3(), entry.name);
16855
17012
  if (await reapLockIfStale(file, {
16856
17013
  maxAgeMs: ttlMs,
16857
17014
  preserveLiveOwner: true,
@@ -16906,12 +17063,12 @@ function withPendingCapture(record, work) {
16906
17063
  return activeCapture.run({ record, save: () => saveCapture(record) }, work);
16907
17064
  }
16908
17065
  function queueDirectory(repoRoot, tool, sessionId) {
16909
- const stateDir4 = process.env.HILLCLIMB_GIT_TRACES_STATE_DIR ?? path18.join(os9.homedir(), ".hillclimb", "git-traces");
16910
- const key = crypto7.createHash("sha256").update(`${path18.resolve(repoRoot)}\0${tool}\0${sessionId}`).digest("hex");
16911
- return path18.join(stateDir4, "pending-v1", key);
17066
+ const stateDir4 = process.env.HILLCLIMB_GIT_TRACES_STATE_DIR ?? path19.join(os9.homedir(), ".hillclimb", "git-traces");
17067
+ const key = crypto7.createHash("sha256").update(`${path19.resolve(repoRoot)}\0${tool}\0${sessionId}`).digest("hex");
17068
+ return path19.join(stateDir4, "pending-v1", key);
16912
17069
  }
16913
17070
  function captureDirectory(record) {
16914
- return path18.join(
17071
+ return path19.join(
16915
17072
  queueDirectory(record.repoRoot, record.tool, record.sessionId),
16916
17073
  record.id
16917
17074
  );
@@ -16919,20 +17076,20 @@ function captureDirectory(record) {
16919
17076
  function writeDurable(file, body) {
16920
17077
  const temporary = `${file}.${process.pid}.tmp`;
16921
17078
  try {
16922
- const descriptor = fs19.openSync(temporary, "w", 384);
17079
+ const descriptor = fs20.openSync(temporary, "w", 384);
16923
17080
  try {
16924
- fs19.writeFileSync(descriptor, body);
16925
- fs19.fsyncSync(descriptor);
17081
+ fs20.writeFileSync(descriptor, body);
17082
+ fs20.fsyncSync(descriptor);
16926
17083
  } finally {
16927
- fs19.closeSync(descriptor);
17084
+ fs20.closeSync(descriptor);
16928
17085
  }
16929
- fs19.renameSync(temporary, file);
17086
+ fs20.renameSync(temporary, file);
16930
17087
  try {
16931
- const directory = fs19.openSync(path18.dirname(file), "r");
17088
+ const directory = fs20.openSync(path19.dirname(file), "r");
16932
17089
  try {
16933
- fs19.fsyncSync(directory);
17090
+ fs20.fsyncSync(directory);
16934
17091
  } finally {
16935
- fs19.closeSync(directory);
17092
+ fs20.closeSync(directory);
16936
17093
  }
16937
17094
  } catch (err) {
16938
17095
  if (!["EINVAL", "ENOTSUP", "EBADF", "EPERM", "EISDIR"].includes(
@@ -16941,7 +17098,7 @@ function writeDurable(file, body) {
16941
17098
  throw err;
16942
17099
  }
16943
17100
  } finally {
16944
- if (fs19.existsSync(temporary)) fs19.unlinkSync(temporary);
17101
+ if (fs20.existsSync(temporary)) fs20.unlinkSync(temporary);
16945
17102
  }
16946
17103
  }
16947
17104
  function saveCapture(record) {
@@ -16952,7 +17109,7 @@ function saveCapture(record) {
16952
17109
  let status = "failed";
16953
17110
  try {
16954
17111
  writeDurable(
16955
- path18.join(captureDirectory(record), "capture.json"),
17112
+ path19.join(captureDirectory(record), "capture.json"),
16956
17113
  JSON.stringify(record)
16957
17114
  );
16958
17115
  status = "ok";
@@ -16964,7 +17121,7 @@ function listPendingCaptures2(repoRoot, tool, sessionId) {
16964
17121
  const directory = queueDirectory(repoRoot, tool, sessionId);
16965
17122
  let entries;
16966
17123
  try {
16967
- entries = fs19.readdirSync(directory, { withFileTypes: true });
17124
+ entries = fs20.readdirSync(directory, { withFileTypes: true });
16968
17125
  } catch (err) {
16969
17126
  if (err.code === "ENOENT") return [];
16970
17127
  throw err;
@@ -16972,10 +17129,10 @@ function listPendingCaptures2(repoRoot, tool, sessionId) {
16972
17129
  const records = [];
16973
17130
  for (const entry of entries) {
16974
17131
  if (!entry.isDirectory() || !/^[0-9a-f-]{36}$/.test(entry.name)) continue;
16975
- const file = path18.join(directory, entry.name, "capture.json");
16976
- if (!fs19.existsSync(file)) continue;
17132
+ const file = path19.join(directory, entry.name, "capture.json");
17133
+ if (!fs20.existsSync(file)) continue;
16977
17134
  const record = JSON.parse(
16978
- fs19.readFileSync(file, "utf-8")
17135
+ fs20.readFileSync(file, "utf-8")
16979
17136
  );
16980
17137
  if (record.version !== 1 || record.id !== entry.name || record.repoRoot !== repoRoot || record.tool !== tool || record.sessionId !== sessionId) {
16981
17138
  throw new Error(`Invalid pending Git capture: ${file}`);
@@ -16988,13 +17145,13 @@ function hasPendingCaptures(repoRoot, tool, sessionId) {
16988
17145
  return listPendingCaptures2(repoRoot, tool, sessionId).length > 0;
16989
17146
  }
16990
17147
  function listPendingQueues() {
16991
- const root = path18.join(
16992
- process.env.HILLCLIMB_GIT_TRACES_STATE_DIR ?? path18.join(os9.homedir(), ".hillclimb", "git-traces"),
17148
+ const root = path19.join(
17149
+ process.env.HILLCLIMB_GIT_TRACES_STATE_DIR ?? path19.join(os9.homedir(), ".hillclimb", "git-traces"),
16993
17150
  "pending-v1"
16994
17151
  );
16995
17152
  let queueDirs;
16996
17153
  try {
16997
- queueDirs = fs19.readdirSync(root, { withFileTypes: true });
17154
+ queueDirs = fs20.readdirSync(root, { withFileTypes: true });
16998
17155
  } catch (err) {
16999
17156
  if (err.code === "ENOENT") return [];
17000
17157
  throw err;
@@ -17002,15 +17159,15 @@ function listPendingQueues() {
17002
17159
  const queues = [];
17003
17160
  for (const queueDir of queueDirs) {
17004
17161
  if (!queueDir.isDirectory()) continue;
17005
- const directory = path18.join(root, queueDir.name);
17162
+ const directory = path19.join(root, queueDir.name);
17006
17163
  let identity = null;
17007
17164
  try {
17008
- for (const entry of fs19.readdirSync(directory, { withFileTypes: true })) {
17165
+ for (const entry of fs20.readdirSync(directory, { withFileTypes: true })) {
17009
17166
  if (!entry.isDirectory()) continue;
17010
- const file = path18.join(directory, entry.name, "capture.json");
17011
- if (!fs19.existsSync(file)) continue;
17167
+ const file = path19.join(directory, entry.name, "capture.json");
17168
+ if (!fs20.existsSync(file)) continue;
17012
17169
  identity = JSON.parse(
17013
- fs19.readFileSync(file, "utf-8")
17170
+ fs20.readFileSync(file, "utf-8")
17014
17171
  );
17015
17172
  break;
17016
17173
  }
@@ -17043,15 +17200,15 @@ function noteReplayFailure(record, error, now = Date.now()) {
17043
17200
  }
17044
17201
  function queueDiskBytes(directory) {
17045
17202
  let diskBytes = 0;
17046
- if (fs19.existsSync(directory)) {
17047
- for (const entry of fs19.readdirSync(directory, { withFileTypes: true })) {
17203
+ if (fs20.existsSync(directory)) {
17204
+ for (const entry of fs20.readdirSync(directory, { withFileTypes: true })) {
17048
17205
  if (!entry.isDirectory()) continue;
17049
- for (const file of fs19.readdirSync(path18.join(directory, entry.name), {
17206
+ for (const file of fs20.readdirSync(path19.join(directory, entry.name), {
17050
17207
  withFileTypes: true
17051
17208
  })) {
17052
17209
  if (file.isFile())
17053
- diskBytes += fs19.statSync(
17054
- path18.join(directory, entry.name, file.name)
17210
+ diskBytes += fs20.statSync(
17211
+ path19.join(directory, entry.name, file.name)
17055
17212
  ).size;
17056
17213
  }
17057
17214
  }
@@ -17067,7 +17224,7 @@ function pendingBytes(records) {
17067
17224
  return queueDiskBytes(
17068
17225
  queueDirectory(first.repoRoot, first.tool, first.sessionId)
17069
17226
  ) + records.reduce(
17070
- (sum, record) => sum + chargedBytes(record) + (fs19.existsSync(path18.join(captureDirectory(record), "capture.json")) ? 0 : Buffer.byteLength(JSON.stringify(record))),
17227
+ (sum, record) => sum + chargedBytes(record) + (fs20.existsSync(path19.join(captureDirectory(record), "capture.json")) ? 0 : Buffer.byteLength(JSON.stringify(record))),
17071
17228
  0
17072
17229
  );
17073
17230
  }
@@ -17160,7 +17317,7 @@ function storePendingCapture(params) {
17160
17317
  );
17161
17318
  }
17162
17319
  const directory = captureDirectory(record);
17163
- fs19.mkdirSync(directory, { recursive: true, mode: 448 });
17320
+ fs20.mkdirSync(directory, { recursive: true, mode: 448 });
17164
17321
  const refRoot = `refs/hillclimb/pending-v1/${record.id}`;
17165
17322
  let published = false;
17166
17323
  try {
@@ -17203,7 +17360,7 @@ function storePendingCapture(params) {
17203
17360
  return record;
17204
17361
  } finally {
17205
17362
  if (!published) {
17206
- fs19.rmSync(directory, { recursive: true, force: true });
17363
+ fs20.rmSync(directory, { recursive: true, force: true });
17207
17364
  for (const label of [
17208
17365
  "snapshot",
17209
17366
  "tree",
@@ -17234,7 +17391,7 @@ async function preparePendingUpload(context, contributionId, filename, buffer) {
17234
17391
  let status = "failed";
17235
17392
  try {
17236
17393
  if (saved) {
17237
- const bytes = fs19.readFileSync(path18.join(directory, saved.file));
17394
+ const bytes = fs20.readFileSync(path19.join(directory, saved.file));
17238
17395
  if (bytes.length !== saved.bytes || crypto7.createHash("sha256").update(bytes).digest("hex") !== saved.sha256) {
17239
17396
  throw new Error(
17240
17397
  `Pending Git artifact is corrupt: ${saved.file}; capture retained`
@@ -17254,7 +17411,7 @@ async function preparePendingUpload(context, contributionId, filename, buffer) {
17254
17411
  );
17255
17412
  }
17256
17413
  const file = `${crypto7.createHash("sha256").update(key).digest("hex")}.artifact`;
17257
- writeDurable(path18.join(directory, file), buffer);
17414
+ writeDurable(path19.join(directory, file), buffer);
17258
17415
  context.record.uploads[key] = {
17259
17416
  file,
17260
17417
  bytes: buffer.byteLength,
@@ -17264,7 +17421,7 @@ async function preparePendingUpload(context, contributionId, filename, buffer) {
17264
17421
  context.save();
17265
17422
  } catch (err) {
17266
17423
  delete context.record.uploads[key];
17267
- fs19.rmSync(path18.join(directory, file), { force: true });
17424
+ fs20.rmSync(path19.join(directory, file), { force: true });
17268
17425
  throw err;
17269
17426
  }
17270
17427
  status = "ok";
@@ -17296,8 +17453,8 @@ function readPreparedCaptureArtifact(context, filename, input) {
17296
17453
  throw new Error(
17297
17454
  `Prepared Git artifact input changed: ${filename}; capture retained`
17298
17455
  );
17299
- const bytes = fs19.readFileSync(
17300
- path18.join(captureDirectory(context.record), saved.file)
17456
+ const bytes = fs20.readFileSync(
17457
+ path19.join(captureDirectory(context.record), saved.file)
17301
17458
  );
17302
17459
  if (bytes.length !== saved.bytes || crypto7.createHash("sha256").update(bytes).digest("hex") !== saved.sha256)
17303
17460
  throw new Error(
@@ -17316,7 +17473,7 @@ function completePendingCapture(record) {
17316
17473
  function removeCompletedCapture(record) {
17317
17474
  if (!record.completed)
17318
17475
  throw new Error("Cannot remove an unacknowledged Git capture");
17319
- fs19.rmSync(captureDirectory(record), { recursive: true, force: true });
17476
+ fs20.rmSync(captureDirectory(record), { recursive: true, force: true });
17320
17477
  for (const label of ["snapshot", "tree", "head", "baseline", "previous"]) {
17321
17478
  deleteRef(
17322
17479
  record.repoRoot,
@@ -17389,7 +17546,7 @@ async function uploadArtifact(client, contributionId, filename, mimeType, buffer
17389
17546
  }
17390
17547
 
17391
17548
  // package.json
17392
- var version = "0.9.4";
17549
+ var version = "0.9.5";
17393
17550
 
17394
17551
  // src/version.ts
17395
17552
  var CLI_VERSION = version;
@@ -17413,7 +17570,7 @@ function lineHasAssistant(line) {
17413
17570
  return line.includes('"type":"assistant"') || line.includes('"role":"assistant"') || line.includes('"type":"assistant.');
17414
17571
  }
17415
17572
  async function hasAssistantMessage(transcriptPath) {
17416
- const stream = fs20.createReadStream(transcriptPath, { encoding: "utf-8" });
17573
+ const stream = fs21.createReadStream(transcriptPath, { encoding: "utf-8" });
17417
17574
  let buffer = "";
17418
17575
  try {
17419
17576
  for await (const chunk of stream) {
@@ -17500,7 +17657,7 @@ function resolveCursorTranscriptPath(payload) {
17500
17657
  const workspace = payload.workspace_roots?.[0];
17501
17658
  if (!id || !workspace) return void 0;
17502
17659
  const encoded = workspace.replace(/^\//, "").replace(/\//g, "-");
17503
- return path19.join(
17660
+ return path20.join(
17504
17661
  os10.homedir(),
17505
17662
  ".cursor",
17506
17663
  "projects",
@@ -17556,14 +17713,14 @@ async function sweepClaudeSubagentTranscripts(payload) {
17556
17713
  const cwd = resolveHookCwd(payload);
17557
17714
  if (!parentSessionId || !cwd || !payload.transcript_path) return;
17558
17715
  try {
17559
- const directory = path19.join(
17560
- path19.dirname(path19.resolve(payload.transcript_path)),
17716
+ const directory = path20.join(
17717
+ path20.dirname(path20.resolve(payload.transcript_path)),
17561
17718
  parentSessionId,
17562
17719
  "subagents"
17563
17720
  );
17564
17721
  let names;
17565
17722
  try {
17566
- names = await fs20.promises.readdir(directory);
17723
+ names = await fs21.promises.readdir(directory);
17567
17724
  } catch (err) {
17568
17725
  if (err.code === "ENOENT") return;
17569
17726
  throw err;
@@ -17572,12 +17729,12 @@ async function sweepClaudeSubagentTranscripts(payload) {
17572
17729
  for (const name of names) {
17573
17730
  const match = /^agent-([A-Za-z0-9_-]+)\.jsonl$/.exec(name);
17574
17731
  if (!match) continue;
17575
- const file = path19.join(directory, name);
17732
+ const file = path20.join(directory, name);
17576
17733
  try {
17577
17734
  files.push({
17578
17735
  agentId: match[1],
17579
17736
  file,
17580
- modifiedMs: (await fs20.promises.stat(file)).mtimeMs
17737
+ modifiedMs: (await fs21.promises.stat(file)).mtimeMs
17581
17738
  });
17582
17739
  } catch {
17583
17740
  }
@@ -17743,7 +17900,7 @@ async function sweepCodexForkTranscripts(payload) {
17743
17900
  const parent = await findProjectForCwd(cwd);
17744
17901
  if (!parent) return;
17745
17902
  const registeredRoots = Object.keys((await loadProjects()).projects).map(
17746
- (root) => path19.resolve(root)
17903
+ (root) => path20.resolve(root)
17747
17904
  );
17748
17905
  const discovery = await discoverCodexForkThreads({
17749
17906
  parentThreadId: parentSessionId,
@@ -17884,9 +18041,9 @@ async function runUploadForSession(payload, options) {
17884
18041
  );
17885
18042
  return "skipped";
17886
18043
  }
17887
- const transcriptResolved = path19.resolve(transcriptPath);
18044
+ const transcriptResolved = path20.resolve(transcriptPath);
17888
18045
  try {
17889
- const stat = await fs20.promises.stat(transcriptResolved);
18046
+ const stat = await fs21.promises.stat(transcriptResolved);
17890
18047
  if (!stat.isFile()) {
17891
18048
  appendLog(
17892
18049
  "warn",
@@ -17942,7 +18099,7 @@ function makeTranscriptGroup(repoRoot, sourceTool, transcriptPath, content) {
17942
18099
  };
17943
18100
  return {
17944
18101
  repoPath: repoRoot,
17945
- label: path19.basename(repoRoot),
18102
+ label: path20.basename(repoRoot),
17946
18103
  files: [sourceFile],
17947
18104
  sourceNames: [sourceTool],
17948
18105
  lastModified: /* @__PURE__ */ new Date()
@@ -17953,7 +18110,7 @@ async function buildRedactChain(repoRoot, capturedSecrets = []) {
17953
18110
  "secret-discovery",
17954
18111
  () => discoverEnvFiles(repoRoot)
17955
18112
  );
17956
- const envFilePaths = envFileNames.map((n) => path19.join(repoRoot, n));
18113
+ const envFilePaths = envFileNames.map((n) => path20.join(repoRoot, n));
17957
18114
  const secretResult = await measureAgentStage(
17958
18115
  "secret-collection",
17959
18116
  () => collectSecrets(repoRoot, envFilePaths, [])
@@ -18607,7 +18764,7 @@ async function uploadSession(args) {
18607
18764
  try {
18608
18765
  raw = await measureAgentStage(
18609
18766
  "transcript-read",
18610
- () => captured ? Promise.resolve(captured.bytes) : fs20.promises.readFile(transcriptPath)
18767
+ () => captured ? Promise.resolve(captured.bytes) : fs21.promises.readFile(transcriptPath)
18611
18768
  );
18612
18769
  } catch (err) {
18613
18770
  if (err.code === "ERR_FS_FILE_TOO_LARGE") {
@@ -18978,15 +19135,15 @@ import crypto9 from "crypto";
18978
19135
 
18979
19136
  // src/git-traces/handlers.ts
18980
19137
  import { execFileSync as execFileSync3 } from "child_process";
18981
- import fs21 from "fs";
18982
- import path20 from "path";
19138
+ import fs22 from "fs";
19139
+ import path21 from "path";
18983
19140
  var GIT_TRACES_SLUG = "git-traces";
18984
19141
  var MAX_FORK_SWEEP_THREADS2 = 8;
18985
19142
  var MAX_UPLOAD_BYTES = 500 * 1024 * 1024;
18986
19143
  var STALE_SCOPED_STATE_TTL_MS = 24 * 60 * 60 * 1e3;
18987
19144
  var REBASELINE_AFTER_DIFF_FAILURES = 2;
18988
19145
  async function withRepoCaptureLock(repoRoot, capture) {
18989
- const canonicalRoot = fs21.realpathSync(repoRoot);
19146
+ const canonicalRoot = fs22.realpathSync(repoRoot);
18990
19147
  await acquireLock3(canonicalRoot, "snapshot-index-v1", null);
18991
19148
  try {
18992
19149
  return capture();
@@ -19044,7 +19201,7 @@ var PendingQueueConflictError = class extends Error {
19044
19201
  async function loadConfiguredRepos() {
19045
19202
  const file = await loadProjects();
19046
19203
  return Object.entries(file.projects).map(([repoRoot, config]) => ({
19047
- repoRoot: path20.resolve(repoRoot),
19204
+ repoRoot: path21.resolve(repoRoot),
19048
19205
  config
19049
19206
  })).sort((a, b) => a.repoRoot.localeCompare(b.repoRoot));
19050
19207
  }
@@ -19062,12 +19219,12 @@ async function configuredReposFor(trigger) {
19062
19219
  return repos;
19063
19220
  }
19064
19221
  async function propagateCodexHooks(parentRoot, worktreeRoot) {
19065
- const source = path20.join(parentRoot, ".codex", "hooks.json");
19066
- const target = path20.join(worktreeRoot, ".codex", "hooks.json");
19067
- if (fs21.existsSync(target) || !fs21.existsSync(source)) return;
19222
+ const source = path21.join(parentRoot, ".codex", "hooks.json");
19223
+ const target = path21.join(worktreeRoot, ".codex", "hooks.json");
19224
+ if (fs22.existsSync(target) || !fs22.existsSync(source)) return;
19068
19225
  try {
19069
- fs21.mkdirSync(path20.dirname(target), { recursive: true });
19070
- fs21.copyFileSync(source, target, fs21.constants.COPYFILE_EXCL);
19226
+ fs22.mkdirSync(path21.dirname(target), { recursive: true });
19227
+ fs22.copyFileSync(source, target, fs22.constants.COPYFILE_EXCL);
19071
19228
  } catch (err) {
19072
19229
  appendLog(
19073
19230
  "warn",
@@ -19089,7 +19246,7 @@ async function propagateCodexHooks(parentRoot, worktreeRoot) {
19089
19246
  }
19090
19247
  }
19091
19248
  function repoLabel(repoRoot) {
19092
- return path20.basename(repoRoot) || repoRoot;
19249
+ return path21.basename(repoRoot) || repoRoot;
19093
19250
  }
19094
19251
  function resolveCwd2(payload) {
19095
19252
  return resolveHookCwd(payload);
@@ -21306,7 +21463,7 @@ async function collectStopTargets(tool, sessionId, repos, repoByRoot) {
21306
21463
  let needsLineageCheck = false;
21307
21464
  const storedStates = await listSessionStatesForSession(tool, sessionId);
21308
21465
  for (const { state } of storedStates) {
21309
- const repo = repoByRoot.get(path20.resolve(state.repoRoot));
21466
+ const repo = repoByRoot.get(path21.resolve(state.repoRoot));
21310
21467
  if (!repo) {
21311
21468
  missingConfig++;
21312
21469
  appendLog(
@@ -21338,7 +21495,7 @@ async function lateInitSkipReason(payload, tool) {
21338
21495
  const transcriptPath = payload.transcript_path;
21339
21496
  if (!transcriptPath) return tool === "codex" ? null : "no-transcript-path";
21340
21497
  try {
21341
- const stat = await fs21.promises.stat(path20.resolve(transcriptPath));
21498
+ const stat = await fs22.promises.stat(path21.resolve(transcriptPath));
21342
21499
  return stat.isFile() ? null : "transcript-not-a-file";
21343
21500
  } catch {
21344
21501
  return "transcript-missing";
@@ -21547,9 +21704,9 @@ async function handleSessionEnd(payload, tool) {
21547
21704
  if (sessionId) {
21548
21705
  const states = await listSessionStatesForSession(tool, sessionId);
21549
21706
  for (const { state } of states) {
21550
- const repoRoot = path20.resolve(state.repoRoot);
21707
+ const repoRoot = path21.resolve(state.repoRoot);
21551
21708
  repoRoots.add(repoRoot);
21552
- const canProcess = repoByRoot.has(repoRoot) || project && path20.resolve(project.repoRoot) === repoRoot;
21709
+ const canProcess = repoByRoot.has(repoRoot) || project && path21.resolve(project.repoRoot) === repoRoot;
21553
21710
  if (canProcess && tool === "codex" && state.codexLineageChecked !== true) {
21554
21711
  needsLineageCheck = true;
21555
21712
  }
@@ -21586,7 +21743,7 @@ async function handleSessionEnd(payload, tool) {
21586
21743
  let rejected = 0;
21587
21744
  const finalCaptures = /* @__PURE__ */ new Map();
21588
21745
  for (const repoRoot of repoRoots) {
21589
- const repo = repoByRoot.get(path20.resolve(repoRoot)) ?? (project && path20.resolve(project.repoRoot) === path20.resolve(repoRoot) ? { repoRoot: project.repoRoot, config: project.config } : null);
21746
+ const repo = repoByRoot.get(path21.resolve(repoRoot)) ?? (project && path21.resolve(project.repoRoot) === path21.resolve(repoRoot) ? { repoRoot: project.repoRoot, config: project.config } : null);
21590
21747
  if (repo)
21591
21748
  finalCaptures.set(
21592
21749
  repoRoot,
@@ -21594,7 +21751,7 @@ async function handleSessionEnd(payload, tool) {
21594
21751
  );
21595
21752
  }
21596
21753
  for (const repoRoot of repoRoots) {
21597
- const repo = repoByRoot.get(path20.resolve(repoRoot)) ?? (project && path20.resolve(project.repoRoot) === path20.resolve(repoRoot) ? { repoRoot: project.repoRoot, config: project.config } : null);
21754
+ const repo = repoByRoot.get(path21.resolve(repoRoot)) ?? (project && path21.resolve(project.repoRoot) === path21.resolve(repoRoot) ? { repoRoot: project.repoRoot, config: project.config } : null);
21598
21755
  if (!repo) {
21599
21756
  skipped++;
21600
21757
  appendLog(
@@ -21973,15 +22130,15 @@ ${stack}` : ""}`
21973
22130
  }
21974
22131
 
21975
22132
  // src/outputs/zip.ts
21976
- import fs23 from "fs";
21977
- import path22 from "path";
22133
+ import fs24 from "fs";
22134
+ import path23 from "path";
21978
22135
  import archiver2 from "archiver";
21979
22136
 
21980
22137
  // src/outputs/downloads.ts
21981
22138
  import { execSync as execSync2 } from "child_process";
21982
- import fs22 from "fs";
22139
+ import fs23 from "fs";
21983
22140
  import os11 from "os";
21984
- import path21 from "path";
22141
+ import path22 from "path";
21985
22142
  function getDownloadsFolder() {
21986
22143
  const home = os11.homedir();
21987
22144
  if (process.platform === "linux") {
@@ -21990,12 +22147,12 @@ function getDownloadsFolder() {
21990
22147
  encoding: "utf-8",
21991
22148
  timeout: 3e3
21992
22149
  }).trim();
21993
- if (xdgDir && fs22.existsSync(xdgDir)) return xdgDir;
22150
+ if (xdgDir && fs23.existsSync(xdgDir)) return xdgDir;
21994
22151
  } catch {
21995
22152
  }
21996
22153
  }
21997
- const downloads = path21.join(home, "Downloads");
21998
- if (fs22.existsSync(downloads)) return downloads;
22154
+ const downloads = path22.join(home, "Downloads");
22155
+ if (fs23.existsSync(downloads)) return downloads;
21999
22156
  return home;
22000
22157
  }
22001
22158
 
@@ -22004,11 +22161,11 @@ function sanitizeFilename(name) {
22004
22161
  return name.replace(/[^a-zA-Z0-9._-]/g, "_");
22005
22162
  }
22006
22163
  function getUniqueFilename(dir, base, ext) {
22007
- let candidate = path22.join(dir, `${base}${ext}`);
22008
- if (!fs23.existsSync(candidate)) return candidate;
22164
+ let candidate = path23.join(dir, `${base}${ext}`);
22165
+ if (!fs24.existsSync(candidate)) return candidate;
22009
22166
  let i = 1;
22010
- while (fs23.existsSync(candidate)) {
22011
- candidate = path22.join(dir, `${base}-${i}${ext}`);
22167
+ while (fs24.existsSync(candidate)) {
22168
+ candidate = path23.join(dir, `${base}-${i}${ext}`);
22012
22169
  i++;
22013
22170
  }
22014
22171
  return candidate;
@@ -22018,13 +22175,13 @@ var ZipOutput = class {
22018
22175
  label = "Save as .zip to Downloads";
22019
22176
  async emit(group, options) {
22020
22177
  const downloadsDir = getDownloadsFolder();
22021
- const repoName = sanitizeFilename(path22.basename(group.repoPath));
22178
+ const repoName = sanitizeFilename(path23.basename(group.repoPath));
22022
22179
  const timeRange = options.timeRange;
22023
22180
  const rangePart = timeRange?.label ?? "all";
22024
22181
  const epochSeconds = Math.floor(Date.now() / 1e3);
22025
22182
  const baseName = `logs-${repoName}-${rangePart}-${epochSeconds}`;
22026
22183
  const outputPath = getUniqueFilename(downloadsDir, baseName, ".zip");
22027
- const output = fs23.createWriteStream(outputPath);
22184
+ const output = fs24.createWriteStream(outputPath);
22028
22185
  const archive = archiver2("zip", { zlib: { level: 6 } });
22029
22186
  const done = new Promise((resolve, reject) => {
22030
22187
  output.on("close", resolve);
@@ -22218,15 +22375,15 @@ async function confirmExport(group, output) {
22218
22375
  }
22219
22376
 
22220
22377
  // src/sources/claude.ts
22221
- import fs24 from "fs";
22378
+ import fs25 from "fs";
22222
22379
  import os12 from "os";
22223
- import path23 from "path";
22380
+ import path24 from "path";
22224
22381
  import readline3 from "readline";
22225
22382
  var SKIP_DIRS = /* @__PURE__ */ new Set(["tool-results"]);
22226
22383
  async function resolveRepoPath(projectDir) {
22227
- const indexPath = path23.join(projectDir, "sessions-index.json");
22384
+ const indexPath = path24.join(projectDir, "sessions-index.json");
22228
22385
  try {
22229
- const raw = await fs24.promises.readFile(indexPath, "utf-8");
22386
+ const raw = await fs25.promises.readFile(indexPath, "utf-8");
22230
22387
  const data = JSON.parse(raw);
22231
22388
  if (data.originalPath && typeof data.originalPath === "string") {
22232
22389
  return data.originalPath;
@@ -22234,12 +22391,12 @@ async function resolveRepoPath(projectDir) {
22234
22391
  } catch {
22235
22392
  }
22236
22393
  const cwdCounts = /* @__PURE__ */ new Map();
22237
- const entries = await fs24.promises.readdir(projectDir, {
22394
+ const entries = await fs25.promises.readdir(projectDir, {
22238
22395
  withFileTypes: true
22239
22396
  });
22240
22397
  for (const entry of entries) {
22241
22398
  if (!entry.isFile() || !entry.name.endsWith(".jsonl")) continue;
22242
- const cwd = await extractCwdFromJsonl(path23.join(projectDir, entry.name));
22399
+ const cwd = await extractCwdFromJsonl(path24.join(projectDir, entry.name));
22243
22400
  if (cwd) {
22244
22401
  cwdCounts.set(cwd, (cwdCounts.get(cwd) ?? 0) + 1);
22245
22402
  }
@@ -22258,7 +22415,7 @@ async function resolveRepoPath(projectDir) {
22258
22415
  return null;
22259
22416
  }
22260
22417
  async function extractCwdFromJsonl(filePath) {
22261
- const stream = fs24.createReadStream(filePath, { encoding: "utf-8" });
22418
+ const stream = fs25.createReadStream(filePath, { encoding: "utf-8" });
22262
22419
  const rl = readline3.createInterface({ input: stream, crlfDelay: Infinity });
22263
22420
  try {
22264
22421
  for await (const line of rl) {
@@ -22280,12 +22437,12 @@ async function extractCwdFromJsonl(filePath) {
22280
22437
  async function collectFiles(dir, baseDir, sourceName, repoPath, results) {
22281
22438
  let entries;
22282
22439
  try {
22283
- entries = await fs24.promises.readdir(dir, { withFileTypes: true });
22440
+ entries = await fs25.promises.readdir(dir, { withFileTypes: true });
22284
22441
  } catch {
22285
22442
  return;
22286
22443
  }
22287
22444
  for (const entry of entries) {
22288
- const fullPath = path23.join(dir, entry.name);
22445
+ const fullPath = path24.join(dir, entry.name);
22289
22446
  if (entry.isDirectory()) {
22290
22447
  if (SKIP_DIRS.has(entry.name)) continue;
22291
22448
  await collectFiles(fullPath, baseDir, sourceName, repoPath, results);
@@ -22307,19 +22464,19 @@ function fallbackDecode(encodedName) {
22307
22464
  var ClaudeSource = class {
22308
22465
  name = "claude";
22309
22466
  async scan() {
22310
- const baseDir = path23.join(os12.homedir(), ".claude", "projects");
22467
+ const baseDir = path24.join(os12.homedir(), ".claude", "projects");
22311
22468
  try {
22312
- await fs24.promises.access(baseDir);
22469
+ await fs25.promises.access(baseDir);
22313
22470
  } catch {
22314
22471
  return [];
22315
22472
  }
22316
- const projectDirs = await fs24.promises.readdir(baseDir, {
22473
+ const projectDirs = await fs25.promises.readdir(baseDir, {
22317
22474
  withFileTypes: true
22318
22475
  });
22319
22476
  const dirEntries = projectDirs.filter((d) => d.isDirectory());
22320
22477
  const resultArrays = await Promise.all(
22321
22478
  dirEntries.map(async (dir) => {
22322
- const projectPath = path23.join(baseDir, dir.name);
22479
+ const projectPath = path24.join(baseDir, dir.name);
22323
22480
  const repoPath = await resolveRepoPath(projectPath) ?? fallbackDecode(dir.name);
22324
22481
  const files = [];
22325
22482
  await collectFiles(
@@ -22337,12 +22494,12 @@ var ClaudeSource = class {
22337
22494
  };
22338
22495
 
22339
22496
  // src/sources/codex.ts
22340
- import fs25 from "fs";
22497
+ import fs26 from "fs";
22341
22498
  import os13 from "os";
22342
- import path24 from "path";
22499
+ import path25 from "path";
22343
22500
  import readline4 from "readline";
22344
22501
  async function parseSessionMeta2(filePath) {
22345
- const stream = fs25.createReadStream(filePath, { encoding: "utf-8" });
22502
+ const stream = fs26.createReadStream(filePath, { encoding: "utf-8" });
22346
22503
  const rl = readline4.createInterface({ input: stream, crlfDelay: Infinity });
22347
22504
  try {
22348
22505
  for await (const line of rl) {
@@ -22367,12 +22524,12 @@ async function findJsonlFiles(dir) {
22367
22524
  async function walk(d) {
22368
22525
  let entries;
22369
22526
  try {
22370
- entries = await fs25.promises.readdir(d, { withFileTypes: true });
22527
+ entries = await fs26.promises.readdir(d, { withFileTypes: true });
22371
22528
  } catch {
22372
22529
  return;
22373
22530
  }
22374
22531
  for (const entry of entries) {
22375
- const full = path24.join(d, entry.name);
22532
+ const full = path25.join(d, entry.name);
22376
22533
  if (entry.isDirectory()) {
22377
22534
  await walk(full);
22378
22535
  } else if (entry.isFile() && entry.name.endsWith(".jsonl")) {
@@ -22386,11 +22543,11 @@ async function findJsonlFiles(dir) {
22386
22543
  async function loadHistory(historyPath) {
22387
22544
  const map = /* @__PURE__ */ new Map();
22388
22545
  try {
22389
- await fs25.promises.access(historyPath);
22546
+ await fs26.promises.access(historyPath);
22390
22547
  } catch {
22391
22548
  return map;
22392
22549
  }
22393
- const stream = fs25.createReadStream(historyPath, { encoding: "utf-8" });
22550
+ const stream = fs26.createReadStream(historyPath, { encoding: "utf-8" });
22394
22551
  const rl = readline4.createInterface({ input: stream, crlfDelay: Infinity });
22395
22552
  try {
22396
22553
  for await (const line of rl) {
@@ -22417,14 +22574,14 @@ async function loadHistory(historyPath) {
22417
22574
  var CodexSource = class {
22418
22575
  name = "codex";
22419
22576
  async scan() {
22420
- const codexDir = path24.join(os13.homedir(), ".codex");
22421
- const sessionsDir2 = path24.join(codexDir, "sessions");
22577
+ const codexDir = path25.join(os13.homedir(), ".codex");
22578
+ const sessionsDir2 = path25.join(codexDir, "sessions");
22422
22579
  try {
22423
- await fs25.promises.access(sessionsDir2);
22580
+ await fs26.promises.access(sessionsDir2);
22424
22581
  } catch {
22425
22582
  return [];
22426
22583
  }
22427
- const historyPath = path24.join(codexDir, "history.jsonl");
22584
+ const historyPath = path25.join(codexDir, "history.jsonl");
22428
22585
  const [jsonlFiles, historyMap] = await Promise.all([
22429
22586
  findJsonlFiles(sessionsDir2),
22430
22587
  loadHistory(historyPath)
@@ -22447,8 +22604,8 @@ var CodexSource = class {
22447
22604
  });
22448
22605
  const historyLines = historyMap.get(meta.sessionId);
22449
22606
  if (historyLines) {
22450
- const sessionDir = path24.relative(sessionsDir2, path24.dirname(filePath));
22451
- const historyAbsPath = path24.join(
22607
+ const sessionDir = path25.relative(sessionsDir2, path25.dirname(filePath));
22608
+ const historyAbsPath = path25.join(
22452
22609
  sessionsDir2,
22453
22610
  sessionDir,
22454
22611
  `history-${meta.sessionId}.jsonl`
@@ -22468,18 +22625,18 @@ var CodexSource = class {
22468
22625
  };
22469
22626
 
22470
22627
  // src/sources/copilotChat.ts
22471
- import fs26 from "fs";
22628
+ import fs27 from "fs";
22472
22629
  import os14 from "os";
22473
- import path25 from "path";
22630
+ import path26 from "path";
22474
22631
  import { fileURLToPath as fileURLToPath2 } from "url";
22475
22632
  function vsCodeUserDirs() {
22476
22633
  const home = os14.homedir();
22477
22634
  const dirs = [
22478
- path25.join(home, "Library", "Application Support", "Code", "User"),
22479
- path25.join(home, ".config", "Code", "User")
22635
+ path26.join(home, "Library", "Application Support", "Code", "User"),
22636
+ path26.join(home, ".config", "Code", "User")
22480
22637
  ];
22481
22638
  if (process.env.APPDATA) {
22482
- dirs.push(path25.join(process.env.APPDATA, "Code", "User"));
22639
+ dirs.push(path26.join(process.env.APPDATA, "Code", "User"));
22483
22640
  }
22484
22641
  return dirs;
22485
22642
  }
@@ -22494,7 +22651,7 @@ function uriToFsPath(uri) {
22494
22651
  async function readWorkspaceFolder(workspaceJsonPath) {
22495
22652
  let raw;
22496
22653
  try {
22497
- raw = await fs26.promises.readFile(workspaceJsonPath, "utf-8");
22654
+ raw = await fs27.promises.readFile(workspaceJsonPath, "utf-8");
22498
22655
  } catch {
22499
22656
  return null;
22500
22657
  }
@@ -22516,10 +22673,10 @@ var CopilotChatSource = class {
22516
22673
  async scan() {
22517
22674
  const results = [];
22518
22675
  for (const userDir of vsCodeUserDirs()) {
22519
- const workspaceStorage = path25.join(userDir, "workspaceStorage");
22676
+ const workspaceStorage = path26.join(userDir, "workspaceStorage");
22520
22677
  let hashDirs;
22521
22678
  try {
22522
- hashDirs = await fs26.promises.readdir(workspaceStorage, {
22679
+ hashDirs = await fs27.promises.readdir(workspaceStorage, {
22523
22680
  withFileTypes: true
22524
22681
  });
22525
22682
  } catch {
@@ -22527,22 +22684,22 @@ var CopilotChatSource = class {
22527
22684
  }
22528
22685
  for (const hash of hashDirs) {
22529
22686
  if (!hash.isDirectory()) continue;
22530
- const wsRoot = path25.join(workspaceStorage, hash.name);
22531
- const transcriptsDir = path25.join(
22687
+ const wsRoot = path26.join(workspaceStorage, hash.name);
22688
+ const transcriptsDir = path26.join(
22532
22689
  wsRoot,
22533
22690
  "GitHub.copilot-chat",
22534
22691
  "transcripts"
22535
22692
  );
22536
22693
  let transcriptEntries;
22537
22694
  try {
22538
- transcriptEntries = await fs26.promises.readdir(transcriptsDir, {
22695
+ transcriptEntries = await fs27.promises.readdir(transcriptsDir, {
22539
22696
  withFileTypes: true
22540
22697
  });
22541
22698
  } catch {
22542
22699
  continue;
22543
22700
  }
22544
22701
  const repoPath = await readWorkspaceFolder(
22545
- path25.join(wsRoot, "workspace.json")
22702
+ path26.join(wsRoot, "workspace.json")
22546
22703
  );
22547
22704
  if (!repoPath) continue;
22548
22705
  for (const entry of transcriptEntries) {
@@ -22550,7 +22707,7 @@ var CopilotChatSource = class {
22550
22707
  const sessionId = entry.name.slice(0, -".jsonl".length);
22551
22708
  results.push({
22552
22709
  sourceName: this.name,
22553
- absolutePath: path25.join(transcriptsDir, entry.name),
22710
+ absolutePath: path26.join(transcriptsDir, entry.name),
22554
22711
  repoPath,
22555
22712
  metadata: { sessionId }
22556
22713
  });
@@ -22590,7 +22747,7 @@ function reportRedactionStats(noun, stats) {
22590
22747
  async function filterByTimeRange(group, range) {
22591
22748
  const results = await Promise.all(
22592
22749
  group.files.map(
22593
- (f) => fs27.promises.stat(f.absolutePath).then((s) => ({ file: f, stat: s })).catch(() => null)
22750
+ (f) => fs28.promises.stat(f.absolutePath).then((s) => ({ file: f, stat: s })).catch(() => null)
22594
22751
  )
22595
22752
  );
22596
22753
  const filtered = [];
@@ -22617,10 +22774,10 @@ async function runInteractive() {
22617
22774
  s.start(`Scanning ${source.name} logs...`);
22618
22775
  const allFiles = await source.scan();
22619
22776
  const allGroups = await mergeByRepo(allFiles);
22620
- const repoRoot = path26.resolve(repo.root);
22777
+ const repoRoot = path27.resolve(repo.root);
22621
22778
  const matching = allGroups.filter((g) => {
22622
- const resolved = path26.resolve(g.repoPath);
22623
- return resolved === repoRoot || resolved.startsWith(repoRoot + path26.sep);
22779
+ const resolved = path27.resolve(g.repoPath);
22780
+ return resolved === repoRoot || resolved.startsWith(repoRoot + path27.sep);
22624
22781
  });
22625
22782
  if (matching.length === 0) {
22626
22783
  s.stop(`No ${source.name} logs found for ${repo.name}.`);
@@ -22651,7 +22808,7 @@ async function runInteractive() {
22651
22808
  }
22652
22809
  }
22653
22810
  const envFileNames = await discoverEnvFiles(repoRoot);
22654
- const envFilePaths = envFileNames.map((n) => path26.join(repoRoot, n));
22811
+ const envFilePaths = envFileNames.map((n) => path27.join(repoRoot, n));
22655
22812
  const additionalFiles = await promptSecretFiles(envFileNames);
22656
22813
  const secretResult = await collectSecrets(
22657
22814
  repoRoot,