opencode-codebase-index 0.24.0 → 0.25.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.cjs CHANGED
@@ -496,7 +496,7 @@ var require_ignore = __commonJS({
496
496
  // path matching.
497
497
  // - check `string` either `MODE_IGNORE` or `MODE_CHECK_IGNORE`
498
498
  // @returns {TestResult} true if a file is ignored
499
- test(path33, checkUnignored, mode) {
499
+ test(path34, checkUnignored, mode) {
500
500
  let ignored = false;
501
501
  let unignored = false;
502
502
  let matchedRule;
@@ -505,7 +505,7 @@ var require_ignore = __commonJS({
505
505
  if (unignored === negative && ignored !== unignored || negative && !ignored && !unignored && !checkUnignored) {
506
506
  return;
507
507
  }
508
- const matched = rule[mode].test(path33);
508
+ const matched = rule[mode].test(path34);
509
509
  if (!matched) {
510
510
  return;
511
511
  }
@@ -526,17 +526,17 @@ var require_ignore = __commonJS({
526
526
  var throwError = (message, Ctor) => {
527
527
  throw new Ctor(message);
528
528
  };
529
- var checkPath = (path33, originalPath, doThrow) => {
530
- if (!isString(path33)) {
529
+ var checkPath = (path34, originalPath, doThrow) => {
530
+ if (!isString(path34)) {
531
531
  return doThrow(
532
532
  `path must be a string, but got \`${originalPath}\``,
533
533
  TypeError
534
534
  );
535
535
  }
536
- if (!path33) {
536
+ if (!path34) {
537
537
  return doThrow(`path must not be empty`, TypeError);
538
538
  }
539
- if (checkPath.isNotRelative(path33)) {
539
+ if (checkPath.isNotRelative(path34)) {
540
540
  const r = "`path.relative()`d";
541
541
  return doThrow(
542
542
  `path should be a ${r} string, but got "${originalPath}"`,
@@ -545,7 +545,7 @@ var require_ignore = __commonJS({
545
545
  }
546
546
  return true;
547
547
  };
548
- var isNotRelative = (path33) => REGEX_TEST_INVALID_PATH.test(path33);
548
+ var isNotRelative = (path34) => REGEX_TEST_INVALID_PATH.test(path34);
549
549
  checkPath.isNotRelative = isNotRelative;
550
550
  checkPath.convert = (p) => p;
551
551
  var Ignore2 = class {
@@ -575,19 +575,19 @@ var require_ignore = __commonJS({
575
575
  }
576
576
  // @returns {TestResult}
577
577
  _test(originalPath, cache, checkUnignored, slices) {
578
- const path33 = originalPath && checkPath.convert(originalPath);
578
+ const path34 = originalPath && checkPath.convert(originalPath);
579
579
  checkPath(
580
- path33,
580
+ path34,
581
581
  originalPath,
582
582
  this._strictPathCheck ? throwError : RETURN_FALSE
583
583
  );
584
- return this._t(path33, cache, checkUnignored, slices);
584
+ return this._t(path34, cache, checkUnignored, slices);
585
585
  }
586
- checkIgnore(path33) {
587
- if (!REGEX_TEST_TRAILING_SLASH.test(path33)) {
588
- return this.test(path33);
586
+ checkIgnore(path34) {
587
+ if (!REGEX_TEST_TRAILING_SLASH.test(path34)) {
588
+ return this.test(path34);
589
589
  }
590
- const slices = path33.split(SLASH2).filter(Boolean);
590
+ const slices = path34.split(SLASH2).filter(Boolean);
591
591
  slices.pop();
592
592
  if (slices.length) {
593
593
  const parent = this._t(
@@ -600,18 +600,18 @@ var require_ignore = __commonJS({
600
600
  return parent;
601
601
  }
602
602
  }
603
- return this._rules.test(path33, false, MODE_CHECK_IGNORE);
603
+ return this._rules.test(path34, false, MODE_CHECK_IGNORE);
604
604
  }
605
- _t(path33, cache, checkUnignored, slices) {
606
- if (path33 in cache) {
607
- return cache[path33];
605
+ _t(path34, cache, checkUnignored, slices) {
606
+ if (path34 in cache) {
607
+ return cache[path34];
608
608
  }
609
609
  if (!slices) {
610
- slices = path33.split(SLASH2).filter(Boolean);
610
+ slices = path34.split(SLASH2).filter(Boolean);
611
611
  }
612
612
  slices.pop();
613
613
  if (!slices.length) {
614
- return cache[path33] = this._rules.test(path33, checkUnignored, MODE_IGNORE);
614
+ return cache[path34] = this._rules.test(path34, checkUnignored, MODE_IGNORE);
615
615
  }
616
616
  const parent = this._t(
617
617
  slices.join(SLASH2) + SLASH2,
@@ -619,29 +619,29 @@ var require_ignore = __commonJS({
619
619
  checkUnignored,
620
620
  slices
621
621
  );
622
- return cache[path33] = parent.ignored ? parent : this._rules.test(path33, checkUnignored, MODE_IGNORE);
622
+ return cache[path34] = parent.ignored ? parent : this._rules.test(path34, checkUnignored, MODE_IGNORE);
623
623
  }
624
- ignores(path33) {
625
- return this._test(path33, this._ignoreCache, false).ignored;
624
+ ignores(path34) {
625
+ return this._test(path34, this._ignoreCache, false).ignored;
626
626
  }
627
627
  createFilter() {
628
- return (path33) => !this.ignores(path33);
628
+ return (path34) => !this.ignores(path34);
629
629
  }
630
630
  filter(paths) {
631
631
  return makeArray(paths).filter(this.createFilter());
632
632
  }
633
633
  // @returns {TestResult}
634
- test(path33) {
635
- return this._test(path33, this._testCache, true);
634
+ test(path34) {
635
+ return this._test(path34, this._testCache, true);
636
636
  }
637
637
  };
638
638
  var factory = (options) => new Ignore2(options);
639
- var isPathValid = (path33) => checkPath(path33 && checkPath.convert(path33), path33, RETURN_FALSE);
639
+ var isPathValid = (path34) => checkPath(path34 && checkPath.convert(path34), path34, RETURN_FALSE);
640
640
  var setupWindows = () => {
641
641
  const makePosix = (str) => /^\\\\\?\\/.test(str) || /["<>|\u0000-\u001F]+/u.test(str) ? str : str.replace(/\\/g, "/");
642
642
  checkPath.convert = makePosix;
643
643
  const REGEX_TEST_WINDOWS_PATH_ABSOLUTE = /^[a-z]:\//i;
644
- checkPath.isNotRelative = (path33) => REGEX_TEST_WINDOWS_PATH_ABSOLUTE.test(path33) || isNotRelative(path33);
644
+ checkPath.isNotRelative = (path34) => REGEX_TEST_WINDOWS_PATH_ABSOLUTE.test(path34) || isNotRelative(path34);
645
645
  };
646
646
  if (
647
647
  // Detect `process` so that it can run in browsers.
@@ -668,8 +668,8 @@ module.exports = __toCommonJS(cli_exports);
668
668
  // src/adapters/mcp/cli.ts
669
669
  var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
670
670
  var import_fs20 = require("fs");
671
- var os8 = __toESM(require("os"), 1);
672
- var path32 = __toESM(require("path"), 1);
671
+ var os9 = __toESM(require("os"), 1);
672
+ var path33 = __toESM(require("path"), 1);
673
673
  var import_url = require("url");
674
674
 
675
675
  // src/config/constants.ts
@@ -1197,9 +1197,9 @@ var import_fs = require("fs");
1197
1197
  var path = __toESM(require("path"), 1);
1198
1198
 
1199
1199
  // src/eval/report-formatters.ts
1200
- function assertFiniteNumber(value, path33) {
1200
+ function assertFiniteNumber(value, path34) {
1201
1201
  if (typeof value !== "number" || Number.isNaN(value) || !Number.isFinite(value)) {
1202
- throw new Error(`${path33} must be a finite number`);
1202
+ throw new Error(`${path34} must be a finite number`);
1203
1203
  }
1204
1204
  return value;
1205
1205
  }
@@ -1439,7 +1439,7 @@ function buildPerQueryArtifact(perQuery) {
1439
1439
  // src/eval/runner.ts
1440
1440
  var crypto2 = __toESM(require("crypto"), 1);
1441
1441
  var import_fs17 = require("fs");
1442
- var path23 = __toESM(require("path"), 1);
1442
+ var path24 = __toESM(require("path"), 1);
1443
1443
  var import_perf_hooks2 = require("perf_hooks");
1444
1444
 
1445
1445
  // src/indexer/index.ts
@@ -1471,7 +1471,7 @@ function pTimeout(promise, options) {
1471
1471
  } = options;
1472
1472
  let timer;
1473
1473
  let abortHandler;
1474
- const wrappedPromise = new Promise((resolve20, reject) => {
1474
+ const wrappedPromise = new Promise((resolve21, reject) => {
1475
1475
  if (typeof milliseconds !== "number" || Math.sign(milliseconds) !== 1) {
1476
1476
  throw new TypeError(`Expected \`milliseconds\` to be a positive number, got \`${milliseconds}\``);
1477
1477
  }
@@ -1485,7 +1485,7 @@ function pTimeout(promise, options) {
1485
1485
  };
1486
1486
  signal.addEventListener("abort", abortHandler, { once: true });
1487
1487
  }
1488
- promise.then(resolve20, reject);
1488
+ promise.then(resolve21, reject);
1489
1489
  if (milliseconds === Number.POSITIVE_INFINITY) {
1490
1490
  return;
1491
1491
  }
@@ -1493,7 +1493,7 @@ function pTimeout(promise, options) {
1493
1493
  timer = customTimers.setTimeout.call(void 0, () => {
1494
1494
  if (fallback) {
1495
1495
  try {
1496
- resolve20(fallback());
1496
+ resolve21(fallback());
1497
1497
  } catch (error) {
1498
1498
  reject(error);
1499
1499
  }
@@ -1503,7 +1503,7 @@ function pTimeout(promise, options) {
1503
1503
  promise.cancel();
1504
1504
  }
1505
1505
  if (message === false) {
1506
- resolve20();
1506
+ resolve21();
1507
1507
  } else if (message instanceof Error) {
1508
1508
  reject(message);
1509
1509
  } else {
@@ -1905,7 +1905,7 @@ var PQueue = class extends import_index.default {
1905
1905
  // Assign unique ID if not provided
1906
1906
  id: options.id ?? (this.#idAssigner++).toString()
1907
1907
  };
1908
- return new Promise((resolve20, reject) => {
1908
+ return new Promise((resolve21, reject) => {
1909
1909
  const taskSymbol = /* @__PURE__ */ Symbol(`task-${options.id}`);
1910
1910
  let cleanupQueueAbortHandler = () => void 0;
1911
1911
  const run = async () => {
@@ -1945,7 +1945,7 @@ var PQueue = class extends import_index.default {
1945
1945
  })]);
1946
1946
  }
1947
1947
  const result = await operation;
1948
- resolve20(result);
1948
+ resolve21(result);
1949
1949
  this.emit("completed", result);
1950
1950
  } catch (error) {
1951
1951
  reject(error);
@@ -2133,13 +2133,13 @@ var PQueue = class extends import_index.default {
2133
2133
  });
2134
2134
  }
2135
2135
  async #onEvent(event, filter) {
2136
- return new Promise((resolve20) => {
2136
+ return new Promise((resolve21) => {
2137
2137
  const listener = () => {
2138
2138
  if (filter && !filter()) {
2139
2139
  return;
2140
2140
  }
2141
2141
  this.off(event, listener);
2142
- resolve20();
2142
+ resolve21();
2143
2143
  };
2144
2144
  this.on(event, listener);
2145
2145
  });
@@ -2425,7 +2425,7 @@ async function onAttemptFailure({ error, attemptNumber, retriesConsumed, startTi
2425
2425
  const finalDelay = Math.min(delayTime, remainingTime);
2426
2426
  options.signal?.throwIfAborted();
2427
2427
  if (finalDelay > 0) {
2428
- await new Promise((resolve20, reject) => {
2428
+ await new Promise((resolve21, reject) => {
2429
2429
  const onAbort = () => {
2430
2430
  clearTimeout(timeoutToken);
2431
2431
  options.signal?.removeEventListener("abort", onAbort);
@@ -2433,7 +2433,7 @@ async function onAttemptFailure({ error, attemptNumber, retriesConsumed, startTi
2433
2433
  };
2434
2434
  const timeoutToken = setTimeout(() => {
2435
2435
  options.signal?.removeEventListener("abort", onAbort);
2436
- resolve20();
2436
+ resolve21();
2437
2437
  }, finalDelay);
2438
2438
  if (options.unref) {
2439
2439
  timeoutToken.unref?.();
@@ -2795,17 +2795,17 @@ function validateExternalUrl(urlString) {
2795
2795
  if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
2796
2796
  return { valid: false, reason: `Blocked protocol: ${parsed.protocol}` };
2797
2797
  }
2798
- const hostname2 = parsed.hostname.toLowerCase();
2799
- if (BLOCKED_HOSTNAMES.has(hostname2)) {
2800
- return { valid: false, reason: `Blocked: cloud metadata service (${hostname2})` };
2798
+ const hostname3 = parsed.hostname.toLowerCase();
2799
+ if (BLOCKED_HOSTNAMES.has(hostname3)) {
2800
+ return { valid: false, reason: `Blocked: cloud metadata service (${hostname3})` };
2801
2801
  }
2802
2802
  for (const pattern of BLOCKED_METADATA_IPS) {
2803
- if (pattern.test(hostname2)) {
2804
- return { valid: false, reason: `Blocked: cloud metadata IP (${hostname2})` };
2803
+ if (pattern.test(hostname3)) {
2804
+ return { valid: false, reason: `Blocked: cloud metadata IP (${hostname3})` };
2805
2805
  }
2806
2806
  }
2807
- if (/^169\.254\./.test(hostname2)) {
2808
- return { valid: false, reason: `Blocked: link-local address (${hostname2})` };
2807
+ if (/^169\.254\./.test(hostname3)) {
2808
+ return { valid: false, reason: `Blocked: link-local address (${hostname3})` };
2809
2809
  }
2810
2810
  return { valid: true };
2811
2811
  }
@@ -3391,26 +3391,46 @@ function createIgnoreFilter(projectRoot) {
3391
3391
  }
3392
3392
  return ig;
3393
3393
  }
3394
- function shouldIncludeFile(filePath, projectRoot, includePatterns, excludePatterns, ignoreFilter) {
3395
- const relativePath = path4.relative(projectRoot, filePath);
3396
- if (hasFilteredPathSegment(relativePath, path4.sep)) {
3397
- return false;
3398
- }
3399
- if (ignoreFilter.ignores(relativePath)) {
3400
- return false;
3394
+ function toPosixRelativePath(relativePath) {
3395
+ return relativePath.split(path4.sep).join("/");
3396
+ }
3397
+ function matchesAnyGlob(filePath, patterns) {
3398
+ const normalized = toPosixRelativePath(filePath);
3399
+ return patterns.some((pattern) => matchGlob(normalized, pattern));
3400
+ }
3401
+ function isExcludedByPatterns(relativePath, excludePatterns) {
3402
+ return matchesAnyGlob(relativePath, excludePatterns);
3403
+ }
3404
+ function isExcludedDirectory(relativePath, excludePatterns) {
3405
+ const normalized = toPosixRelativePath(relativePath);
3406
+ if (matchesAnyGlob(normalized, excludePatterns)) {
3407
+ return true;
3401
3408
  }
3402
3409
  for (const pattern of excludePatterns) {
3403
- if (matchGlob(relativePath, pattern)) {
3404
- return false;
3410
+ const posixPattern = toPosixRelativePath(pattern).replace(/\/+$/, "");
3411
+ if (!posixPattern.endsWith("/**")) {
3412
+ continue;
3405
3413
  }
3406
- }
3407
- for (const pattern of includePatterns) {
3408
- if (matchGlob(relativePath, pattern)) {
3414
+ const directoryPattern = posixPattern.slice(0, -3);
3415
+ if (directoryPattern && matchesAnyGlob(normalized, [directoryPattern])) {
3409
3416
  return true;
3410
3417
  }
3411
3418
  }
3412
3419
  return false;
3413
3420
  }
3421
+ function shouldIncludeFile(filePath, projectRoot, includePatterns, excludePatterns, ignoreFilter) {
3422
+ const relativePath = toPosixRelativePath(path4.relative(projectRoot, filePath));
3423
+ if (hasFilteredPathSegment(relativePath, "/")) {
3424
+ return false;
3425
+ }
3426
+ if (ignoreFilter.ignores(relativePath)) {
3427
+ return false;
3428
+ }
3429
+ if (isExcludedByPatterns(relativePath, excludePatterns)) {
3430
+ return false;
3431
+ }
3432
+ return matchesAnyGlob(relativePath, includePatterns);
3433
+ }
3414
3434
  function matchGlob(filePath, pattern) {
3415
3435
  if (pattern.startsWith("**/")) {
3416
3436
  const withoutPrefix = pattern.slice(3);
@@ -3432,7 +3452,7 @@ async function* walkDirectory(dir, projectRoot, includePatterns, excludePatterns
3432
3452
  const subdirs = [];
3433
3453
  for (const entry of entries) {
3434
3454
  const fullPath = path4.join(dir, entry.name);
3435
- const relativePath = path4.relative(projectRoot, fullPath);
3455
+ const relativePath = toPosixRelativePath(path4.relative(projectRoot, fullPath));
3436
3456
  if (isHiddenPathSegment(entry.name)) {
3437
3457
  if (entry.isDirectory()) {
3438
3458
  skipped.push({ path: relativePath, reason: "excluded" });
@@ -3450,6 +3470,10 @@ async function* walkDirectory(dir, projectRoot, includePatterns, excludePatterns
3450
3470
  continue;
3451
3471
  }
3452
3472
  if (entry.isDirectory()) {
3473
+ if (isExcludedDirectory(relativePath, excludePatterns)) {
3474
+ skipped.push({ path: relativePath, reason: "excluded" });
3475
+ continue;
3476
+ }
3453
3477
  subdirs.push({ fullPath, relativePath });
3454
3478
  } else if (entry.isFile()) {
3455
3479
  const stat5 = await import_fs3.promises.stat(fullPath);
@@ -3457,20 +3481,11 @@ async function* walkDirectory(dir, projectRoot, includePatterns, excludePatterns
3457
3481
  skipped.push({ path: relativePath, reason: "too_large" });
3458
3482
  continue;
3459
3483
  }
3460
- for (const pattern of excludePatterns) {
3461
- if (matchGlob(relativePath, pattern)) {
3462
- skipped.push({ path: relativePath, reason: "excluded" });
3463
- continue;
3464
- }
3465
- }
3466
- let matched = false;
3467
- for (const pattern of includePatterns) {
3468
- if (matchGlob(relativePath, pattern)) {
3469
- matched = true;
3470
- break;
3471
- }
3484
+ if (isExcludedByPatterns(relativePath, excludePatterns)) {
3485
+ skipped.push({ path: relativePath, reason: "excluded" });
3486
+ continue;
3472
3487
  }
3473
- if (matched) {
3488
+ if (matchesAnyGlob(relativePath, includePatterns)) {
3474
3489
  filesInDir.push({ path: fullPath, size: stat5.size });
3475
3490
  }
3476
3491
  }
@@ -3481,7 +3496,7 @@ async function* walkDirectory(dir, projectRoot, includePatterns, excludePatterns
3481
3496
  yield f;
3482
3497
  }
3483
3498
  for (let i = options.maxFilesPerDirectory; i < filesInDir.length; i++) {
3484
- skipped.push({ path: path4.relative(projectRoot, filesInDir[i].path), reason: "excluded" });
3499
+ skipped.push({ path: toPosixRelativePath(path4.relative(projectRoot, filesInDir[i].path)), reason: "excluded" });
3485
3500
  }
3486
3501
  const canRecurse = options.maxDepth === -1 || currentDepth < options.maxDepth;
3487
3502
  if (canRecurse) {
@@ -3610,6 +3625,26 @@ function formatCostEstimate(estimate) {
3610
3625
  \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518
3611
3626
  `;
3612
3627
  }
3628
+ function formatDryRunEstimate(estimate) {
3629
+ return `Dry run: parsed the file set to measure the embedding workload. No embedding requests were made and the index was not changed.
3630
+
3631
+ Files to embed: ${estimate.filesCount.toLocaleString()}
3632
+ Chunks to embed: ${estimate.chunksCount.toLocaleString()}
3633
+ Tokens to embed: ${estimate.tokensToEmbed.toLocaleString()}
3634
+
3635
+ The "Tokens to embed" value uses the local estimateTokens(text) = ceil(len/4). It
3636
+ matches the live "Tokens used" counter only for providers that report usage on the
3637
+ same basis (ollama); for providers that report a server tokenizer count (OpenAI,
3638
+ Gemini, custom) it is only an estimate.
3639
+
3640
+ For a matching provider and a project-scoped force index, the force pass clears its
3641
+ own cached embeddings, so the live counter climbs to this number. A force index on a
3642
+ shared global index can reuse cached embeddings from other projects, and an
3643
+ incremental index counts cached chunks that are not re-embedded; in both cases this
3644
+ number is an upper bound on the live counter, so a progress percent against this
3645
+ total tops out below 100%.
3646
+ `;
3647
+ }
3613
3648
  function formatBytes(bytes) {
3614
3649
  if (bytes === 0) return "0 B";
3615
3650
  const k = 1024;
@@ -6835,6 +6870,9 @@ var CALL_GRAPH_SYMBOL_CHUNK_TYPES = /* @__PURE__ */ new Set([
6835
6870
  "enum_declaration",
6836
6871
  "function_definition",
6837
6872
  "class_definition",
6873
+ // Ruby module/class symbols that are declaration-bearing and navigable.
6874
+ "class",
6875
+ "module",
6838
6876
  "class_specifier",
6839
6877
  "struct_specifier",
6840
6878
  "namespace_definition",
@@ -7079,8 +7117,8 @@ function pathSegmentsForAffinityMatch(filePath) {
7079
7117
  if (segments.length === 0) {
7080
7118
  return [];
7081
7119
  }
7082
- const basename8 = segments[segments.length - 1] ?? "";
7083
- const basenameWithoutExt = basename8.replace(/\.[^/.]+$/u, "");
7120
+ const basename9 = segments[segments.length - 1] ?? "";
7121
+ const basenameWithoutExt = basename9.replace(/\.[^/.]+$/u, "");
7084
7122
  const normalizedSegments = segments.map((segment) => segment.toLowerCase());
7085
7123
  return Array.from(/* @__PURE__ */ new Set([
7086
7124
  ...normalizedSegments,
@@ -7493,7 +7531,7 @@ function removeDeadReclaimMarker(lockPath, expectedOwner) {
7493
7531
  return true;
7494
7532
  }
7495
7533
  function reclaimDeadOwner(indexPath, lockPath, expectedOwner) {
7496
- const reclaimPath = path13.join(lockPath, RECLAIM_DIRECTORY_NAME);
7534
+ const reclaimPath2 = path13.join(lockPath, RECLAIM_DIRECTORY_NAME);
7497
7535
  const reclaimOwner = {
7498
7536
  pid: process.pid,
7499
7537
  hostname: os5.hostname(),
@@ -7502,19 +7540,19 @@ function reclaimDeadOwner(indexPath, lockPath, expectedOwner) {
7502
7540
  expectedOwnerToken: expectedOwner.token
7503
7541
  };
7504
7542
  for (let attempt = 0; attempt < 2; attempt += 1) {
7505
- if (publishJsonDirectory(reclaimPath, reclaimOwner)) break;
7543
+ if (publishJsonDirectory(reclaimPath2, reclaimOwner)) break;
7506
7544
  if (attempt === 0 && removeDeadReclaimMarker(lockPath, expectedOwner)) continue;
7507
7545
  return false;
7508
7546
  }
7509
7547
  try {
7510
- const currentReclaimer = readReclaimOwner(reclaimPath);
7548
+ const currentReclaimer = readReclaimOwner(reclaimPath2);
7511
7549
  const currentOwner = readDirectoryOwner(lockPath);
7512
7550
  if (!currentReclaimer || !sameReclaimOwner(currentReclaimer, reclaimOwner) || !currentOwner || !sameOwner(currentOwner, expectedOwner) || getOwnerLiveness(currentOwner) !== "dead") {
7513
7551
  return false;
7514
7552
  }
7515
7553
  publishRecoveryMarker(indexPath, expectedOwner);
7516
7554
  const ownerBeforeQuarantine = readDirectoryOwner(lockPath);
7517
- const reclaimerBeforeQuarantine = readReclaimOwner(reclaimPath);
7555
+ const reclaimerBeforeQuarantine = readReclaimOwner(reclaimPath2);
7518
7556
  if (!ownerBeforeQuarantine || !sameOwner(ownerBeforeQuarantine, expectedOwner) || getOwnerLiveness(ownerBeforeQuarantine) !== "dead" || !reclaimerBeforeQuarantine || !sameReclaimOwner(reclaimerBeforeQuarantine, reclaimOwner)) {
7519
7557
  return false;
7520
7558
  }
@@ -8730,6 +8768,17 @@ var Indexer = class _Indexer {
8730
8768
  }
8731
8769
  return path15.relative(this.projectRoot, canonicalFilePath).split(path15.sep).join("/");
8732
8770
  }
8771
+ isStoredPathExcluded(storedPath) {
8772
+ let matchPath = storedPath.split(path15.sep).join("/");
8773
+ if (path15.isAbsolute(storedPath)) {
8774
+ const relativePath = path15.relative(this.projectRoot, storedPath).split(path15.sep).join("/");
8775
+ if (relativePath.startsWith("..") || path15.isAbsolute(relativePath)) {
8776
+ return false;
8777
+ }
8778
+ matchPath = relativePath;
8779
+ }
8780
+ return isExcludedByPatterns(matchPath, this.config.exclude);
8781
+ }
8733
8782
  resolveStoredFilePath(filePath, rootPath = this.projectRoot) {
8734
8783
  if (path15.isAbsolute(filePath)) {
8735
8784
  return filePath;
@@ -9754,7 +9803,7 @@ var Indexer = class _Indexer {
9754
9803
  await options.queue.onSizeLessThan(Math.max(1, options.providerRateLimits.concurrency));
9755
9804
  const task = options.queue.add(async () => {
9756
9805
  if (options.rateLimitState.backoffMs > 0) {
9757
- await new Promise((resolve20) => setTimeout(resolve20, options.rateLimitState.backoffMs));
9806
+ await new Promise((resolve21) => setTimeout(resolve21, options.rateLimitState.backoffMs));
9758
9807
  }
9759
9808
  try {
9760
9809
  const embeddingResult = await pRetry(
@@ -10871,6 +10920,70 @@ var Indexer = class _Indexer {
10871
10920
  );
10872
10921
  return createCostEstimate(files, configuredProviderInfo);
10873
10922
  }
10923
+ // Dry-run counterpart to index()/forceIndex(): parse the real file set and sum
10924
+ // estimateTokens over the embedding text of every indexable chunk, without
10925
+ // calling the embedding provider or writing to the index. Read-only and
10926
+ // lock-free (mirrors estimateCost). The token sum is the exact value "Tokens
10927
+ // used" climbs to for a force index (cache bypassed); for an incremental it is
10928
+ // an upper bound because cached chunks are counted here but not re-embedded.
10929
+ // Used by index_codebase(dryRun:true) to give a stable, monotonic progress
10930
+ // denominator that matches the live "Tokens used" basis.
10931
+ async dryRunCost() {
10932
+ const { configuredProviderInfo } = await this.ensureInitialized();
10933
+ const maxChunkTokens = getSafeEmbeddingChunkTokenLimit(configuredProviderInfo);
10934
+ const includePatterns = [...this.config.include, ...this.config.additionalInclude];
10935
+ const { files } = await collectFiles(
10936
+ this.materializedProjectRoot,
10937
+ includePatterns,
10938
+ this.config.exclude,
10939
+ this.config.indexing.maxFileSize,
10940
+ this.getMaterializedKnowledgeBases(),
10941
+ { maxDepth: this.config.indexing.maxDepth, maxFilesPerDirectory: this.config.indexing.maxFilesPerDirectory }
10942
+ );
10943
+ let filesCount = 0;
10944
+ let chunksCount = 0;
10945
+ let tokensToEmbed = 0;
10946
+ for (const batch of iterateOrderedFileBatches(files, (f) => f.size, this.fileBatchLimits)) {
10947
+ const loadedFiles = await Promise.all(batch.map(async (f) => {
10948
+ try {
10949
+ return {
10950
+ path: this.toStoredFilePath(f.path),
10951
+ content: await import_fs10.promises.readFile(f.path, "utf-8")
10952
+ };
10953
+ } catch {
10954
+ return null;
10955
+ }
10956
+ }));
10957
+ const readable = loadedFiles.filter(
10958
+ (f) => f !== null
10959
+ );
10960
+ filesCount += readable.length;
10961
+ const contentByPath = new Map(readable.map((f) => [f.path, f.content]));
10962
+ const parsedFiles = parseFiles(readable, this.config.indexing.linesPerChunk);
10963
+ for (const parsed of parsedFiles) {
10964
+ let chunksToProcess = parsed.chunks;
10965
+ if (this.config.indexing.fallbackToTextOnMaxChunks && chunksToProcess.length > this.config.indexing.maxChunksPerFile) {
10966
+ const content = contentByPath.get(parsed.path);
10967
+ if (content !== void 0) {
10968
+ chunksToProcess = parseFileAsText(parsed.path, content, this.config.indexing.linesPerChunk);
10969
+ }
10970
+ }
10971
+ chunksToProcess = selectIndexableChunks(
10972
+ chunksToProcess,
10973
+ this.config.indexing.maxChunksPerFile,
10974
+ this.config.indexing.semanticOnly
10975
+ );
10976
+ for (const chunk of chunksToProcess) {
10977
+ const texts = createEmbeddingTexts(chunk, parsed.path, maxChunkTokens);
10978
+ chunksCount += 1;
10979
+ for (const text of texts) {
10980
+ tokensToEmbed += estimateTokens2(text);
10981
+ }
10982
+ }
10983
+ }
10984
+ }
10985
+ return { filesCount, chunksCount, tokensToEmbed };
10986
+ }
10874
10987
  async index(onProgress) {
10875
10988
  return this.withIndexMutationLease("index", async (recoveredOwners) => {
10876
10989
  return this.indexUnlocked(onProgress, recoveredOwners);
@@ -11067,7 +11180,7 @@ var Indexer = class _Indexer {
11067
11180
  }
11068
11181
  }
11069
11182
  }
11070
- const shouldRetryFailedPath = (filePath) => filePath !== null && currentFileHashes.has(filePath) && unchangedFilePaths.has(filePath);
11183
+ const shouldRetryFailedPath = (filePath) => filePath !== null && !this.isStoredPathExcluded(filePath) && currentFileHashes.has(filePath) && unchangedFilePaths.has(filePath);
11071
11184
  const failedProcessing = this.prepareFailedBatchProcessing(scopedRoots, shouldRetryFailedPath);
11072
11185
  const maxChunkTokens = getSafeEmbeddingChunkTokenLimit(configuredProviderInfo);
11073
11186
  const providerRateLimits = this.getProviderRateLimits(configuredProviderInfo.provider);
@@ -12395,7 +12508,8 @@ var Indexer = class _Indexer {
12395
12508
  const maxChunkTokens = getSafeEmbeddingChunkTokenLimit(configuredProviderInfo);
12396
12509
  const providerRateLimits = this.getProviderRateLimits(configuredProviderInfo.provider);
12397
12510
  const roots = this.config.scope === "global" ? this.getScopedRoots() : null;
12398
- const failedProcessing = this.prepareFailedBatchProcessing(roots, () => true);
12511
+ const shouldProcessFailedPath = (filePath) => filePath === null || !this.isStoredPathExcluded(filePath);
12512
+ const failedProcessing = this.prepareFailedBatchProcessing(roots, shouldProcessFailedPath);
12399
12513
  if (failedProcessing.latestById.size === 0) {
12400
12514
  this.finalizeFailedBatchWriteState(failedProcessing.state);
12401
12515
  return { succeeded: 0, failed: 0, remaining: 0 };
@@ -12408,7 +12522,7 @@ var Indexer = class _Indexer {
12408
12522
  const retryableChunks = this.iterateLatestFailedChunks(
12409
12523
  failedProcessing.latestById,
12410
12524
  roots,
12411
- () => true,
12525
+ shouldProcessFailedPath,
12412
12526
  maxChunkTokens
12413
12527
  );
12414
12528
  for (const retryBatch of iterateOrderedFileBatches(
@@ -12678,9 +12792,9 @@ var Indexer = class _Indexer {
12678
12792
  this.requireReadableComponents(readIssues, "database");
12679
12793
  let shortest = [];
12680
12794
  for (const branchKey of this.getBranchCatalogKeys()) {
12681
- const path33 = database.findShortestPath(fromName, toName, branchKey, maxDepth);
12682
- if (path33.length > 0 && (shortest.length === 0 || path33.length < shortest.length)) {
12683
- shortest = path33;
12795
+ const path34 = database.findShortestPath(fromName, toName, branchKey, maxDepth);
12796
+ if (path34.length > 0 && (shortest.length === 0 || path34.length < shortest.length)) {
12797
+ shortest = path34;
12684
12798
  }
12685
12799
  }
12686
12800
  return shortest.map((hop) => this.resolveFilePathRecord(hop));
@@ -12728,13 +12842,13 @@ var Indexer = class _Indexer {
12728
12842
  }
12729
12843
  }
12730
12844
  if (!found) continue;
12731
- const path33 = [];
12845
+ const path34 = [];
12732
12846
  let currentSymbolId = toSymbolId;
12733
12847
  while (true) {
12734
12848
  const symbol = symbolsById.get(currentSymbolId);
12735
12849
  if (!symbol) break;
12736
12850
  const parent = parentBySymbolId.get(currentSymbolId);
12737
- path33.push({
12851
+ path34.push({
12738
12852
  symbolId: symbol.id,
12739
12853
  symbolName: symbol.name,
12740
12854
  filePath: symbol.filePath,
@@ -12744,9 +12858,9 @@ var Indexer = class _Indexer {
12744
12858
  if (!parent) break;
12745
12859
  currentSymbolId = parent.parentId;
12746
12860
  }
12747
- path33.reverse();
12748
- if (path33.length > 0 && (shortest.length === 0 || path33.length < shortest.length)) {
12749
- shortest = path33;
12861
+ path34.reverse();
12862
+ if (path34.length > 0 && (shortest.length === 0 || path34.length < shortest.length)) {
12863
+ shortest = path34;
12750
12864
  }
12751
12865
  }
12752
12866
  return shortest.map((hop) => this.resolveFilePathRecord(hop));
@@ -13130,7 +13244,7 @@ var CODE_COMMUNITIES_MAX_COUPLING_LIMIT = 100;
13130
13244
 
13131
13245
  // src/tools/operations.ts
13132
13246
  var import_fs14 = require("fs");
13133
- var path21 = __toESM(require("path"), 1);
13247
+ var path22 = __toESM(require("path"), 1);
13134
13248
 
13135
13249
  // src/tools/knowledge-base-paths.ts
13136
13250
  var path16 = __toESM(require("path"), 1);
@@ -13425,8 +13539,8 @@ function formatExactSearchHandoff(results) {
13425
13539
  }
13426
13540
  function formatContextEvidence(result, index) {
13427
13541
  const symbol = result.name ? ` ${JSON.stringify(compactEvidenceValue(result.name, 80))}` : "";
13428
- const path33 = compactEvidenceValue(result.filePath, 120);
13429
- return `[${index}] ${result.chunkType}${symbol} in ${path33}:${result.startLine}-${result.endLine} (score ${result.score.toFixed(2)})`;
13542
+ const path34 = compactEvidenceValue(result.filePath, 120);
13543
+ return `[${index}] ${result.chunkType}${symbol} in ${path34}:${result.startLine}-${result.endLine} (score ${result.score.toFixed(2)})`;
13430
13544
  }
13431
13545
  function formatContextPack(heading, selected, candidateCount, duplicateCount, limitOmittedCount, budgetOmittedCount, includeExactSearchHandoff) {
13432
13546
  const lines = selected.map((result, index) => formatContextEvidence(result, index + 1));
@@ -14069,124 +14183,43 @@ function formatEffectivenessMetrics(snapshot) {
14069
14183
 
14070
14184
  // src/utils/auto-index.ts
14071
14185
  var import_fs11 = require("fs");
14186
+ var os7 = __toESM(require("os"), 1);
14187
+ var path18 = __toESM(require("path"), 1);
14188
+
14189
+ // src/utils/background-worker.ts
14190
+ var import_node_crypto2 = require("crypto");
14191
+ var import_node_fs = require("fs");
14072
14192
  var os6 = __toESM(require("os"), 1);
14073
14193
  var path17 = __toESM(require("path"), 1);
14074
-
14075
- // src/utils/power-source.ts
14076
- var childProcess = __toESM(require("child_process"), 1);
14077
- var POWER_SOURCE_RECHECK_DELAY_MS = 6e4;
14078
- var PMSET_TIMEOUT_MS = 5e3;
14079
- function getErrorMessage4(error) {
14080
- return error instanceof Error ? error.message : String(error);
14081
- }
14082
- function runCommand(file, args, options) {
14083
- return new Promise((resolve20, reject) => {
14084
- childProcess.execFile(
14085
- file,
14086
- args,
14087
- { encoding: "utf8", timeout: options.timeoutMs },
14088
- (error, stdout) => {
14089
- if (error) {
14090
- reject(error);
14091
- return;
14092
- }
14093
- resolve20(stdout);
14094
- }
14095
- );
14096
- });
14097
- }
14098
- function parseMacOsPowerSource(output) {
14099
- const match = output.match(/Now drawing from '([^']+)'/i);
14100
- if (!match) {
14101
- return "unknown";
14102
- }
14103
- const source = match[1].toLowerCase();
14104
- if (source === "battery power") {
14105
- return "battery";
14106
- }
14107
- if (source === "ac power") {
14108
- return "ac";
14109
- }
14110
- return "unknown";
14111
- }
14112
- async function readMacOsPowerSource(commandRunner = runCommand) {
14113
- const output = await commandRunner(
14114
- "/usr/bin/pmset",
14115
- ["-g", "batt"],
14116
- { timeoutMs: PMSET_TIMEOUT_MS }
14117
- );
14118
- return parseMacOsPowerSource(output);
14119
- }
14120
- var MacOsBackgroundIndexingPolicy = class {
14121
- constructor(readPowerSource, recheckDelayMs) {
14122
- this.readPowerSource = readPowerSource;
14123
- this.recheckDelayMs = recheckDelayMs;
14124
- }
14125
- readPowerSource;
14126
- recheckDelayMs;
14127
- lastPaused = null;
14128
- reportedFailure = false;
14129
- isPaused() {
14130
- return this.checkPowerSource();
14131
- }
14132
- async checkPowerSource() {
14133
- try {
14134
- const source = await this.readPowerSource();
14135
- if (source === "unknown") {
14136
- throw new Error("pmset returned an unrecognized power source");
14137
- }
14138
- this.reportedFailure = false;
14139
- const paused = source === "battery";
14140
- if (paused && this.lastPaused !== true) {
14141
- console.warn("[codebase-index] Background indexing paused while macOS is using battery power.");
14142
- } else if (!paused && this.lastPaused === true) {
14143
- console.warn("[codebase-index] AC power detected; resuming pending background indexing.");
14144
- }
14145
- this.lastPaused = paused;
14146
- return paused;
14147
- } catch (error) {
14148
- if (!this.reportedFailure) {
14149
- console.error(
14150
- `[codebase-index] Failed to determine the macOS power source; background indexing will continue: ${getErrorMessage4(error)}`
14151
- );
14152
- this.reportedFailure = true;
14153
- }
14154
- this.lastPaused = false;
14155
- return false;
14156
- }
14157
- }
14158
- };
14159
- function createBackgroundIndexingPolicy(pauseOnBattery, options = {}) {
14160
- const platform2 = options.platform ?? process.platform;
14161
- if (!pauseOnBattery || platform2 !== "darwin") {
14162
- return null;
14163
- }
14164
- return new MacOsBackgroundIndexingPolicy(
14165
- options.readPowerSource ?? readMacOsPowerSource,
14166
- options.recheckDelayMs ?? POWER_SOURCE_RECHECK_DELAY_MS
14167
- );
14168
- }
14169
-
14170
- // src/utils/auto-index.ts
14171
- var MAX_RETRY_DELAY_MS = 1e4;
14172
- var SHUTDOWN_WAIT_MS = 2e3;
14173
- var coordinators = /* @__PURE__ */ new Map();
14174
- var coordinatorKeysByProject = /* @__PURE__ */ new Map();
14175
- var coordinatorReplacementBarriers = /* @__PURE__ */ new Map();
14176
- var AutoIndexCancelledError = class extends Error {
14177
- constructor() {
14178
- super("Auto-index coordination was cancelled");
14179
- this.name = "AutoIndexCancelledError";
14180
- }
14194
+ var OWNER_FILE_NAME2 = "owner.json";
14195
+ var HEARTBEAT_FILE_PREFIX = "heartbeat.";
14196
+ var RECLAIM_DIRECTORY_NAME2 = "reclaim";
14197
+ var REFRESH_REQUEST_FILE_NAME = "refresh-request.json";
14198
+ var HEARTBEAT_INTERVAL_MS = 5e3;
14199
+ var STALE_LEASE_MS = 3e4;
14200
+ var RETRY_DELAY_MS = 5e3;
14201
+ var UUID_PATTERN2 = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
14202
+ var BackgroundWorkerStopError = class extends Error {
14203
+ constructor(watcherError, autoIndexError) {
14204
+ super("Failed to stop background worker");
14205
+ this.watcherError = watcherError;
14206
+ this.autoIndexError = autoIndexError;
14207
+ this.name = "BackgroundWorkerStopError";
14208
+ }
14209
+ watcherError;
14210
+ autoIndexError;
14181
14211
  };
14182
- function now() {
14183
- return (/* @__PURE__ */ new Date()).toISOString();
14212
+ var workers = /* @__PURE__ */ new Map();
14213
+ var workerKeysByProject = /* @__PURE__ */ new Map();
14214
+ var workerReplacementBarriers = /* @__PURE__ */ new Map();
14215
+ function getErrorCode2(error) {
14216
+ return typeof error === "object" && error !== null && "code" in error ? String(error.code) : void 0;
14184
14217
  }
14185
14218
  function canonicalizePath(targetPath) {
14186
14219
  const resolved = path17.resolve(targetPath);
14187
- if ((0, import_fs11.existsSync)(resolved)) {
14220
+ if ((0, import_node_fs.existsSync)(resolved)) {
14188
14221
  try {
14189
- return import_fs11.realpathSync.native(resolved);
14222
+ return import_node_fs.realpathSync.native(resolved);
14190
14223
  } catch {
14191
14224
  return resolved;
14192
14225
  }
@@ -14195,50 +14228,1020 @@ function canonicalizePath(targetPath) {
14195
14228
  if (parent === resolved) return resolved;
14196
14229
  return path17.join(canonicalizePath(parent), path17.basename(resolved));
14197
14230
  }
14198
- function isHomeDirectory(projectRoot) {
14199
- return canonicalizePath(projectRoot) === canonicalizePath(os6.homedir());
14200
- }
14201
14231
  function projectLookupKey(projectRoot, host) {
14202
14232
  return `${host}::${canonicalizePath(projectRoot)}`;
14203
14233
  }
14204
- function coordinatorKey(projectRoot, config, host) {
14234
+ function getBackgroundWorkerProjectKey(projectRoot, host) {
14235
+ return projectLookupKey(projectRoot, host);
14236
+ }
14237
+ function resolveIdentity(projectRoot, config, host) {
14205
14238
  const canonicalProjectRoot = canonicalizePath(projectRoot);
14206
- const indexPath = resolveProjectIndexPath(projectRoot, config.scope, host);
14207
- return `${canonicalizePath(indexPath)}::${canonicalProjectRoot}`;
14239
+ const canonicalIndexPath = canonicalizePath(resolveProjectIndexPath(projectRoot, config.scope, host));
14240
+ return {
14241
+ canonicalIndexPath,
14242
+ canonicalProjectRoot,
14243
+ key: `${canonicalIndexPath}::${canonicalProjectRoot}`
14244
+ };
14208
14245
  }
14209
- function getProjectSafety(projectRoot, config) {
14210
- if (isHomeDirectory(projectRoot)) {
14211
- return { safeToRun: false, blockedReason: "home-directory" };
14212
- }
14213
- if (config.indexing.requireProjectMarker && !hasProjectMarker(projectRoot)) {
14214
- return { safeToRun: false, blockedReason: "project-marker-missing" };
14246
+ function controllerKey(identity, host) {
14247
+ return `${identity.key}::${host}`;
14248
+ }
14249
+ function leaseDirectoryName(identity) {
14250
+ const hash = (0, import_node_crypto2.createHash)("sha256").update(identity.key).digest("hex").slice(0, 32);
14251
+ return `background-worker.${hash}.lease`;
14252
+ }
14253
+ function leasePathFor(identity) {
14254
+ return path17.join(identity.canonicalIndexPath, leaseDirectoryName(identity));
14255
+ }
14256
+ function parseOwner2(value) {
14257
+ if (typeof value !== "object" || value === null) return null;
14258
+ const candidate = value;
14259
+ if (candidate.version !== 1) return null;
14260
+ if (!Number.isInteger(candidate.pid) || (candidate.pid ?? 0) <= 0) return null;
14261
+ if (typeof candidate.hostname !== "string" || candidate.hostname.length === 0) return null;
14262
+ if (typeof candidate.startedAt !== "string" || Number.isNaN(Date.parse(candidate.startedAt))) return null;
14263
+ if (typeof candidate.heartbeatAt !== "string" || Number.isNaN(Date.parse(candidate.heartbeatAt))) return null;
14264
+ if (typeof candidate.projectRoot !== "string" || candidate.projectRoot.length === 0) return null;
14265
+ if (typeof candidate.indexPath !== "string" || candidate.indexPath.length === 0) return null;
14266
+ if (typeof candidate.token !== "string" || !UUID_PATTERN2.test(candidate.token)) return null;
14267
+ return candidate;
14268
+ }
14269
+ function parseHeartbeat(value, expectedToken) {
14270
+ if (typeof value !== "object" || value === null) return null;
14271
+ const candidate = value;
14272
+ if (candidate.version !== 1 || candidate.token !== expectedToken) return null;
14273
+ if (typeof candidate.heartbeatAt !== "string" || Number.isNaN(Date.parse(candidate.heartbeatAt))) return null;
14274
+ return candidate;
14275
+ }
14276
+ function parseReclaimOwner2(value) {
14277
+ if (typeof value !== "object" || value === null) return null;
14278
+ const candidate = value;
14279
+ if (candidate.version !== 1) return null;
14280
+ if (!Number.isInteger(candidate.pid) || (candidate.pid ?? 0) <= 0) return null;
14281
+ if (typeof candidate.hostname !== "string" || candidate.hostname.length === 0) return null;
14282
+ if (typeof candidate.startedAt !== "string" || Number.isNaN(Date.parse(candidate.startedAt))) return null;
14283
+ if (typeof candidate.token !== "string" || !UUID_PATTERN2.test(candidate.token)) return null;
14284
+ if (candidate.expectedOwnerToken !== null && (typeof candidate.expectedOwnerToken !== "string" || !UUID_PATTERN2.test(candidate.expectedOwnerToken))) return null;
14285
+ return candidate;
14286
+ }
14287
+ function heartbeatPath(leasePath, token) {
14288
+ return path17.join(leasePath, `${HEARTBEAT_FILE_PREFIX}${token}.json`);
14289
+ }
14290
+ function reclaimPath(leasePath) {
14291
+ return path17.join(leasePath, RECLAIM_DIRECTORY_NAME2);
14292
+ }
14293
+ function refreshRequestPath(leasePath) {
14294
+ return path17.join(leasePath, REFRESH_REQUEST_FILE_NAME);
14295
+ }
14296
+ function readLeaseOwner(leasePath) {
14297
+ try {
14298
+ return parseOwner2(JSON.parse((0, import_node_fs.readFileSync)(path17.join(leasePath, OWNER_FILE_NAME2), "utf-8")));
14299
+ } catch {
14300
+ return null;
14215
14301
  }
14216
- return { safeToRun: true };
14217
14302
  }
14218
- function calculatePercentage2(progress) {
14219
- if (progress.phase === "scanning") return 0;
14220
- if (progress.phase === "complete") return 100;
14221
- if (progress.phase === "parsing") {
14222
- return progress.totalFiles === 0 ? 5 : Math.round(5 + progress.filesProcessed / progress.totalFiles * 15);
14303
+ function readOwner(leasePath) {
14304
+ const owner = readLeaseOwner(leasePath);
14305
+ if (!owner) return null;
14306
+ try {
14307
+ const heartbeat = parseHeartbeat(
14308
+ JSON.parse((0, import_node_fs.readFileSync)(heartbeatPath(leasePath, owner.token), "utf-8")),
14309
+ owner.token
14310
+ );
14311
+ return heartbeat ? { ...owner, heartbeatAt: heartbeat.heartbeatAt } : owner;
14312
+ } catch {
14313
+ return owner;
14223
14314
  }
14224
- if (progress.phase === "embedding") {
14225
- return progress.totalChunks === 0 ? 20 : Math.round(20 + progress.chunksProcessed / progress.totalChunks * 70);
14315
+ }
14316
+ function readReclaimOwner2(leasePath) {
14317
+ try {
14318
+ return parseReclaimOwner2(JSON.parse((0, import_node_fs.readFileSync)(path17.join(reclaimPath(leasePath), OWNER_FILE_NAME2), "utf-8")));
14319
+ } catch {
14320
+ return null;
14226
14321
  }
14227
- if (progress.phase === "storing") return 95;
14228
- return 0;
14229
14322
  }
14230
- function safeFailureMessage(error) {
14231
- if (isTransientIndexLockContention(error)) {
14232
- return "Another index process remained busy after the configured retries.";
14323
+ function ownerLiveness(owner) {
14324
+ if (owner.hostname !== os6.hostname()) return "unknown";
14325
+ try {
14326
+ process.kill(owner.pid, 0);
14327
+ return "alive";
14328
+ } catch (error) {
14329
+ const code = getErrorCode2(error);
14330
+ if (code === "ESRCH") return "dead";
14331
+ if (code === "EPERM") return "alive";
14332
+ return "unknown";
14233
14333
  }
14234
- return "Automatic indexing failed. Check the embedding provider configuration, then run index_codebase.";
14235
14334
  }
14236
- function cancellableDelay(delayMs, signal) {
14237
- if (signal.aborted) return Promise.reject(new AutoIndexCancelledError());
14238
- return new Promise((resolve20, reject) => {
14239
- const timer = setTimeout(() => {
14335
+ function isHeartbeatExpired(owner) {
14336
+ return Date.now() - Date.parse(owner.heartbeatAt) >= STALE_LEASE_MS;
14337
+ }
14338
+ function sameOwner2(left, right) {
14339
+ return left.pid === right.pid && left.hostname === right.hostname && left.token === right.token;
14340
+ }
14341
+ function writeHeartbeat(leasePath, owner) {
14342
+ const targetPath = heartbeatPath(leasePath, owner.token);
14343
+ const temporaryPath = `${targetPath}.tmp.${process.pid}.${owner.token}.${(0, import_node_crypto2.randomUUID)()}`;
14344
+ const heartbeat = {
14345
+ version: 1,
14346
+ token: owner.token,
14347
+ heartbeatAt: owner.heartbeatAt
14348
+ };
14349
+ try {
14350
+ (0, import_node_fs.writeFileSync)(temporaryPath, JSON.stringify(heartbeat), {
14351
+ encoding: "utf-8",
14352
+ flag: "wx",
14353
+ mode: 384
14354
+ });
14355
+ (0, import_node_fs.renameSync)(temporaryPath, targetPath);
14356
+ const currentOwner = readLeaseOwner(leasePath);
14357
+ return currentOwner !== null && sameOwner2(currentOwner, owner);
14358
+ } finally {
14359
+ if ((0, import_node_fs.existsSync)(temporaryPath)) (0, import_node_fs.rmSync)(temporaryPath, { force: true });
14360
+ }
14361
+ }
14362
+ function requestRefreshFromLeader(leasePath, allowDisabledAutoIndex) {
14363
+ const requestPath = refreshRequestPath(leasePath);
14364
+ const temporaryPath = `${requestPath}.tmp.${process.pid}.${(0, import_node_crypto2.randomUUID)()}`;
14365
+ try {
14366
+ const request = {
14367
+ allowDisabledAutoIndex,
14368
+ requestedAt: (/* @__PURE__ */ new Date()).toISOString(),
14369
+ version: 1
14370
+ };
14371
+ (0, import_node_fs.writeFileSync)(temporaryPath, JSON.stringify(request), {
14372
+ encoding: "utf-8",
14373
+ flag: "wx",
14374
+ mode: 384
14375
+ });
14376
+ (0, import_node_fs.renameSync)(temporaryPath, requestPath);
14377
+ } catch (error) {
14378
+ if (getErrorCode2(error) !== "ENOENT") {
14379
+ console.error("[codebase-index] Failed to request background index refresh from the project worker:", error);
14380
+ }
14381
+ } finally {
14382
+ if ((0, import_node_fs.existsSync)(temporaryPath)) (0, import_node_fs.rmSync)(temporaryPath, { force: true });
14383
+ }
14384
+ }
14385
+ function consumeRefreshRequest(leasePath) {
14386
+ const requestPath = refreshRequestPath(leasePath);
14387
+ const claimedPath = `${requestPath}.handling.${process.pid}.${(0, import_node_crypto2.randomUUID)()}`;
14388
+ try {
14389
+ (0, import_node_fs.renameSync)(requestPath, claimedPath);
14390
+ } catch (error) {
14391
+ if (getErrorCode2(error) === "ENOENT") return null;
14392
+ throw error;
14393
+ }
14394
+ try {
14395
+ const value = JSON.parse((0, import_node_fs.readFileSync)(claimedPath, "utf-8"));
14396
+ return {
14397
+ allowDisabledAutoIndex: value.version === 1 && value.allowDisabledAutoIndex === true,
14398
+ requestedAt: typeof value.requestedAt === "string" ? value.requestedAt : (/* @__PURE__ */ new Date()).toISOString(),
14399
+ version: 1
14400
+ };
14401
+ } catch {
14402
+ return { allowDisabledAutoIndex: false, requestedAt: (/* @__PURE__ */ new Date()).toISOString(), version: 1 };
14403
+ } finally {
14404
+ (0, import_node_fs.rmSync)(claimedPath, { force: true });
14405
+ }
14406
+ }
14407
+ function publishLease(leasePath, owner) {
14408
+ const candidatePath = `${leasePath}.candidate.${process.pid}.${owner.token}`;
14409
+ try {
14410
+ (0, import_node_fs.mkdirSync)(candidatePath, { mode: 448 });
14411
+ } catch (error) {
14412
+ if (getErrorCode2(error) === "ENOENT") return false;
14413
+ throw error;
14414
+ }
14415
+ try {
14416
+ (0, import_node_fs.writeFileSync)(path17.join(candidatePath, OWNER_FILE_NAME2), JSON.stringify(owner), {
14417
+ encoding: "utf-8",
14418
+ flag: "wx",
14419
+ mode: 384
14420
+ });
14421
+ if ((0, import_node_fs.existsSync)(leasePath)) return false;
14422
+ try {
14423
+ (0, import_node_fs.renameSync)(candidatePath, leasePath);
14424
+ return true;
14425
+ } catch (error) {
14426
+ if ((0, import_node_fs.existsSync)(leasePath) || getErrorCode2(error) === "ENOENT") return false;
14427
+ throw error;
14428
+ }
14429
+ } finally {
14430
+ if ((0, import_node_fs.existsSync)(candidatePath)) (0, import_node_fs.rmSync)(candidatePath, { recursive: true, force: true });
14431
+ }
14432
+ }
14433
+ function sameReclaimOwner2(left, right) {
14434
+ return left.pid === right.pid && left.hostname === right.hostname && left.token === right.token && left.expectedOwnerToken === right.expectedOwnerToken;
14435
+ }
14436
+ function reclaimerLiveness(owner) {
14437
+ return ownerLiveness(owner);
14438
+ }
14439
+ function isReclaimMarkerExpired(leasePath, owner) {
14440
+ const startedAt = owner ? Date.parse(owner.startedAt) : (() => {
14441
+ try {
14442
+ return (0, import_node_fs.lstatSync)(reclaimPath(leasePath)).mtimeMs;
14443
+ } catch {
14444
+ return Date.now();
14445
+ }
14446
+ })();
14447
+ return Date.now() - startedAt >= STALE_LEASE_MS;
14448
+ }
14449
+ function hasActiveReclaimMarker(leasePath, owner) {
14450
+ const marker = readReclaimOwner2(leasePath);
14451
+ return marker !== null && marker.expectedOwnerToken === owner.token && (marker.hostname !== os6.hostname() || ownerLiveness(owner) !== "alive");
14452
+ }
14453
+ function publishReclaimMarker(leasePath, expectedOwner) {
14454
+ const markerPath = reclaimPath(leasePath);
14455
+ const owner = {
14456
+ version: 1,
14457
+ pid: process.pid,
14458
+ hostname: os6.hostname(),
14459
+ startedAt: (/* @__PURE__ */ new Date()).toISOString(),
14460
+ token: (0, import_node_crypto2.randomUUID)(),
14461
+ expectedOwnerToken: expectedOwner?.token ?? null
14462
+ };
14463
+ try {
14464
+ (0, import_node_fs.mkdirSync)(markerPath, { mode: 448 });
14465
+ } catch (error) {
14466
+ if (getErrorCode2(error) === "EEXIST" || getErrorCode2(error) === "ENOENT") return null;
14467
+ throw error;
14468
+ }
14469
+ try {
14470
+ (0, import_node_fs.writeFileSync)(path17.join(markerPath, OWNER_FILE_NAME2), JSON.stringify(owner), {
14471
+ encoding: "utf-8",
14472
+ flag: "wx",
14473
+ mode: 384
14474
+ });
14475
+ return owner;
14476
+ } catch (error) {
14477
+ (0, import_node_fs.rmSync)(markerPath, { recursive: true, force: true });
14478
+ throw error;
14479
+ }
14480
+ }
14481
+ function removeExpiredReclaimMarker(leasePath, expectedOwner) {
14482
+ const marker = readReclaimOwner2(leasePath);
14483
+ const markerPath = reclaimPath(leasePath);
14484
+ if (!(0, import_node_fs.existsSync)(markerPath)) return false;
14485
+ if (marker && marker.expectedOwnerToken !== (expectedOwner?.token ?? null)) return false;
14486
+ if (marker && (reclaimerLiveness(marker) === "alive" || !isReclaimMarkerExpired(leasePath, marker))) return false;
14487
+ if (!marker && !isReclaimMarkerExpired(leasePath, null)) return false;
14488
+ const staleMarkerPath = `${markerPath}.stale.${marker?.pid ?? process.pid}.${marker?.token ?? (0, import_node_crypto2.randomUUID)()}.${(0, import_node_crypto2.randomUUID)()}`;
14489
+ try {
14490
+ (0, import_node_fs.renameSync)(markerPath, staleMarkerPath);
14491
+ } catch (error) {
14492
+ if (getErrorCode2(error) === "ENOENT") return false;
14493
+ throw error;
14494
+ }
14495
+ try {
14496
+ let claimedMarker = null;
14497
+ try {
14498
+ claimedMarker = parseReclaimOwner2(
14499
+ JSON.parse((0, import_node_fs.readFileSync)(path17.join(staleMarkerPath, OWNER_FILE_NAME2), "utf-8"))
14500
+ );
14501
+ } catch {
14502
+ claimedMarker = null;
14503
+ }
14504
+ const markerMatches = marker ? claimedMarker !== null && sameReclaimOwner2(claimedMarker, marker) : claimedMarker === null;
14505
+ if (!markerMatches || !canReclaimLease(leasePath, expectedOwner)) {
14506
+ if (!(0, import_node_fs.existsSync)(markerPath) && (0, import_node_fs.existsSync)(staleMarkerPath)) (0, import_node_fs.renameSync)(staleMarkerPath, markerPath);
14507
+ return false;
14508
+ }
14509
+ (0, import_node_fs.rmSync)(staleMarkerPath, { recursive: true, force: true });
14510
+ return true;
14511
+ } catch (error) {
14512
+ if (getErrorCode2(error) === "ENOENT") return false;
14513
+ throw error;
14514
+ }
14515
+ }
14516
+ function canReclaimLease(leasePath, expectedOwner) {
14517
+ if (!(0, import_node_fs.existsSync)(leasePath)) return false;
14518
+ if (!expectedOwner) return false;
14519
+ const currentOwner = readOwner(leasePath);
14520
+ if (!currentOwner || !sameOwner2(currentOwner, expectedOwner)) return false;
14521
+ if (currentOwner.hostname === os6.hostname()) {
14522
+ return ownerLiveness(currentOwner) === "dead";
14523
+ }
14524
+ return isHeartbeatExpired(currentOwner);
14525
+ }
14526
+ function reclaimLease(leasePath, expectedOwner) {
14527
+ let marker = null;
14528
+ for (let attempt = 0; attempt < 2; attempt += 1) {
14529
+ marker = publishReclaimMarker(leasePath, expectedOwner);
14530
+ if (marker) break;
14531
+ if (attempt === 0 && removeExpiredReclaimMarker(leasePath, expectedOwner)) continue;
14532
+ return false;
14533
+ }
14534
+ if (!marker) return false;
14535
+ const markerPath = reclaimPath(leasePath);
14536
+ try {
14537
+ const currentMarker = readReclaimOwner2(leasePath);
14538
+ if (!currentMarker || !sameReclaimOwner2(currentMarker, marker) || !canReclaimLease(leasePath, expectedOwner)) {
14539
+ return false;
14540
+ }
14541
+ const stalePath = `${leasePath}.stale.${process.pid}.${marker.token}`;
14542
+ (0, import_node_fs.renameSync)(leasePath, stalePath);
14543
+ const quarantinedOwner = readOwner(stalePath);
14544
+ const quarantinedMarker = readReclaimOwner2(stalePath);
14545
+ if (!quarantinedMarker || !sameReclaimOwner2(quarantinedMarker, marker) || expectedOwner !== null && (!quarantinedOwner || !sameOwner2(quarantinedOwner, expectedOwner))) {
14546
+ if (!(0, import_node_fs.existsSync)(leasePath) && (0, import_node_fs.existsSync)(stalePath)) (0, import_node_fs.renameSync)(stalePath, leasePath);
14547
+ return false;
14548
+ }
14549
+ (0, import_node_fs.rmSync)(stalePath, { recursive: true, force: true });
14550
+ return true;
14551
+ } catch (error) {
14552
+ if (getErrorCode2(error) === "ENOENT") return false;
14553
+ throw error;
14554
+ } finally {
14555
+ const currentMarker = readReclaimOwner2(leasePath);
14556
+ if (currentMarker && sameReclaimOwner2(currentMarker, marker)) {
14557
+ (0, import_node_fs.rmSync)(markerPath, { recursive: true, force: true });
14558
+ }
14559
+ }
14560
+ }
14561
+ function acquireLease(identity) {
14562
+ (0, import_node_fs.mkdirSync)(identity.canonicalIndexPath, { recursive: true, mode: 448 });
14563
+ const canonicalIndexPath = import_node_fs.realpathSync.native(identity.canonicalIndexPath);
14564
+ const leasePath = path17.join(canonicalIndexPath, leaseDirectoryName({ ...identity, canonicalIndexPath }));
14565
+ for (let attempt = 0; attempt < 4; attempt += 1) {
14566
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
14567
+ const owner = {
14568
+ version: 1,
14569
+ pid: process.pid,
14570
+ hostname: os6.hostname(),
14571
+ startedAt: timestamp,
14572
+ heartbeatAt: timestamp,
14573
+ projectRoot: identity.canonicalProjectRoot,
14574
+ indexPath: canonicalIndexPath,
14575
+ token: (0, import_node_crypto2.randomUUID)()
14576
+ };
14577
+ if (publishLease(leasePath, owner)) {
14578
+ return { leasePath, owner };
14579
+ }
14580
+ const existingOwner = readOwner(leasePath);
14581
+ if (existingOwner) {
14582
+ if (canReclaimLease(leasePath, existingOwner) && reclaimLease(leasePath, existingOwner)) continue;
14583
+ return null;
14584
+ }
14585
+ return null;
14586
+ }
14587
+ return null;
14588
+ }
14589
+ function releaseLease(lease) {
14590
+ const currentOwner = readOwner(lease.leasePath);
14591
+ if (!currentOwner || !sameOwner2(currentOwner, lease.owner)) return false;
14592
+ const releasePath = `${lease.leasePath}.release.${lease.owner.pid}.${lease.owner.token}`;
14593
+ try {
14594
+ (0, import_node_fs.renameSync)(lease.leasePath, releasePath);
14595
+ } catch (error) {
14596
+ if (getErrorCode2(error) === "ENOENT") return false;
14597
+ throw error;
14598
+ }
14599
+ const claimedOwner = readOwner(releasePath);
14600
+ if (!claimedOwner || !sameOwner2(claimedOwner, lease.owner)) {
14601
+ if (!(0, import_node_fs.existsSync)(lease.leasePath) && (0, import_node_fs.existsSync)(releasePath)) {
14602
+ (0, import_node_fs.renameSync)(releasePath, lease.leasePath);
14603
+ }
14604
+ return false;
14605
+ }
14606
+ (0, import_node_fs.rmSync)(releasePath, { recursive: true, force: true });
14607
+ return true;
14608
+ }
14609
+ var BackgroundWorkerController = class {
14610
+ constructor(projectRoot, host, config, hooks, identity) {
14611
+ this.projectRoot = projectRoot;
14612
+ this.host = host;
14613
+ this.config = config;
14614
+ this.hooks = hooks;
14615
+ this.identity = identity;
14616
+ }
14617
+ projectRoot;
14618
+ host;
14619
+ config;
14620
+ hooks;
14621
+ identity;
14622
+ lease = null;
14623
+ watcher = null;
14624
+ leaderReady = Promise.resolve();
14625
+ heartbeatTimer = null;
14626
+ retryTimer = null;
14627
+ teardownRetryTimer = null;
14628
+ transition = Promise.resolve();
14629
+ stopPromise = null;
14630
+ stopped = false;
14631
+ stopping = false;
14632
+ losingLeadership = false;
14633
+ restartAfterStop = false;
14634
+ leaderWorkStopped = false;
14635
+ startingLeaderWork = false;
14636
+ stopAutoIndexOnTeardown = true;
14637
+ autoIndexStarted = false;
14638
+ reportedError = null;
14639
+ update(config, hooks, options) {
14640
+ const autoIndexWasEnabled = this.config.indexing.autoIndex;
14641
+ const shouldReplaceWatcher = this.watcher !== null && hooks.watcherFactory !== void 0 && (hooks.watcherFactory === null || hooks.replaceWatcher === true);
14642
+ this.config = config;
14643
+ this.hooks = {
14644
+ ...this.hooks,
14645
+ ...hooks,
14646
+ watcherFactory: hooks.watcherFactory === void 0 ? this.hooks.watcherFactory : hooks.watcherFactory,
14647
+ watcherFactoryForConfig: hooks.watcherFactoryForConfig === void 0 ? this.hooks.watcherFactoryForConfig : hooks.watcherFactoryForConfig
14648
+ };
14649
+ if (autoIndexWasEnabled && !config.indexing.autoIndex || options.restartAutoIndex === true && config.indexing.autoIndex && !this.startingLeaderWork) {
14650
+ this.autoIndexStarted = false;
14651
+ }
14652
+ if (!this.canRun()) {
14653
+ void this.stop().catch((error) => {
14654
+ console.error("[codebase-index] Failed to stop background worker after disabling automatic work:", error);
14655
+ });
14656
+ return;
14657
+ }
14658
+ if (shouldReplaceWatcher) {
14659
+ void this.enqueue(async () => {
14660
+ const watcher = this.watcher;
14661
+ if (watcher) {
14662
+ await watcher.stop();
14663
+ if (this.watcher === watcher) this.watcher = null;
14664
+ }
14665
+ if (this.lease && !this.stopped) this.startLeaderWork();
14666
+ }).catch((error) => {
14667
+ console.error("[codebase-index] Failed to replace background file watcher:", error);
14668
+ });
14669
+ }
14670
+ this.start();
14671
+ }
14672
+ startAfter(activation) {
14673
+ this.transition = activation.catch(() => void 0);
14674
+ this.start();
14675
+ }
14676
+ start() {
14677
+ if (!this.canRun() || this.losingLeadership) return;
14678
+ if (this.stopping) {
14679
+ this.restartAfterStop = true;
14680
+ return;
14681
+ }
14682
+ this.stopped = false;
14683
+ void this.enqueue(async () => {
14684
+ if (this.stopped || this.stopping || this.losingLeadership || !this.canRun()) return;
14685
+ if (!this.lease) {
14686
+ try {
14687
+ this.lease = acquireLease(this.identity);
14688
+ this.reportedError = null;
14689
+ } catch (error) {
14690
+ this.reportAcquireError(error);
14691
+ this.scheduleRetry();
14692
+ return;
14693
+ }
14694
+ }
14695
+ if (!this.lease) {
14696
+ this.scheduleRetry();
14697
+ return;
14698
+ }
14699
+ this.startHeartbeat();
14700
+ this.startLeaderWork();
14701
+ });
14702
+ }
14703
+ waitForStart() {
14704
+ return this.transition.catch(() => void 0).then(() => this.leaderReady);
14705
+ }
14706
+ requestRefresh(allowDisabledAutoIndex = false) {
14707
+ this.start();
14708
+ if (!this.isLeader()) {
14709
+ requestRefreshFromLeader(leasePathFor(this.identity), allowDisabledAutoIndex);
14710
+ return;
14711
+ }
14712
+ void this.enqueue(async () => {
14713
+ if (this.stopped || !this.lease) return;
14714
+ this.hooks.startAutoIndex("retrieval", allowDisabledAutoIndex);
14715
+ });
14716
+ }
14717
+ isLeader() {
14718
+ return this.lease !== null && !this.stopping && !this.losingLeadership;
14719
+ }
14720
+ isStopping() {
14721
+ return this.stopping;
14722
+ }
14723
+ getHooksForConfig(config) {
14724
+ const watcherFactoryForConfig = this.hooks.watcherFactoryForConfig;
14725
+ if (!watcherFactoryForConfig) return this.hooks;
14726
+ return {
14727
+ ...this.hooks,
14728
+ watcherFactory: watcherFactoryForConfig(config),
14729
+ replaceWatcher: true
14730
+ };
14731
+ }
14732
+ attachWatcher(watcherFactory, watcherFactoryForConfig) {
14733
+ if (this.hooks.watcherFactory !== void 0) return;
14734
+ this.hooks = {
14735
+ ...this.hooks,
14736
+ watcherFactory,
14737
+ watcherFactoryForConfig: watcherFactoryForConfig ?? this.hooks.watcherFactoryForConfig
14738
+ };
14739
+ this.start();
14740
+ }
14741
+ async stop(stopAutoIndex = true) {
14742
+ if (this.stopPromise) return this.stopPromise;
14743
+ this.stopped = true;
14744
+ this.stopping = true;
14745
+ this.stopAutoIndexOnTeardown &&= stopAutoIndex;
14746
+ this.clearRetryTimer();
14747
+ const attempt = this.enqueue(async () => {
14748
+ try {
14749
+ const lease = this.lease;
14750
+ if (this.leaderWorkStopped) {
14751
+ if (lease) {
14752
+ this.releaseStoppedLease(lease);
14753
+ } else {
14754
+ this.finishStoppedLease();
14755
+ }
14756
+ return;
14757
+ }
14758
+ const hadLeaderWork = lease !== null || this.watcher !== null || this.autoIndexStarted;
14759
+ const stopped = await this.stopLeaderWork(hadLeaderWork && this.stopAutoIndexOnTeardown);
14760
+ if (!lease) {
14761
+ this.finishStoppedLease();
14762
+ return;
14763
+ }
14764
+ if (!stopped.completed) {
14765
+ this.releaseLeaseWhenAutoIndexStops(lease, stopped.completion);
14766
+ return;
14767
+ }
14768
+ this.leaderWorkStopped = true;
14769
+ this.releaseStoppedLease(lease);
14770
+ } catch (error) {
14771
+ this.scheduleTeardownRetry();
14772
+ throw error;
14773
+ }
14774
+ });
14775
+ const completion = attempt.finally(() => {
14776
+ if (this.stopPromise === completion) this.stopPromise = null;
14777
+ });
14778
+ this.stopPromise = completion;
14779
+ return completion;
14780
+ }
14781
+ canRun() {
14782
+ return this.config.indexing.autoIndex || this.hooks.watcherFactory != null;
14783
+ }
14784
+ enqueue(operation) {
14785
+ const next = this.transition.catch(() => void 0).then(operation);
14786
+ this.transition = next;
14787
+ return next;
14788
+ }
14789
+ startLeaderWork() {
14790
+ if (this.stopped || this.stopping || this.losingLeadership) return;
14791
+ this.startingLeaderWork = true;
14792
+ try {
14793
+ if (this.config.indexing.autoIndex && !this.autoIndexStarted) {
14794
+ this.autoIndexStarted = true;
14795
+ this.hooks.startAutoIndex("startup");
14796
+ }
14797
+ if (!this.watcher && this.hooks.watcherFactory) {
14798
+ try {
14799
+ const watcher = this.hooks.watcherFactory();
14800
+ this.watcher = watcher;
14801
+ this.leaderReady = watcher.whenReady?.().catch((error) => {
14802
+ console.error("[codebase-index] Failed while waiting for background file watcher startup:", error);
14803
+ }) ?? Promise.resolve();
14804
+ } catch (error) {
14805
+ console.error("[codebase-index] Failed to start background file watcher:", error);
14806
+ this.leaderReady = Promise.resolve();
14807
+ }
14808
+ }
14809
+ } finally {
14810
+ this.startingLeaderWork = false;
14811
+ }
14812
+ }
14813
+ async stopLeaderWork(stopAutoIndex) {
14814
+ const watcher = this.watcher;
14815
+ let watcherError;
14816
+ if (watcher) {
14817
+ try {
14818
+ await watcher.stop();
14819
+ if (this.watcher === watcher) this.watcher = null;
14820
+ } catch (error) {
14821
+ watcherError = error;
14822
+ }
14823
+ }
14824
+ let autoIndexError;
14825
+ let autoIndexStop = {
14826
+ completed: true,
14827
+ completion: Promise.resolve()
14828
+ };
14829
+ if (stopAutoIndex) {
14830
+ try {
14831
+ autoIndexStop = await this.hooks.stopAutoIndex();
14832
+ this.autoIndexStarted = false;
14833
+ } catch (error) {
14834
+ autoIndexError = error;
14835
+ }
14836
+ }
14837
+ if (watcherError !== void 0 || autoIndexError !== void 0) {
14838
+ throw new BackgroundWorkerStopError(watcherError, autoIndexError);
14839
+ }
14840
+ return autoIndexStop;
14841
+ }
14842
+ releaseLeaseWhenAutoIndexStops(lease, completion) {
14843
+ void completion.then(
14844
+ () => {
14845
+ void this.enqueue(async () => {
14846
+ if (this.lease !== lease || !this.stopping) return;
14847
+ this.leaderWorkStopped = true;
14848
+ this.releaseStoppedLease(lease);
14849
+ }).catch((error) => {
14850
+ console.error("[codebase-index] Failed to release background worker lease after automatic indexing stopped:", error);
14851
+ this.scheduleTeardownRetry();
14852
+ });
14853
+ },
14854
+ (error) => {
14855
+ console.error("[codebase-index] Failed while waiting for automatic indexing to stop:", error);
14856
+ this.scheduleTeardownRetry();
14857
+ }
14858
+ );
14859
+ }
14860
+ releaseStoppedLease(lease) {
14861
+ if (this.lease !== lease) {
14862
+ this.finishStoppedLease();
14863
+ return;
14864
+ }
14865
+ releaseLease(lease);
14866
+ this.lease = null;
14867
+ this.finishStoppedLease();
14868
+ }
14869
+ finishStoppedLease() {
14870
+ this.leaderWorkStopped = false;
14871
+ this.stopAutoIndexOnTeardown = true;
14872
+ this.stopping = false;
14873
+ this.clearTimers();
14874
+ this.restartAfterTeardown();
14875
+ if (!this.stopped || this.stopping) return;
14876
+ const projectKey = projectLookupKey(this.projectRoot, this.host);
14877
+ const key = controllerKey(this.identity, this.host);
14878
+ if (workers.get(key) === this) workers.delete(key);
14879
+ if (workerKeysByProject.get(projectKey) === key) workerKeysByProject.delete(projectKey);
14880
+ }
14881
+ startHeartbeat() {
14882
+ if (this.heartbeatTimer) return;
14883
+ const heartbeat = () => {
14884
+ void this.heartbeat();
14885
+ };
14886
+ this.heartbeatTimer = setInterval(heartbeat, HEARTBEAT_INTERVAL_MS);
14887
+ this.heartbeatTimer.unref?.();
14888
+ }
14889
+ async heartbeat() {
14890
+ const lease = this.lease;
14891
+ if (!lease || this.losingLeadership || this.stopped && !this.stopping) return;
14892
+ if (hasActiveReclaimMarker(lease.leasePath, lease.owner)) {
14893
+ await this.loseLeadership();
14894
+ return;
14895
+ }
14896
+ const currentOwner = readOwner(lease.leasePath);
14897
+ if (!currentOwner || !sameOwner2(currentOwner, lease.owner)) {
14898
+ await this.loseLeadership();
14899
+ return;
14900
+ }
14901
+ try {
14902
+ const nextOwner = { ...lease.owner, heartbeatAt: (/* @__PURE__ */ new Date()).toISOString() };
14903
+ if (!writeHeartbeat(lease.leasePath, nextOwner)) {
14904
+ await this.loseLeadership();
14905
+ return;
14906
+ }
14907
+ lease.owner = nextOwner;
14908
+ const refreshRequest = !this.stopping ? consumeRefreshRequest(lease.leasePath) : null;
14909
+ if (refreshRequest) {
14910
+ this.hooks.startAutoIndex("retrieval", refreshRequest.allowDisabledAutoIndex);
14911
+ }
14912
+ } catch (error) {
14913
+ const ownerAfterError = readOwner(lease.leasePath);
14914
+ if (hasActiveReclaimMarker(lease.leasePath, lease.owner) || !ownerAfterError || !sameOwner2(ownerAfterError, lease.owner)) {
14915
+ await this.loseLeadership();
14916
+ return;
14917
+ }
14918
+ console.error("[codebase-index] Failed to renew background worker lease:", error);
14919
+ }
14920
+ }
14921
+ async loseLeadership() {
14922
+ if (this.losingLeadership) return;
14923
+ this.losingLeadership = true;
14924
+ this.clearHeartbeat();
14925
+ await this.enqueue(async () => this.stopAfterLeadershipLoss());
14926
+ }
14927
+ async stopAfterLeadershipLoss() {
14928
+ const lease = this.lease;
14929
+ if (!lease) {
14930
+ this.losingLeadership = false;
14931
+ return;
14932
+ }
14933
+ try {
14934
+ const stopped = await this.stopLeaderWork(true);
14935
+ this.lease = null;
14936
+ this.losingLeadership = false;
14937
+ if (stopped.completed) {
14938
+ this.scheduleRetry();
14939
+ } else {
14940
+ void stopped.completion.then(() => this.scheduleRetry());
14941
+ }
14942
+ } catch (error) {
14943
+ console.error("[codebase-index] Failed to stop background work after losing its lease:", error);
14944
+ this.scheduleLostLeadershipTeardownRetry();
14945
+ }
14946
+ }
14947
+ scheduleRetry() {
14948
+ if (this.stopped || !this.canRun() || this.retryTimer) return;
14949
+ this.retryTimer = setTimeout(() => {
14950
+ this.retryTimer = null;
14951
+ this.start();
14952
+ }, RETRY_DELAY_MS);
14953
+ this.retryTimer.unref?.();
14954
+ }
14955
+ scheduleTeardownRetry() {
14956
+ if (!this.stopping || this.teardownRetryTimer) return;
14957
+ this.teardownRetryTimer = setTimeout(() => {
14958
+ this.teardownRetryTimer = null;
14959
+ void this.stop(this.stopAutoIndexOnTeardown).catch((error) => {
14960
+ console.error("[codebase-index] Failed to retry background worker teardown:", error);
14961
+ });
14962
+ }, RETRY_DELAY_MS);
14963
+ this.teardownRetryTimer.unref?.();
14964
+ }
14965
+ restartAfterTeardown() {
14966
+ if (!this.restartAfterStop || !this.canRun() || this.losingLeadership) return;
14967
+ this.restartAfterStop = false;
14968
+ this.stopped = false;
14969
+ this.start();
14970
+ }
14971
+ scheduleLostLeadershipTeardownRetry() {
14972
+ if (this.stopped || !this.losingLeadership || this.retryTimer) return;
14973
+ this.retryTimer = setTimeout(() => {
14974
+ this.retryTimer = null;
14975
+ void this.enqueue(async () => this.stopAfterLeadershipLoss());
14976
+ }, RETRY_DELAY_MS);
14977
+ this.retryTimer.unref?.();
14978
+ }
14979
+ clearHeartbeat() {
14980
+ if (!this.heartbeatTimer) return;
14981
+ clearInterval(this.heartbeatTimer);
14982
+ this.heartbeatTimer = null;
14983
+ }
14984
+ clearTimers() {
14985
+ this.clearHeartbeat();
14986
+ this.clearRetryTimer();
14987
+ if (this.teardownRetryTimer) {
14988
+ clearTimeout(this.teardownRetryTimer);
14989
+ this.teardownRetryTimer = null;
14990
+ }
14991
+ }
14992
+ clearRetryTimer() {
14993
+ if (!this.retryTimer) return;
14994
+ clearTimeout(this.retryTimer);
14995
+ this.retryTimer = null;
14996
+ }
14997
+ reportAcquireError(error) {
14998
+ const message = error instanceof Error ? error.message : String(error);
14999
+ if (this.reportedError === message) return;
15000
+ this.reportedError = message;
15001
+ console.error("[codebase-index] Failed to acquire background worker lease:", error);
15002
+ }
15003
+ };
15004
+ function configureBackgroundWorker(projectRoot, host, config, hooks, options = {}) {
15005
+ const projectKey = projectLookupKey(projectRoot, host);
15006
+ const identity = resolveIdentity(projectRoot, config, host);
15007
+ const key = controllerKey(identity, host);
15008
+ const previousKey = workerKeysByProject.get(projectKey);
15009
+ if (previousKey && previousKey !== key) {
15010
+ const previous = workers.get(previousKey);
15011
+ const previousBarrier = workerReplacementBarriers.get(projectKey) ?? Promise.resolve();
15012
+ const stopPrevious = previous?.stop(options.stopPreviousAutoIndex ?? true) ?? Promise.resolve();
15013
+ const activation = Promise.all([previousBarrier, stopPrevious]).then(() => void 0);
15014
+ workerReplacementBarriers.set(projectKey, activation);
15015
+ workers.delete(previousKey);
15016
+ const worker2 = new BackgroundWorkerController(projectRoot, host, config, hooks, identity);
15017
+ worker2.startAfter(activation);
15018
+ workers.set(key, worker2);
15019
+ workerKeysByProject.set(projectKey, key);
15020
+ return;
15021
+ }
15022
+ let worker = workers.get(key);
15023
+ if (!worker) {
15024
+ worker = new BackgroundWorkerController(projectRoot, host, config, hooks, identity);
15025
+ workers.set(key, worker);
15026
+ } else {
15027
+ worker.update(config, hooks, options);
15028
+ }
15029
+ workerKeysByProject.set(projectKey, key);
15030
+ worker.start();
15031
+ }
15032
+ function attachBackgroundWorkerWatcher(projectRoot, host, watcherFactory, watcherFactoryForConfig) {
15033
+ const key = workerKeysByProject.get(projectLookupKey(projectRoot, host));
15034
+ workers.get(key ?? "")?.attachWatcher(watcherFactory, watcherFactoryForConfig);
15035
+ }
15036
+ function updateBackgroundWorkerConfig(projectRoot, host, config) {
15037
+ const projectKey = projectLookupKey(projectRoot, host);
15038
+ const key = workerKeysByProject.get(projectKey);
15039
+ const worker = key ? workers.get(key) : void 0;
15040
+ if (!worker) return;
15041
+ configureBackgroundWorker(projectRoot, host, config, worker.getHooksForConfig(config), {
15042
+ stopPreviousAutoIndex: false,
15043
+ restartAutoIndex: true
15044
+ });
15045
+ }
15046
+ function requestBackgroundWorker(projectRoot, host) {
15047
+ const key = workerKeysByProject.get(projectLookupKey(projectRoot, host));
15048
+ workers.get(key ?? "")?.start();
15049
+ }
15050
+ function waitForBackgroundWorkerStart(projectRoot, host) {
15051
+ const key = workerKeysByProject.get(projectLookupKey(projectRoot, host));
15052
+ return workers.get(key ?? "")?.waitForStart() ?? Promise.resolve();
15053
+ }
15054
+ function requestBackgroundWorkerRefresh(projectRoot, host, allowDisabledAutoIndex = false) {
15055
+ const key = workerKeysByProject.get(projectLookupKey(projectRoot, host));
15056
+ workers.get(key ?? "")?.requestRefresh(allowDisabledAutoIndex);
15057
+ }
15058
+ function isBackgroundWorkerManaged(projectRoot, host) {
15059
+ const key = workerKeysByProject.get(projectLookupKey(projectRoot, host));
15060
+ return key !== void 0 && workers.has(key);
15061
+ }
15062
+ function isBackgroundWorkerLeader(projectRoot, host) {
15063
+ const key = workerKeysByProject.get(projectLookupKey(projectRoot, host));
15064
+ return key !== void 0 && workers.get(key)?.isLeader() === true;
15065
+ }
15066
+ function isBackgroundWorkerStopping(projectRoot, host) {
15067
+ const key = workerKeysByProject.get(projectLookupKey(projectRoot, host));
15068
+ return key !== void 0 && workers.get(key)?.isStopping() === true;
15069
+ }
15070
+ async function stopBackgroundWorker(projectRoot, host) {
15071
+ const projectKey = projectLookupKey(projectRoot, host);
15072
+ const key = workerKeysByProject.get(projectKey);
15073
+ const worker = key ? workers.get(key) : void 0;
15074
+ if (!worker) return;
15075
+ await worker.stop();
15076
+ }
15077
+
15078
+ // src/utils/power-source.ts
15079
+ var childProcess = __toESM(require("child_process"), 1);
15080
+ var POWER_SOURCE_RECHECK_DELAY_MS = 6e4;
15081
+ var PMSET_TIMEOUT_MS = 5e3;
15082
+ function getErrorMessage4(error) {
15083
+ return error instanceof Error ? error.message : String(error);
15084
+ }
15085
+ function runCommand(file, args, options) {
15086
+ return new Promise((resolve21, reject) => {
15087
+ childProcess.execFile(
15088
+ file,
15089
+ args,
15090
+ { encoding: "utf8", timeout: options.timeoutMs },
15091
+ (error, stdout) => {
15092
+ if (error) {
15093
+ reject(error);
15094
+ return;
15095
+ }
15096
+ resolve21(stdout);
15097
+ }
15098
+ );
15099
+ });
15100
+ }
15101
+ function parseMacOsPowerSource(output) {
15102
+ const match = output.match(/Now drawing from '([^']+)'/i);
15103
+ if (!match) {
15104
+ return "unknown";
15105
+ }
15106
+ const source = match[1].toLowerCase();
15107
+ if (source === "battery power") {
15108
+ return "battery";
15109
+ }
15110
+ if (source === "ac power") {
15111
+ return "ac";
15112
+ }
15113
+ return "unknown";
15114
+ }
15115
+ async function readMacOsPowerSource(commandRunner = runCommand) {
15116
+ const output = await commandRunner(
15117
+ "/usr/bin/pmset",
15118
+ ["-g", "batt"],
15119
+ { timeoutMs: PMSET_TIMEOUT_MS }
15120
+ );
15121
+ return parseMacOsPowerSource(output);
15122
+ }
15123
+ var MacOsBackgroundIndexingPolicy = class {
15124
+ constructor(readPowerSource, recheckDelayMs) {
15125
+ this.readPowerSource = readPowerSource;
15126
+ this.recheckDelayMs = recheckDelayMs;
15127
+ }
15128
+ readPowerSource;
15129
+ recheckDelayMs;
15130
+ lastPaused = null;
15131
+ reportedFailure = false;
15132
+ isPaused() {
15133
+ return this.checkPowerSource();
15134
+ }
15135
+ async checkPowerSource() {
15136
+ try {
15137
+ const source = await this.readPowerSource();
15138
+ if (source === "unknown") {
15139
+ throw new Error("pmset returned an unrecognized power source");
15140
+ }
15141
+ this.reportedFailure = false;
15142
+ const paused = source === "battery";
15143
+ if (paused && this.lastPaused !== true) {
15144
+ console.warn("[codebase-index] Background indexing paused while macOS is using battery power.");
15145
+ } else if (!paused && this.lastPaused === true) {
15146
+ console.warn("[codebase-index] AC power detected; resuming pending background indexing.");
15147
+ }
15148
+ this.lastPaused = paused;
15149
+ return paused;
15150
+ } catch (error) {
15151
+ if (!this.reportedFailure) {
15152
+ console.error(
15153
+ `[codebase-index] Failed to determine the macOS power source; background indexing will continue: ${getErrorMessage4(error)}`
15154
+ );
15155
+ this.reportedFailure = true;
15156
+ }
15157
+ this.lastPaused = false;
15158
+ return false;
15159
+ }
15160
+ }
15161
+ };
15162
+ function createBackgroundIndexingPolicy(pauseOnBattery, options = {}) {
15163
+ const platform2 = options.platform ?? process.platform;
15164
+ if (!pauseOnBattery || platform2 !== "darwin") {
15165
+ return null;
15166
+ }
15167
+ return new MacOsBackgroundIndexingPolicy(
15168
+ options.readPowerSource ?? readMacOsPowerSource,
15169
+ options.recheckDelayMs ?? POWER_SOURCE_RECHECK_DELAY_MS
15170
+ );
15171
+ }
15172
+
15173
+ // src/utils/auto-index.ts
15174
+ var MAX_RETRY_DELAY_MS = 1e4;
15175
+ var SHUTDOWN_WAIT_MS = 2e3;
15176
+ var coordinators = /* @__PURE__ */ new Map();
15177
+ var coordinatorKeysByProject = /* @__PURE__ */ new Map();
15178
+ var coordinatorReplacementBarriers = /* @__PURE__ */ new Map();
15179
+ var AutoIndexCancelledError = class extends Error {
15180
+ constructor() {
15181
+ super("Auto-index coordination was cancelled");
15182
+ this.name = "AutoIndexCancelledError";
15183
+ }
15184
+ };
15185
+ function now() {
15186
+ return (/* @__PURE__ */ new Date()).toISOString();
15187
+ }
15188
+ function canonicalizePath2(targetPath) {
15189
+ const resolved = path18.resolve(targetPath);
15190
+ if ((0, import_fs11.existsSync)(resolved)) {
15191
+ try {
15192
+ return import_fs11.realpathSync.native(resolved);
15193
+ } catch {
15194
+ return resolved;
15195
+ }
15196
+ }
15197
+ const parent = path18.dirname(resolved);
15198
+ if (parent === resolved) return resolved;
15199
+ return path18.join(canonicalizePath2(parent), path18.basename(resolved));
15200
+ }
15201
+ function isHomeDirectory(projectRoot) {
15202
+ return canonicalizePath2(projectRoot) === canonicalizePath2(os7.homedir());
15203
+ }
15204
+ function projectLookupKey2(projectRoot, host) {
15205
+ return `${host}::${canonicalizePath2(projectRoot)}`;
15206
+ }
15207
+ function coordinatorKey(projectRoot, config, host) {
15208
+ const canonicalProjectRoot = canonicalizePath2(projectRoot);
15209
+ const indexPath = resolveProjectIndexPath(projectRoot, config.scope, host);
15210
+ return `${canonicalizePath2(indexPath)}::${canonicalProjectRoot}`;
15211
+ }
15212
+ function getProjectSafety(projectRoot, config) {
15213
+ if (isHomeDirectory(projectRoot)) {
15214
+ return { safeToRun: false, blockedReason: "home-directory" };
15215
+ }
15216
+ if (config.indexing.requireProjectMarker && !hasProjectMarker(projectRoot)) {
15217
+ return { safeToRun: false, blockedReason: "project-marker-missing" };
15218
+ }
15219
+ return { safeToRun: true };
15220
+ }
15221
+ function calculatePercentage2(progress) {
15222
+ if (progress.phase === "scanning") return 0;
15223
+ if (progress.phase === "complete") return 100;
15224
+ if (progress.phase === "parsing") {
15225
+ return progress.totalFiles === 0 ? 5 : Math.round(5 + progress.filesProcessed / progress.totalFiles * 15);
15226
+ }
15227
+ if (progress.phase === "embedding") {
15228
+ return progress.totalChunks === 0 ? 20 : Math.round(20 + progress.chunksProcessed / progress.totalChunks * 70);
15229
+ }
15230
+ if (progress.phase === "storing") return 95;
15231
+ return 0;
15232
+ }
15233
+ function safeFailureMessage(error) {
15234
+ if (isTransientIndexLockContention(error)) {
15235
+ return "Another index process remained busy after the configured retries.";
15236
+ }
15237
+ return "Automatic indexing failed. Check the embedding provider configuration, then run index_codebase.";
15238
+ }
15239
+ function cancellableDelay(delayMs, signal) {
15240
+ if (signal.aborted) return Promise.reject(new AutoIndexCancelledError());
15241
+ return new Promise((resolve21, reject) => {
15242
+ const timer = setTimeout(() => {
14240
15243
  signal.removeEventListener("abort", onAbort);
14241
- resolve20();
15244
+ resolve21();
14242
15245
  }, delayMs);
14243
15246
  timer.unref?.();
14244
15247
  const onAbort = () => {
@@ -14250,18 +15253,44 @@ function cancellableDelay(delayMs, signal) {
14250
15253
  }
14251
15254
  function withTimeout(promise, timeoutMs) {
14252
15255
  if (timeoutMs <= 0) return Promise.resolve(void 0);
14253
- return new Promise((resolve20) => {
14254
- const timer = setTimeout(() => resolve20(void 0), timeoutMs);
15256
+ return new Promise((resolve21) => {
15257
+ const timer = setTimeout(() => resolve21(void 0), timeoutMs);
14255
15258
  timer.unref?.();
14256
15259
  void promise.then((value) => {
14257
15260
  clearTimeout(timer);
14258
- resolve20(value);
15261
+ resolve21(value);
14259
15262
  }, () => {
14260
15263
  clearTimeout(timer);
14261
- resolve20(void 0);
15264
+ resolve21(void 0);
14262
15265
  });
14263
15266
  });
14264
15267
  }
15268
+ function settlesWithin(promise, timeoutMs) {
15269
+ if (timeoutMs <= 0) return Promise.resolve(false);
15270
+ return new Promise((resolve21) => {
15271
+ let settled = false;
15272
+ const timer = setTimeout(() => {
15273
+ if (settled) return;
15274
+ settled = true;
15275
+ resolve21(false);
15276
+ }, timeoutMs);
15277
+ timer.unref?.();
15278
+ void promise.then(
15279
+ () => {
15280
+ if (settled) return;
15281
+ settled = true;
15282
+ clearTimeout(timer);
15283
+ resolve21(true);
15284
+ },
15285
+ () => {
15286
+ if (settled) return;
15287
+ settled = true;
15288
+ clearTimeout(timer);
15289
+ resolve21(true);
15290
+ }
15291
+ );
15292
+ });
15293
+ }
14265
15294
  function requestPriority(request) {
14266
15295
  if (request.force) return 4;
14267
15296
  if (request.source === "manual") return 3;
@@ -14272,6 +15301,7 @@ function mergeRequests(current, next) {
14272
15301
  if (!current) return next;
14273
15302
  const preferred = requestPriority(next) > requestPriority(current) ? next : current;
14274
15303
  return {
15304
+ allowDisabledAutoIndex: current.allowDisabledAutoIndex || next.allowDisabledAutoIndex,
14275
15305
  checkFreshness: current.checkFreshness && next.checkFreshness,
14276
15306
  force: current.force || next.force,
14277
15307
  onProgress: next.onProgress ?? current.onProgress,
@@ -14333,11 +15363,11 @@ var AutoIndexCoordinator = class {
14333
15363
  progress: this.status.progress ? { ...this.status.progress } : void 0
14334
15364
  };
14335
15365
  }
14336
- start(source) {
15366
+ start(source, allowDisabledAutoIndex = false) {
14337
15367
  this.refreshSafety();
14338
- if (!this.registration.config.indexing.autoIndex || !this.registration.safeToRun) return null;
15368
+ if (!this.registration.config.indexing.autoIndex && !allowDisabledAutoIndex || !this.registration.safeToRun) return null;
14339
15369
  if (this.status.state === "failed") return this.inFlight;
14340
- return this.request({ checkFreshness: true, force: false, source });
15370
+ return this.request({ allowDisabledAutoIndex, checkFreshness: true, force: false, source });
14341
15371
  }
14342
15372
  request(request) {
14343
15373
  if (this.stopped) {
@@ -14412,13 +15442,15 @@ var AutoIndexCoordinator = class {
14412
15442
  retryAttempt: void 0
14413
15443
  });
14414
15444
  const inFlight = this.inFlight;
14415
- if (inFlight) {
14416
- if (waitForCompletion) {
14417
- await inFlight;
14418
- } else {
14419
- await withTimeout(inFlight, SHUTDOWN_WAIT_MS);
14420
- }
15445
+ const completion = inFlight ? inFlight.then(() => void 0, () => void 0) : Promise.resolve();
15446
+ if (!inFlight) {
15447
+ return { completed: true, completion };
15448
+ }
15449
+ if (waitForCompletion) {
15450
+ await completion;
15451
+ return { completed: true, completion };
14421
15452
  }
15453
+ return { completed: await settlesWithin(completion, SHUTDOWN_WAIT_MS), completion };
14422
15454
  }
14423
15455
  startRequest(request) {
14424
15456
  if (this.stopped || !this.canRun(request)) {
@@ -14607,7 +15639,7 @@ var AutoIndexCoordinator = class {
14607
15639
  if (request.source === "manual" || request.source === "watcher") {
14608
15640
  return true;
14609
15641
  }
14610
- return this.registration.safeToRun && this.registration.config.indexing.autoIndex;
15642
+ return this.registration.safeToRun && (this.registration.config.indexing.autoIndex || request.allowDisabledAutoIndex === true);
14611
15643
  }
14612
15644
  shouldDeferForBattery(request) {
14613
15645
  return this.registration.backgroundIndexingPolicy !== null && (request.source === "startup" || request.source === "watcher");
@@ -14640,17 +15672,17 @@ var AutoIndexCoordinator = class {
14640
15672
  }
14641
15673
  }
14642
15674
  waitForBatteryRetry(delayMs) {
14643
- return new Promise((resolve20) => {
15675
+ return new Promise((resolve21) => {
14644
15676
  const timer = setTimeout(() => {
14645
15677
  if (this.batteryRetryTimer === timer) {
14646
15678
  this.batteryRetryTimer = null;
14647
15679
  this.resolveBatteryRetry = null;
14648
15680
  }
14649
- resolve20();
15681
+ resolve21();
14650
15682
  }, delayMs);
14651
15683
  timer.unref?.();
14652
15684
  this.batteryRetryTimer = timer;
14653
- this.resolveBatteryRetry = resolve20;
15685
+ this.resolveBatteryRetry = resolve21;
14654
15686
  });
14655
15687
  }
14656
15688
  cancelBatteryRetry() {
@@ -14658,9 +15690,9 @@ var AutoIndexCoordinator = class {
14658
15690
  clearTimeout(this.batteryRetryTimer);
14659
15691
  this.batteryRetryTimer = null;
14660
15692
  }
14661
- const resolve20 = this.resolveBatteryRetry;
15693
+ const resolve21 = this.resolveBatteryRetry;
14662
15694
  this.resolveBatteryRetry = null;
14663
- resolve20?.();
15695
+ resolve21?.();
14664
15696
  }
14665
15697
  finishBatteryCheck(batteryCheck) {
14666
15698
  if (this.batteryCheck !== batteryCheck) return;
@@ -14673,12 +15705,25 @@ var AutoIndexCoordinator = class {
14673
15705
  }
14674
15706
  };
14675
15707
  function getCoordinator(projectRoot, host) {
14676
- const key = coordinatorKeysByProject.get(projectLookupKey(projectRoot, host));
15708
+ const key = coordinatorKeysByProject.get(projectLookupKey2(projectRoot, host));
14677
15709
  return key ? coordinators.get(key) ?? null : null;
14678
15710
  }
14679
- function configureAutoIndex(projectRoot, host, config, getIndexer) {
14680
- const projectKey = projectLookupKey(projectRoot, host);
15711
+ function synchronizeBackgroundWorker(projectRoot, host, config, safeToRun) {
15712
+ if (safeToRun) {
15713
+ updateBackgroundWorkerConfig(projectRoot, host, config);
15714
+ return;
15715
+ }
15716
+ void stopBackgroundWorker(projectRoot, host).catch((error) => {
15717
+ console.error("[codebase-index] Failed to stop background worker after project safety changed:", error);
15718
+ });
15719
+ }
15720
+ function configureAutoIndex(projectRoot, host, config, getIndexer, options = {}) {
15721
+ const projectKey = projectLookupKey2(projectRoot, host);
14681
15722
  const safety = getProjectSafety(projectRoot, config);
15723
+ const synchronizeWorker = options.synchronizeBackgroundWorker ?? true;
15724
+ if (options.preserveManagedWorker === true && isBackgroundWorkerManaged(projectRoot, host)) {
15725
+ return;
15726
+ }
14682
15727
  const registration = {
14683
15728
  backgroundIndexingPolicy: createBackgroundIndexingPolicy(
14684
15729
  config.indexing.pauseBackgroundIndexingOnBattery
@@ -14696,6 +15741,9 @@ function configureAutoIndex(projectRoot, host, config, getIndexer) {
14696
15741
  const stopPrevious = previousCoordinator?.stop(true) ?? Promise.resolve();
14697
15742
  const activation = Promise.all([previousBarrier, stopPrevious]).then(() => void 0);
14698
15743
  coordinatorReplacementBarriers.set(projectKey, activation);
15744
+ if (synchronizeWorker) {
15745
+ synchronizeBackgroundWorker(projectRoot, host, config, safety.safeToRun);
15746
+ }
14699
15747
  coordinators.delete(previousKey);
14700
15748
  const coordinator2 = new AutoIndexCoordinator(registration);
14701
15749
  coordinator2.activateAfter(activation);
@@ -14711,11 +15759,17 @@ function configureAutoIndex(projectRoot, host, config, getIndexer) {
14711
15759
  coordinator.update(registration);
14712
15760
  }
14713
15761
  coordinatorKeysByProject.set(projectKey, key);
15762
+ if (synchronizeWorker) {
15763
+ synchronizeBackgroundWorker(projectRoot, host, config, safety.safeToRun);
15764
+ }
14714
15765
  }
14715
- function startAutoIndex(projectRoot, host, source = "startup") {
14716
- return getCoordinator(projectRoot, host)?.start(source) ?? null;
15766
+ function startAutoIndexForBackgroundWorker(projectRoot, host, source = "startup", allowDisabledAutoIndex = false) {
15767
+ return getCoordinator(projectRoot, host)?.start(source, allowDisabledAutoIndex) ?? null;
14717
15768
  }
14718
15769
  function requestBackgroundIndex(projectRoot, host) {
15770
+ if (isBackgroundWorkerManaged(projectRoot, host) && !isBackgroundWorkerLeader(projectRoot, host)) {
15771
+ return null;
15772
+ }
14719
15773
  return getCoordinator(projectRoot, host)?.request({
14720
15774
  checkFreshness: false,
14721
15775
  force: false,
@@ -14755,15 +15809,23 @@ async function waitForAutoIndexForRetrieval(projectRoot, host) {
14755
15809
  };
14756
15810
  }
14757
15811
  try {
14758
- if (await hasReadableCurrentIndex(coordinator)) return { ready: true };
15812
+ const readiness = await getSearchReadiness(coordinator);
15813
+ if (readiness.searchable) {
15814
+ return { ready: true };
15815
+ }
15816
+ if (readiness.blocked) return unavailableSnapshotResult(readiness.reason);
14759
15817
  } catch {
14760
15818
  }
14761
- const job = coordinator.start("retrieval") ?? coordinator.currentJob();
15819
+ const job = startRetrievalRefresh(projectRoot, host, coordinator);
14762
15820
  if (job) {
14763
15821
  await withTimeout(job, coordinator.getWaitMs());
15822
+ } else if (isBackgroundWorkerManaged(projectRoot, host)) {
15823
+ await waitForPublishedSnapshot(coordinator, coordinator.getWaitMs());
14764
15824
  }
14765
15825
  try {
14766
- if (await hasReadableCurrentIndex(coordinator)) return { ready: true };
15826
+ const readiness = await getSearchReadiness(coordinator);
15827
+ if (readiness.searchable) return { ready: true };
15828
+ if (readiness.blocked) return unavailableSnapshotResult(readiness.reason);
14767
15829
  } catch {
14768
15830
  }
14769
15831
  const status = coordinator.snapshot();
@@ -14784,31 +15846,62 @@ async function waitForAutoIndexForRetrieval(projectRoot, host) {
14784
15846
  text: `Automatic indexing is ${status.state}. Retry shortly or call index_status for progress. You can also run index_codebase explicitly.`
14785
15847
  };
14786
15848
  }
14787
- async function stopAutoIndex(projectRoot, host) {
14788
- await getCoordinator(projectRoot, host)?.stop();
15849
+ async function stopAutoIndexForBackgroundWorker(projectRoot, host, waitForCompletion = false) {
15850
+ const coordinator = getCoordinator(projectRoot, host);
15851
+ if (!coordinator) {
15852
+ return { completed: true, completion: Promise.resolve() };
15853
+ }
15854
+ return coordinator.stop(waitForCompletion);
14789
15855
  }
14790
- async function hasReadableCurrentIndex(coordinator) {
15856
+ async function getSearchReadiness(coordinator) {
14791
15857
  const indexer = coordinator.getIndexer();
14792
15858
  if (indexer.getIndexFreshness) {
14793
15859
  const freshness = await indexer.getIndexFreshness();
14794
- return freshness.readable && freshness.current;
15860
+ const searchable = freshness.readable && freshness.current && freshness.reason === "current";
15861
+ return {
15862
+ blocked: freshness.reason === "unreadable" || freshness.reason === "incompatible" || freshness.reason === "failed-batches" || freshness.reason === "migration-required",
15863
+ reason: freshness.reason,
15864
+ searchable
15865
+ };
15866
+ }
15867
+ const indexed = (await indexer.getStatus()).indexed;
15868
+ return { blocked: false, searchable: indexed };
15869
+ }
15870
+ function unavailableSnapshotResult(reason) {
15871
+ const detail = reason === "incompatible" ? "The existing index is incompatible with the configured embedding provider." : reason === "migration-required" ? "The existing index requires a storage migration." : reason === "failed-batches" ? "The existing index has failed embedding batches." : "The existing index is unreadable.";
15872
+ return {
15873
+ ready: false,
15874
+ text: `${detail} Run index_codebase before retrying retrieval.`
15875
+ };
15876
+ }
15877
+ function startRetrievalRefresh(projectRoot, host, coordinator) {
15878
+ if (isBackgroundWorkerManaged(projectRoot, host)) {
15879
+ requestBackgroundWorkerRefresh(projectRoot, host, true);
15880
+ return isBackgroundWorkerLeader(projectRoot, host) ? coordinator.currentJob() : null;
15881
+ }
15882
+ return coordinator.start("retrieval") ?? coordinator.currentJob();
15883
+ }
15884
+ async function waitForPublishedSnapshot(coordinator, waitMs) {
15885
+ const deadline = Date.now() + waitMs;
15886
+ while (Date.now() < deadline) {
15887
+ if ((await getSearchReadiness(coordinator)).searchable) return;
15888
+ await new Promise((resolve21) => setTimeout(resolve21, Math.min(250, deadline - Date.now())));
14795
15889
  }
14796
- return (await indexer.getStatus()).indexed;
14797
15890
  }
14798
15891
 
14799
15892
  // src/tools/config-state.ts
14800
15893
  var import_fs13 = require("fs");
14801
- var path20 = __toESM(require("path"), 1);
15894
+ var path21 = __toESM(require("path"), 1);
14802
15895
 
14803
15896
  // src/config/merger.ts
14804
15897
  var import_fs12 = require("fs");
14805
- var path19 = __toESM(require("path"), 1);
15898
+ var path20 = __toESM(require("path"), 1);
14806
15899
 
14807
15900
  // src/config/rebase.ts
14808
- var path18 = __toESM(require("path"), 1);
15901
+ var path19 = __toESM(require("path"), 1);
14809
15902
  function isWithinRoot(rootDir, targetPath) {
14810
- const relativePath = path18.relative(rootDir, targetPath);
14811
- return relativePath === "" || !relativePath.startsWith("..") && !path18.isAbsolute(relativePath);
15903
+ const relativePath = path19.relative(rootDir, targetPath);
15904
+ return relativePath === "" || !relativePath.startsWith("..") && !path19.isAbsolute(relativePath);
14812
15905
  }
14813
15906
  function rebasePathEntries(values, fromDir, toDir) {
14814
15907
  if (!Array.isArray(values)) {
@@ -14816,10 +15909,10 @@ function rebasePathEntries(values, fromDir, toDir) {
14816
15909
  }
14817
15910
  return values.filter((value) => typeof value === "string").map((value) => {
14818
15911
  const trimmed = value.trim();
14819
- if (!trimmed || path18.isAbsolute(trimmed)) {
15912
+ if (!trimmed || path19.isAbsolute(trimmed)) {
14820
15913
  return trimmed;
14821
15914
  }
14822
- return normalizePathSeparators(path18.normalize(path18.relative(toDir, path18.resolve(fromDir, trimmed))));
15915
+ return normalizePathSeparators(path19.normalize(path19.relative(toDir, path19.resolve(fromDir, trimmed))));
14823
15916
  }).filter(Boolean);
14824
15917
  }
14825
15918
  function resolveInheritedKnowledgeBaseEntries(values, sourceRoot, targetRoot) {
@@ -14831,17 +15924,17 @@ function resolveInheritedKnowledgeBaseEntries(values, sourceRoot, targetRoot) {
14831
15924
  if (!trimmed) {
14832
15925
  return trimmed;
14833
15926
  }
14834
- if (path18.isAbsolute(trimmed)) {
15927
+ if (path19.isAbsolute(trimmed)) {
14835
15928
  if (isWithinRoot(sourceRoot, trimmed)) {
14836
- return normalizePathSeparators(path18.normalize(path18.relative(sourceRoot, trimmed) || "."));
15929
+ return normalizePathSeparators(path19.normalize(path19.relative(sourceRoot, trimmed) || "."));
14837
15930
  }
14838
- return path18.normalize(trimmed);
15931
+ return path19.normalize(trimmed);
14839
15932
  }
14840
- const resolvedFromSource = path18.resolve(sourceRoot, trimmed);
15933
+ const resolvedFromSource = path19.resolve(sourceRoot, trimmed);
14841
15934
  if (isWithinRoot(sourceRoot, resolvedFromSource)) {
14842
- return normalizePathSeparators(path18.normalize(trimmed));
15935
+ return normalizePathSeparators(path19.normalize(trimmed));
14843
15936
  }
14844
- return normalizePathSeparators(path18.normalize(path18.relative(targetRoot, resolvedFromSource)));
15937
+ return normalizePathSeparators(path19.normalize(path19.relative(targetRoot, resolvedFromSource)));
14845
15938
  }).filter(Boolean);
14846
15939
  }
14847
15940
 
@@ -14879,8 +15972,8 @@ function mergeUniqueStringArray(values) {
14879
15972
  return [...new Set(values.map((value) => String(value).trim()))];
14880
15973
  }
14881
15974
  function normalizeKnowledgeBasePath2(value) {
14882
- let normalized = path19.normalize(String(value).trim());
14883
- const root = path19.parse(normalized).root;
15975
+ let normalized = path20.normalize(String(value).trim());
15976
+ const root = path20.parse(normalized).root;
14884
15977
  while (normalized.length > root.length && /[\\/]$/.test(normalized)) {
14885
15978
  normalized = normalized.slice(0, -1);
14886
15979
  }
@@ -14938,7 +16031,7 @@ function loadProjectConfigLayer(projectRoot, host) {
14938
16031
  return {};
14939
16032
  }
14940
16033
  const normalizedConfig = { ...projectConfig };
14941
- const projectConfigBaseDir = path19.dirname(path19.dirname(projectConfigPath));
16034
+ const projectConfigBaseDir = path20.dirname(path20.dirname(projectConfigPath));
14942
16035
  if (Array.isArray(normalizedConfig.knowledgeBases)) {
14943
16036
  normalizedConfig.knowledgeBases = resolveInheritedKnowledgeBaseEntries(
14944
16037
  normalizedConfig.knowledgeBases,
@@ -15028,8 +16121,8 @@ function loadEditableConfig(projectRoot, host) {
15028
16121
  }
15029
16122
  function saveConfig(projectRoot, config, host) {
15030
16123
  const configPath = getConfigPath(projectRoot, host);
15031
- const configDir = path20.dirname(configPath);
15032
- const configBaseDir = path20.dirname(configDir);
16124
+ const configDir = path21.dirname(configPath);
16125
+ const configBaseDir = path21.dirname(configDir);
15033
16126
  if (!(0, import_fs13.existsSync)(configDir)) {
15034
16127
  (0, import_fs13.mkdirSync)(configDir, { recursive: true });
15035
16128
  }
@@ -15118,15 +16211,24 @@ function getOrCreateIndexer(projectRoot, host) {
15118
16211
  }
15119
16212
  const indexer = new Indexer(projectRoot, config, host);
15120
16213
  indexerCache.set(key, indexer);
15121
- configureAutoIndex(projectRoot, host, config, () => getOrCreateIndexer(projectRoot, host));
16214
+ configureAutoIndex(projectRoot, host, config, () => getOrCreateIndexer(projectRoot, host), {
16215
+ preserveManagedWorker: true,
16216
+ synchronizeBackgroundWorker: false
16217
+ });
15122
16218
  return indexer;
15123
16219
  }
15124
- function initializeTools(projectRoot, config, host) {
16220
+ function initializeTools(projectRoot, config, host, options = {}) {
15125
16221
  defaultProjectRoots.set(host, projectRoot);
15126
16222
  const key = getIndexerCacheKey(projectRoot, host);
16223
+ if (options.preserveManagedWorker === true && isBackgroundWorkerManaged(projectRoot, host) && indexerCache.has(key)) {
16224
+ return;
16225
+ }
15127
16226
  configCache.set(key, config);
15128
16227
  indexerCache.set(key, new Indexer(projectRoot, config, host));
15129
- configureAutoIndex(projectRoot, host, config, () => getOrCreateIndexer(projectRoot, host));
16228
+ configureAutoIndex(projectRoot, host, config, () => getOrCreateIndexer(projectRoot, host), {
16229
+ preserveManagedWorker: options.preserveManagedWorker,
16230
+ synchronizeBackgroundWorker: false
16231
+ });
15130
16232
  }
15131
16233
  function getIndexerForProject(projectRoot, host) {
15132
16234
  const root = getProjectRoot(projectRoot, host);
@@ -15140,7 +16242,9 @@ function refreshIndexerForDirectory(projectRoot, host, config = parseConfig(load
15140
16242
  const key = getIndexerCacheKey(projectRoot, host);
15141
16243
  configCache.set(key, config);
15142
16244
  indexerCache.set(key, new Indexer(projectRoot, config, host));
15143
- configureAutoIndex(projectRoot, host, config, () => getOrCreateIndexer(projectRoot, host));
16245
+ configureAutoIndex(projectRoot, host, config, () => getOrCreateIndexer(projectRoot, host), {
16246
+ synchronizeBackgroundWorker: true
16247
+ });
15144
16248
  return config;
15145
16249
  }
15146
16250
  var AutoIndexRetrievalUnavailableError = class extends Error {
@@ -15167,7 +16271,7 @@ function trimOrUndefined(value) {
15167
16271
  return normalized || void 0;
15168
16272
  }
15169
16273
  function normalizeCallGraphPath(value) {
15170
- let normalized = path21.posix.normalize(value.trim().replaceAll("\\", "/"));
16274
+ let normalized = path22.posix.normalize(value.trim().replaceAll("\\", "/"));
15171
16275
  if (normalized.startsWith("./")) {
15172
16276
  normalized = normalized.slice(2);
15173
16277
  }
@@ -15360,12 +16464,12 @@ async function getCallGraphPath(projectRoot, host, from, to, maxDepth, fromFileP
15360
16464
  if (fromResolution.status !== "resolved" || toResolution.status !== "resolved") {
15361
16465
  return { from: fromResolution, to: toResolution, path: [] };
15362
16466
  }
15363
- const path33 = await indexer.findCallPathBySymbolIds(
16467
+ const path34 = await indexer.findCallPathBySymbolIds(
15364
16468
  fromResolution.symbolId,
15365
16469
  toResolution.symbolId,
15366
16470
  maxDepth
15367
16471
  );
15368
- return { from: fromResolution, to: toResolution, path: path33 };
16472
+ return { from: fromResolution, to: toResolution, path: path34 };
15369
16473
  }
15370
16474
  async function runIndexCodebase(projectRoot, host, args, onProgress) {
15371
16475
  const root = getProjectRoot(projectRoot, host);
@@ -15374,6 +16478,9 @@ async function runIndexCodebase(projectRoot, host, args, onProgress) {
15374
16478
  if (args.estimateOnly) {
15375
16479
  return { kind: "estimate", estimate: await indexer.estimateCost() };
15376
16480
  }
16481
+ if (args.dryRun) {
16482
+ return { kind: "dryrun", dryrun: await indexer.dryRunCost() };
16483
+ }
15377
16484
  const coordinated = runCoordinatedIndex(root, host, args.force ?? false, (progress) => {
15378
16485
  if (onProgress) {
15379
16486
  void onProgress(formatProgressTitle(progress), {
@@ -15560,8 +16667,8 @@ async function getIndexLogs(projectRoot, host, args) {
15560
16667
  function addKnowledgeBase(projectRoot, host, knowledgeBasePath) {
15561
16668
  const root = getProjectRoot(projectRoot, host);
15562
16669
  const inputPath = knowledgeBasePath.trim();
15563
- const normalizedPath3 = path21.resolve(
15564
- path21.isAbsolute(inputPath) ? inputPath : resolveKnowledgeBasePath(inputPath, root)
16670
+ const normalizedPath3 = path22.resolve(
16671
+ path22.isAbsolute(inputPath) ? inputPath : resolveKnowledgeBasePath(inputPath, root)
15565
16672
  );
15566
16673
  if (!(0, import_fs14.existsSync)(normalizedPath3)) {
15567
16674
  return `Error: Directory does not exist: ${normalizedPath3}`;
@@ -15597,7 +16704,7 @@ function addKnowledgeBase(projectRoot, host, knowledgeBasePath) {
15597
16704
  }
15598
16705
  }
15599
16706
  for (const dotDir of sensitiveDotDirs) {
15600
- const sensitiveDir = path21.join(homeDir, dotDir);
16707
+ const sensitiveDir = path22.join(homeDir, dotDir);
15601
16708
  if (sensitiveDir && (realPath === sensitiveDir || realPath.startsWith(`${sensitiveDir}/`))) {
15602
16709
  return `Error: Adding sensitive directory as knowledge base is not allowed: ${normalizedPath3}`;
15603
16710
  }
@@ -15660,7 +16767,7 @@ function listKnowledgeBases(projectRoot, host) {
15660
16767
  }
15661
16768
  result += "\n";
15662
16769
  }
15663
- const hasHostConfig = (0, import_fs14.existsSync)(path21.join(root, getHostProjectConfigRelativePath(host)));
16770
+ const hasHostConfig = (0, import_fs14.existsSync)(path22.join(root, getHostProjectConfigRelativePath(host)));
15664
16771
  if (hasHostConfig) {
15665
16772
  result += `
15666
16773
  Config sources: 1 file(s).`;
@@ -16133,7 +17240,7 @@ async function resolveCodebaseContextUnmeasured(projectRoot, host, input) {
16133
17240
  const directory = input.directory ?? void 0;
16134
17241
  const tokenBudget = input.tokenBudget ?? void 0;
16135
17242
  if (from && to) {
16136
- const path33 = await getCallGraphPath(
17243
+ const path34 = await getCallGraphPath(
16137
17244
  projectRoot,
16138
17245
  host,
16139
17246
  from,
@@ -16142,25 +17249,25 @@ async function resolveCodebaseContextUnmeasured(projectRoot, host, input) {
16142
17249
  fromFilePath,
16143
17250
  toFilePath
16144
17251
  );
16145
- const pathText = formatCallGraphPathResult(path33);
16146
- if (path33.path.length > 0) {
17252
+ const pathText = formatCallGraphPathResult(path34);
17253
+ if (path34.path.length > 0) {
16147
17254
  const fitted2 = fitTextToContextBudget(
16148
17255
  pathText,
16149
17256
  tokenBudget
16150
17257
  );
16151
17258
  return {
16152
17259
  text: fitted2.text,
16153
- details: fittedDetails("path", fitted2, path33.path.length)
17260
+ details: fittedDetails("path", fitted2, path34.path.length)
16154
17261
  };
16155
17262
  }
16156
- if (path33.from.status !== "resolved" || path33.to.status !== "resolved") {
17263
+ if (path34.from.status !== "resolved" || path34.to.status !== "resolved") {
16157
17264
  const fitted2 = fitTextToContextBudget(pathText, tokenBudget);
16158
17265
  return {
16159
17266
  text: fitted2.text,
16160
17267
  details: fittedDetails("path", fitted2, 0)
16161
17268
  };
16162
17269
  }
16163
- const resolvedFrom = path33.from;
17270
+ const resolvedFrom = path34.from;
16164
17271
  const { callers } = await getCallGraphData(projectRoot, host, {
16165
17272
  name: to,
16166
17273
  direction: "callers",
@@ -16638,9 +17745,9 @@ function getRelevantEvidence(query) {
16638
17745
  });
16639
17746
  }
16640
17747
  if (query.expected.acceptableFiles) {
16641
- for (const path33 of query.expected.acceptableFiles) {
17748
+ for (const path34 of query.expected.acceptableFiles) {
16642
17749
  legacyEvidence.push({
16643
- path: path33,
17750
+ path: path34,
16644
17751
  ...query.expected.symbol !== void 0 ? { symbol: query.expected.symbol } : {},
16645
17752
  relevance: 1
16646
17753
  });
@@ -16963,8 +18070,8 @@ function computeEvalMetrics(queries, perQuery, embeddingCallCount, embeddingToke
16963
18070
 
16964
18071
  // src/eval/runner-config.ts
16965
18072
  var import_fs15 = require("fs");
16966
- var os7 = __toESM(require("os"), 1);
16967
- var path22 = __toESM(require("path"), 1);
18073
+ var os8 = __toESM(require("os"), 1);
18074
+ var path23 = __toESM(require("path"), 1);
16968
18075
  function isRecord2(value) {
16969
18076
  return typeof value === "object" && value !== null && !Array.isArray(value);
16970
18077
  }
@@ -17008,20 +18115,20 @@ function parseJsonConfigFile(filePath) {
17008
18115
  }
17009
18116
  }
17010
18117
  function toAbsolute(projectRoot, maybeRelative) {
17011
- return path22.isAbsolute(maybeRelative) ? maybeRelative : path22.join(projectRoot, maybeRelative);
18118
+ return path23.isAbsolute(maybeRelative) ? maybeRelative : path23.join(projectRoot, maybeRelative);
17012
18119
  }
17013
18120
  function isProjectScopedConfigPath(configPath) {
17014
- return path22.basename(configPath) === "codebase-index.json" && path22.basename(path22.dirname(configPath)) === ".opencode";
18121
+ return path23.basename(configPath) === "codebase-index.json" && path23.basename(path23.dirname(configPath)) === ".opencode";
17015
18122
  }
17016
18123
  function normalizeEvalConfigKnowledgeBases(rawConfig, projectRoot, resolvedConfigPath) {
17017
18124
  const config = rawConfig && typeof rawConfig === "object" ? { ...rawConfig } : {};
17018
18125
  const rebaseEntries = (values) => isProjectScopedConfigPath(resolvedConfigPath) ? resolveInheritedKnowledgeBaseEntries(
17019
18126
  values,
17020
- path22.dirname(path22.dirname(resolvedConfigPath)),
18127
+ path23.dirname(path23.dirname(resolvedConfigPath)),
17021
18128
  projectRoot
17022
18129
  ) : rebasePathEntries(
17023
18130
  values,
17024
- path22.dirname(resolvedConfigPath),
18131
+ path23.dirname(resolvedConfigPath),
17025
18132
  projectRoot
17026
18133
  );
17027
18134
  if (Array.isArray(config.knowledgeBases)) {
@@ -17049,7 +18156,7 @@ function loadRawConfig(projectRoot, configPath) {
17049
18156
  projectConfig
17050
18157
  );
17051
18158
  }
17052
- const globalConfig = path22.join(os7.homedir(), ".config", "opencode", "codebase-index.json");
18159
+ const globalConfig = path23.join(os8.homedir(), ".config", "opencode", "codebase-index.json");
17053
18160
  if ((0, import_fs15.existsSync)(globalConfig)) {
17054
18161
  return parseJsonConfigFile(globalConfig);
17055
18162
  }
@@ -17059,10 +18166,10 @@ function getIndexRootPath(projectRoot, scope) {
17059
18166
  return scope === "global" ? getGlobalIndexPath("opencode") : resolveProjectIndexPath(projectRoot, scope, "opencode");
17060
18167
  }
17061
18168
  function getLocalProjectIndexRoot(projectRoot) {
17062
- return path22.join(projectRoot, ".opencode", "index");
18169
+ return path23.join(projectRoot, ".opencode", "index");
17063
18170
  }
17064
18171
  function getLocalProjectConfigPath(projectRoot) {
17065
- return path22.join(projectRoot, ".opencode", "codebase-index.json");
18172
+ return path23.join(projectRoot, ".opencode", "codebase-index.json");
17066
18173
  }
17067
18174
  function clearIndexRoot(projectRoot, scope) {
17068
18175
  const indexRoot = scope === "global" ? getIndexRootPath(projectRoot, scope) : getLocalProjectIndexRoot(projectRoot);
@@ -17084,7 +18191,7 @@ function ensureLocalEvalProjectConfig(projectRoot, configPath) {
17084
18191
  projectRoot,
17085
18192
  resolvedConfigPath
17086
18193
  );
17087
- (0, import_fs15.mkdirSync)(path22.dirname(localConfigPath), { recursive: true });
18194
+ (0, import_fs15.mkdirSync)(path23.dirname(localConfigPath), { recursive: true });
17088
18195
  (0, import_fs15.writeFileSync)(localConfigPath, JSON.stringify(sourceConfig, null, 2), "utf-8");
17089
18196
  return localConfigPath;
17090
18197
  }
@@ -17137,68 +18244,68 @@ function isStringArray4(value) {
17137
18244
  function isNonEmptyString(value) {
17138
18245
  return typeof value === "string" && value.trim().length > 0;
17139
18246
  }
17140
- function asPositiveNumber(value, path33) {
18247
+ function asPositiveNumber(value, path34) {
17141
18248
  if (typeof value !== "number" || Number.isNaN(value) || value < 0) {
17142
- throw new Error(`${path33} must be a non-negative number`);
18249
+ throw new Error(`${path34} must be a non-negative number`);
17143
18250
  }
17144
18251
  return value;
17145
18252
  }
17146
- function parseQueryType(value, path33) {
18253
+ function parseQueryType(value, path34) {
17147
18254
  if (value === "definition" || value === "implementation-intent" || value === "similarity" || value === "keyword-heavy" || value === "conceptual") {
17148
18255
  return value;
17149
18256
  }
17150
18257
  throw new Error(
17151
- `${path33} must be one of: definition, implementation-intent, similarity, keyword-heavy, conceptual`
18258
+ `${path34} must be one of: definition, implementation-intent, similarity, keyword-heavy, conceptual`
17152
18259
  );
17153
18260
  }
17154
- function parseExpectedRoute(value, path33) {
18261
+ function parseExpectedRoute(value, path34) {
17155
18262
  if (value === void 0) return void 0;
17156
18263
  if (value === "search" || value === "definition") return value;
17157
- throw new Error(`${path33} must be one of: search, definition`);
18264
+ throw new Error(`${path34} must be one of: search, definition`);
17158
18265
  }
17159
- function parseExpectedOutcome(value, path33) {
18266
+ function parseExpectedOutcome(value, path34) {
17160
18267
  if (value === void 0) return void 0;
17161
18268
  if (value === "results" || value === "no-results") {
17162
18269
  return value;
17163
18270
  }
17164
- throw new Error(`${path33} must be one of: results, no-results`);
18271
+ throw new Error(`${path34} must be one of: results, no-results`);
17165
18272
  }
17166
- function parseRecoveryExpectation(value, path33) {
18273
+ function parseRecoveryExpectation(value, path34) {
17167
18274
  if (value === void 0) return void 0;
17168
18275
  if (value === "none" || value === "filter-relaxed") {
17169
18276
  return value;
17170
18277
  }
17171
- throw new Error(`${path33} must be one of: none, filter-relaxed`);
18278
+ throw new Error(`${path34} must be one of: none, filter-relaxed`);
17172
18279
  }
17173
- function parseQueryDifficulty(value, path33) {
18280
+ function parseQueryDifficulty(value, path34) {
17174
18281
  if (value === void 0) return void 0;
17175
18282
  if (value === "easy" || value === "medium" || value === "hard") {
17176
18283
  return value;
17177
18284
  }
17178
- throw new Error(`${path33} must be one of: easy, medium, hard`);
18285
+ throw new Error(`${path34} must be one of: easy, medium, hard`);
17179
18286
  }
17180
- function parseQueryTags(value, path33) {
18287
+ function parseQueryTags(value, path34) {
17181
18288
  if (value === void 0) return void 0;
17182
18289
  if (!isStringArray4(value) || value.some((tag) => tag.trim().length === 0)) {
17183
- throw new Error(`${path33} must be an array of non-empty strings`);
18290
+ throw new Error(`${path34} must be an array of non-empty strings`);
17184
18291
  }
17185
18292
  if (value.length > 16) {
17186
- throw new Error(`${path33} must contain at most 16 tags`);
18293
+ throw new Error(`${path34} must contain at most 16 tags`);
17187
18294
  }
17188
18295
  return value;
17189
18296
  }
17190
- function parseQueryArgs(value, path33) {
18297
+ function parseQueryArgs(value, path34) {
17191
18298
  if (value === void 0) return void 0;
17192
18299
  if (!isRecord3(value)) {
17193
- throw new Error(`${path33} must be an object`);
17194
- }
17195
- const symbol = parseStringOrUndefined(value.symbol, `${path33}.symbol`);
17196
- const filePath = parseStringOrUndefined(value.filePath, `${path33}.filePath`);
17197
- const fileType = parseStringOrUndefined(value.fileType, `${path33}.fileType`);
17198
- const directory = parseStringOrUndefined(value.directory, `${path33}.directory`);
17199
- const callerLimit = parsePositiveIntegerOrUndefined(value.callerLimit, `${path33}.callerLimit`);
17200
- const calleeLimit = parsePositiveIntegerOrUndefined(value.calleeLimit, `${path33}.calleeLimit`);
17201
- const tokenBudget = parsePositiveIntegerOrUndefined(value.tokenBudget, `${path33}.tokenBudget`);
18300
+ throw new Error(`${path34} must be an object`);
18301
+ }
18302
+ const symbol = parseStringOrUndefined(value.symbol, `${path34}.symbol`);
18303
+ const filePath = parseStringOrUndefined(value.filePath, `${path34}.filePath`);
18304
+ const fileType = parseStringOrUndefined(value.fileType, `${path34}.fileType`);
18305
+ const directory = parseStringOrUndefined(value.directory, `${path34}.directory`);
18306
+ const callerLimit = parsePositiveIntegerOrUndefined(value.callerLimit, `${path34}.callerLimit`);
18307
+ const calleeLimit = parsePositiveIntegerOrUndefined(value.calleeLimit, `${path34}.calleeLimit`);
18308
+ const tokenBudget = parsePositiveIntegerOrUndefined(value.tokenBudget, `${path34}.tokenBudget`);
17202
18309
  return {
17203
18310
  ...symbol !== void 0 ? { symbol } : {},
17204
18311
  ...filePath !== void 0 ? { filePath } : {},
@@ -17209,50 +18316,50 @@ function parseQueryArgs(value, path33) {
17209
18316
  ...tokenBudget !== void 0 ? { tokenBudget } : {}
17210
18317
  };
17211
18318
  }
17212
- function parsePositiveIntegerOrUndefined(value, path33) {
18319
+ function parsePositiveIntegerOrUndefined(value, path34) {
17213
18320
  if (value === void 0 || value === null) return void 0;
17214
18321
  if (typeof value !== "number" || !Number.isInteger(value) || value <= 0) {
17215
- throw new Error(`${path33} must be a positive integer`);
18322
+ throw new Error(`${path34} must be a positive integer`);
17216
18323
  }
17217
18324
  return value;
17218
18325
  }
17219
18326
  var SEMVER_VERSION_PATTERN = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/;
17220
- function parseSemanticVersion(value, path33) {
18327
+ function parseSemanticVersion(value, path34) {
17221
18328
  if (!isNonEmptyString(value)) {
17222
- throw new Error(`${path33} must be a non-empty string`);
18329
+ throw new Error(`${path34} must be a non-empty string`);
17223
18330
  }
17224
18331
  if (!SEMVER_VERSION_PATTERN.test(value)) {
17225
- throw new Error(`${path33} must be a valid semantic version (MAJOR.MINOR.PATCH)`);
18332
+ throw new Error(`${path34} must be a valid semantic version (MAJOR.MINOR.PATCH)`);
17226
18333
  }
17227
18334
  return value;
17228
18335
  }
17229
- function parseRetrievalMode(value, path33) {
18336
+ function parseRetrievalMode(value, path34) {
17230
18337
  if (value === void 0 || value === "search") return "search";
17231
18338
  if (value === "context" || value === "edit-context") return value;
17232
- throw new Error(`${path33} must be one of: search, context, edit-context`);
18339
+ throw new Error(`${path34} must be one of: search, context, edit-context`);
17233
18340
  }
17234
- function parseStringOrUndefined(value, path33) {
18341
+ function parseStringOrUndefined(value, path34) {
17235
18342
  if (value === void 0 || value === null) return void 0;
17236
18343
  if (!isNonEmptyString(value)) {
17237
- throw new Error(`${path33} must be a non-empty string`);
18344
+ throw new Error(`${path34} must be a non-empty string`);
17238
18345
  }
17239
18346
  return value;
17240
18347
  }
17241
- function parseGradedEvidence(value, path33) {
18348
+ function parseGradedEvidence(value, path34) {
17242
18349
  if (value === void 0) return [];
17243
18350
  if (!Array.isArray(value)) {
17244
- throw new Error(`${path33} must be an array`);
18351
+ throw new Error(`${path34} must be an array`);
17245
18352
  }
17246
18353
  return value.map((entry, index) => {
17247
18354
  if (!isRecord3(entry)) {
17248
- throw new Error(`${path33}[${index}] must be an object`);
18355
+ throw new Error(`${path34}[${index}] must be an object`);
17249
18356
  }
17250
- const evidencePath = parseStringOrUndefined(entry.path, `${path33}[${index}].path`);
18357
+ const evidencePath = parseStringOrUndefined(entry.path, `${path34}[${index}].path`);
17251
18358
  if (evidencePath === void 0) {
17252
- throw new Error(`${path33}[${index}].path is required`);
18359
+ throw new Error(`${path34}[${index}].path is required`);
17253
18360
  }
17254
- const symbol = parseStringOrUndefined(entry.symbol, `${path33}[${index}].symbol`);
17255
- const relevance = parseEvidenceRelevance(entry.relevance, `${path33}[${index}].relevance`);
18361
+ const symbol = parseStringOrUndefined(entry.symbol, `${path34}[${index}].symbol`);
18362
+ const relevance = parseEvidenceRelevance(entry.relevance, `${path34}[${index}].relevance`);
17256
18363
  return {
17257
18364
  path: evidencePath,
17258
18365
  ...symbol !== void 0 ? { symbol } : {},
@@ -17260,27 +18367,27 @@ function parseGradedEvidence(value, path33) {
17260
18367
  };
17261
18368
  });
17262
18369
  }
17263
- function parseEvidenceRelevance(value, path33) {
18370
+ function parseEvidenceRelevance(value, path34) {
17264
18371
  if (value === void 0) {
17265
- throw new Error(`${path33} is required`);
18372
+ throw new Error(`${path34} is required`);
17266
18373
  }
17267
18374
  if (value !== 1 && value !== 2 && value !== 3) {
17268
- throw new Error(`${path33} must be 1, 2, or 3`);
18375
+ throw new Error(`${path34} must be 1, 2, or 3`);
17269
18376
  }
17270
18377
  return value;
17271
18378
  }
17272
- function parseExpectedGraphNeighbor(value, path33) {
18379
+ function parseExpectedGraphNeighbor(value, path34) {
17273
18380
  if (value === void 0) return void 0;
17274
18381
  if (!isRecord3(value)) {
17275
- throw new Error(`${path33} must be an object`);
18382
+ throw new Error(`${path34} must be an object`);
17276
18383
  }
17277
18384
  if (value.direction !== "caller" && value.direction !== "callee") {
17278
- throw new Error(`${path33}.direction must be one of: caller, callee`);
18385
+ throw new Error(`${path34}.direction must be one of: caller, callee`);
17279
18386
  }
17280
- const filePath = parseStringOrUndefined(value.filePath, `${path33}.filePath`);
17281
- const symbol = parseStringOrUndefined(value.symbol, `${path33}.symbol`);
18387
+ const filePath = parseStringOrUndefined(value.filePath, `${path34}.filePath`);
18388
+ const symbol = parseStringOrUndefined(value.symbol, `${path34}.symbol`);
17282
18389
  if (filePath === void 0 && symbol === void 0) {
17283
- throw new Error(`${path33} must include filePath or symbol`);
18390
+ throw new Error(`${path34} must include filePath or symbol`);
17284
18391
  }
17285
18392
  return {
17286
18393
  direction: value.direction,
@@ -17288,9 +18395,9 @@ function parseExpectedGraphNeighbor(value, path33) {
17288
18395
  ...symbol !== void 0 ? { symbol } : {}
17289
18396
  };
17290
18397
  }
17291
- function parseExpected(input, path33) {
18398
+ function parseExpected(input, path34) {
17292
18399
  if (!isRecord3(input)) {
17293
- throw new Error(`${path33} must be an object`);
18400
+ throw new Error(`${path34} must be an object`);
17294
18401
  }
17295
18402
  const filePathRaw = input.filePath;
17296
18403
  const acceptableFilesRaw = input.acceptableFiles;
@@ -17301,29 +18408,29 @@ function parseExpected(input, path33) {
17301
18408
  const recoveryExpectationRaw = input.recoveryExpectation;
17302
18409
  const gradedEvidenceRaw = input.gradedEvidence;
17303
18410
  const graphNeighborRaw = input.graphNeighbor;
17304
- const filePath = parseStringOrUndefined(filePathRaw, `${path33}.filePath`);
18411
+ const filePath = parseStringOrUndefined(filePathRaw, `${path34}.filePath`);
17305
18412
  const acceptableFiles = isStringArray4(acceptableFilesRaw) ? acceptableFilesRaw : void 0;
17306
- const gradedEvidence = parseGradedEvidence(gradedEvidenceRaw, `${path33}.gradedEvidence`);
17307
- const graphNeighbor = parseExpectedGraphNeighbor(graphNeighborRaw, `${path33}.graphNeighbor`);
17308
- const expectedOutcome = parseExpectedOutcome(expectedOutcomeRaw, `${path33}.expectedOutcome`);
18413
+ const gradedEvidence = parseGradedEvidence(gradedEvidenceRaw, `${path34}.gradedEvidence`);
18414
+ const graphNeighbor = parseExpectedGraphNeighbor(graphNeighborRaw, `${path34}.graphNeighbor`);
18415
+ const expectedOutcome = parseExpectedOutcome(expectedOutcomeRaw, `${path34}.expectedOutcome`);
17309
18416
  if (expectedOutcome !== "no-results" && !filePath && (!acceptableFiles || acceptableFiles.length === 0) && gradedEvidence.length === 0) {
17310
18417
  throw new Error(
17311
- `${path33} must include expected.filePath, expected.acceptableFiles, or expected.gradedEvidence`
18418
+ `${path34} must include expected.filePath, expected.acceptableFiles, or expected.gradedEvidence`
17312
18419
  );
17313
18420
  }
17314
18421
  if (acceptableFilesRaw !== void 0 && !isStringArray4(acceptableFilesRaw)) {
17315
- throw new Error(`${path33}.acceptableFiles must be an array of strings`);
18422
+ throw new Error(`${path34}.acceptableFiles must be an array of strings`);
17316
18423
  }
17317
18424
  if (symbolRaw !== void 0 && typeof symbolRaw !== "string") {
17318
- throw new Error(`${path33}.symbol must be a string when provided`);
18425
+ throw new Error(`${path34}.symbol must be a string when provided`);
17319
18426
  }
17320
18427
  if (branchRaw !== void 0 && typeof branchRaw !== "string") {
17321
- throw new Error(`${path33}.branch must be a string when provided`);
18428
+ throw new Error(`${path34}.branch must be a string when provided`);
17322
18429
  }
17323
- const expectedRoute = parseExpectedRoute(expectedRouteRaw, `${path33}.expectedRoute`);
18430
+ const expectedRoute = parseExpectedRoute(expectedRouteRaw, `${path34}.expectedRoute`);
17324
18431
  const recoveryExpectation = parseRecoveryExpectation(
17325
18432
  recoveryExpectationRaw,
17326
- `${path33}.recoveryExpectation`
18433
+ `${path34}.recoveryExpectation`
17327
18434
  );
17328
18435
  return {
17329
18436
  filePath,
@@ -17337,13 +18444,13 @@ function parseExpected(input, path33) {
17337
18444
  ...graphNeighbor !== void 0 ? { graphNeighbor } : {}
17338
18445
  };
17339
18446
  }
17340
- function parseQueryLanguage(value, path33) {
17341
- return parseStringOrUndefined(value, path33);
18447
+ function parseQueryLanguage(value, path34) {
18448
+ return parseStringOrUndefined(value, path34);
17342
18449
  }
17343
18450
  function parseQuery(input, index) {
17344
- const path33 = `queries[${index}]`;
18451
+ const path34 = `queries[${index}]`;
17345
18452
  if (!isRecord3(input)) {
17346
- throw new Error(`${path33} must be an object`);
18453
+ throw new Error(`${path34} must be an object`);
17347
18454
  }
17348
18455
  const id = input.id;
17349
18456
  const query = input.query;
@@ -17355,21 +18462,21 @@ function parseQuery(input, index) {
17355
18462
  const tags = input.tags;
17356
18463
  const args = input.args;
17357
18464
  if (typeof id !== "string" || id.trim().length === 0) {
17358
- throw new Error(`${path33}.id must be a non-empty string`);
18465
+ throw new Error(`${path34}.id must be a non-empty string`);
17359
18466
  }
17360
18467
  if (typeof query !== "string" || query.trim().length === 0) {
17361
- throw new Error(`${path33}.query must be a non-empty string`);
18468
+ throw new Error(`${path34}.query must be a non-empty string`);
17362
18469
  }
17363
18470
  return {
17364
18471
  id,
17365
18472
  query,
17366
- queryType: parseQueryType(queryType, `${path33}.queryType`),
17367
- retrievalMode: parseRetrievalMode(retrievalMode, `${path33}.retrievalMode`),
17368
- language: parseQueryLanguage(language, `${path33}.language`),
17369
- difficulty: parseQueryDifficulty(difficulty, `${path33}.difficulty`),
17370
- args: parseQueryArgs(args, `${path33}.args`),
17371
- tags: parseQueryTags(tags, `${path33}.tags`),
17372
- expected: parseExpected(expected, `${path33}.expected`)
18473
+ queryType: parseQueryType(queryType, `${path34}.queryType`),
18474
+ retrievalMode: parseRetrievalMode(retrievalMode, `${path34}.retrievalMode`),
18475
+ language: parseQueryLanguage(language, `${path34}.language`),
18476
+ difficulty: parseQueryDifficulty(difficulty, `${path34}.difficulty`),
18477
+ args: parseQueryArgs(args, `${path34}.args`),
18478
+ tags: parseQueryTags(tags, `${path34}.tags`),
18479
+ expected: parseExpected(expected, `${path34}.expected`)
17373
18480
  };
17374
18481
  }
17375
18482
  function parseGoldenDataset(raw, sourceLabel) {
@@ -17785,13 +18892,13 @@ async function runEvaluation(options) {
17785
18892
  };
17786
18893
  const outputDir = createRunDirectory(toAbsolute(options.projectRoot, options.outputRoot));
17787
18894
  const perQueryArtifact = buildPerQueryArtifact(perQuery);
17788
- writeJson(path23.join(outputDir, "summary.json"), summary);
17789
- writeJson(path23.join(outputDir, "per-query.json"), perQueryArtifact);
18895
+ writeJson(path24.join(outputDir, "summary.json"), summary);
18896
+ writeJson(path24.join(outputDir, "per-query.json"), perQueryArtifact);
17790
18897
  let comparison;
17791
18898
  if (againstPath) {
17792
18899
  const baseline = loadSummary(againstPath);
17793
18900
  comparison = compareSummaries(summary, baseline, againstPath);
17794
- writeJson(path23.join(outputDir, "compare.json"), comparison);
18901
+ writeJson(path24.join(outputDir, "compare.json"), comparison);
17795
18902
  }
17796
18903
  let gate;
17797
18904
  if (options.ciMode) {
@@ -17804,7 +18911,7 @@ async function runEvaluation(options) {
17804
18911
  if ((0, import_fs17.existsSync)(resolvedBaseline)) {
17805
18912
  const baselineSummary = loadSummary(resolvedBaseline);
17806
18913
  comparison = compareSummaries(summary, baselineSummary, resolvedBaseline);
17807
- writeJson(path23.join(outputDir, "compare.json"), comparison);
18914
+ writeJson(path24.join(outputDir, "compare.json"), comparison);
17808
18915
  } else if (budget.failOnMissingBaseline) {
17809
18916
  throw new Error(
17810
18917
  `Budget baseline is missing: ${resolvedBaseline}. Set failOnMissingBaseline=false to allow CI run without baseline.`
@@ -17814,7 +18921,7 @@ async function runEvaluation(options) {
17814
18921
  gate = evaluateBudgetGate(budget, summary, comparison);
17815
18922
  }
17816
18923
  const markdown = createSummaryMarkdown(summary, comparison, gate);
17817
- writeText(path23.join(outputDir, "summary.md"), markdown);
18924
+ writeText(path24.join(outputDir, "summary.md"), markdown);
17818
18925
  return { outputDir, summary, perQuery, comparison, gate };
17819
18926
  } finally {
17820
18927
  await indexer.close();
@@ -17872,23 +18979,23 @@ async function runSweep(options, sweep) {
17872
18979
  bestByMrrAt10,
17873
18980
  bestByP95Latency
17874
18981
  };
17875
- writeJson(path23.join(outputDir, "compare.json"), aggregate);
18982
+ writeJson(path24.join(outputDir, "compare.json"), aggregate);
17876
18983
  const md = createSummaryMarkdown(
17877
18984
  bestByHitAt5?.summary ?? runs[0].summary,
17878
18985
  bestByHitAt5?.comparison,
17879
18986
  void 0,
17880
18987
  aggregate
17881
18988
  );
17882
- writeText(path23.join(outputDir, "summary.md"), md);
17883
- writeJson(path23.join(outputDir, "summary.json"), bestByHitAt5?.summary ?? runs[0].summary);
18989
+ writeText(path24.join(outputDir, "summary.md"), md);
18990
+ writeJson(path24.join(outputDir, "summary.json"), bestByHitAt5?.summary ?? runs[0].summary);
17884
18991
  return { outputDir, aggregate };
17885
18992
  }
17886
18993
 
17887
18994
  // src/eval/cli.ts
17888
- var path25 = __toESM(require("path"), 1);
18995
+ var path26 = __toESM(require("path"), 1);
17889
18996
 
17890
18997
  // src/eval/cli-parser.ts
17891
- var path24 = __toESM(require("path"), 1);
18998
+ var path25 = __toESM(require("path"), 1);
17892
18999
  function printUsage() {
17893
19000
  console.log(`
17894
19001
  Usage:
@@ -17960,12 +19067,12 @@ function parseEvalArgs(argv, cwd) {
17960
19067
  const arg = argv[i];
17961
19068
  const next = argv[i + 1];
17962
19069
  if (arg === "--project" && next) {
17963
- parsed.projectRoot = path24.resolve(cwd, next);
19070
+ parsed.projectRoot = path25.resolve(cwd, next);
17964
19071
  i += 1;
17965
19072
  continue;
17966
19073
  }
17967
19074
  if (arg === "--config" && next) {
17968
- parsed.configPath = path24.resolve(cwd, next);
19075
+ parsed.configPath = path25.resolve(cwd, next);
17969
19076
  i += 1;
17970
19077
  continue;
17971
19078
  }
@@ -18161,22 +19268,22 @@ async function handleEvalCommand(args, cwd) {
18161
19268
  if (!parsed.againstPath.endsWith(".json")) {
18162
19269
  throw new Error("eval diff --against must point to a summary JSON file");
18163
19270
  }
18164
- const currentSummary = loadSummary(path25.resolve(parsed.projectRoot, currentPath), {
19271
+ const currentSummary = loadSummary(path26.resolve(parsed.projectRoot, currentPath), {
18165
19272
  allowLegacyDiversityMetrics: true
18166
19273
  });
18167
- const baselineSummary = loadSummary(path25.resolve(parsed.projectRoot, parsed.againstPath), {
19274
+ const baselineSummary = loadSummary(path26.resolve(parsed.projectRoot, parsed.againstPath), {
18168
19275
  allowLegacyDiversityMetrics: true
18169
19276
  });
18170
19277
  const comparison = compareSummaries(
18171
19278
  currentSummary,
18172
19279
  baselineSummary,
18173
- path25.resolve(parsed.projectRoot, parsed.againstPath)
19280
+ path26.resolve(parsed.projectRoot, parsed.againstPath)
18174
19281
  );
18175
- const outputDir = createRunDirectory(path25.resolve(parsed.projectRoot, parsed.outputRoot));
19282
+ const outputDir = createRunDirectory(path26.resolve(parsed.projectRoot, parsed.outputRoot));
18176
19283
  const summaryMd = createSummaryMarkdown(currentSummary, comparison);
18177
- writeJson(path25.join(outputDir, "compare.json"), comparison);
18178
- writeText(path25.join(outputDir, "summary.md"), summaryMd);
18179
- writeJson(path25.join(outputDir, "summary.json"), currentSummary);
19284
+ writeJson(path26.join(outputDir, "compare.json"), comparison);
19285
+ writeText(path26.join(outputDir, "summary.md"), summaryMd);
19286
+ writeJson(path26.join(outputDir, "summary.json"), currentSummary);
18180
19287
  console.log(`Eval diff complete. Artifacts: ${outputDir}`);
18181
19288
  return 0;
18182
19289
  }
@@ -18301,6 +19408,7 @@ async function executeCodebaseEditContext(projectRoot, host, args) {
18301
19408
  async function executeIndexCodebase(projectRoot, host, args, onProgress) {
18302
19409
  const result = await runIndexCodebase(projectRoot, host, args, onProgress);
18303
19410
  if (result.kind === "estimate") return { text: formatCostEstimate(result.estimate) };
19411
+ if (result.kind === "dryrun") return { text: formatDryRunEstimate(result.dryrun) };
18304
19412
  if (result.kind === "busy") return { text: result.text, isError: true };
18305
19413
  if (result.kind === "message") return { text: result.text };
18306
19414
  return { text: formatIndexStats(result.stats, args.verbose ?? false) };
@@ -18337,7 +19445,7 @@ async function executeCallGraph(projectRoot, host, args) {
18337
19445
  return { text: formatCallGraphResult(await getCallGraphData(projectRoot, host, args)) };
18338
19446
  }
18339
19447
  async function executeCallGraphPath(projectRoot, host, args) {
18340
- const path33 = await getCallGraphPath(
19448
+ const path34 = await getCallGraphPath(
18341
19449
  projectRoot,
18342
19450
  host,
18343
19451
  args.from,
@@ -18346,7 +19454,7 @@ async function executeCallGraphPath(projectRoot, host, args) {
18346
19454
  args.fromFilePath,
18347
19455
  args.toFilePath
18348
19456
  );
18349
- return { text: formatCallGraphPathResult(path33) };
19457
+ return { text: formatCallGraphPathResult(path34) };
18350
19458
  }
18351
19459
  async function executeCodeCommunities(projectRoot, host, args) {
18352
19460
  const result = await getCodeCommunities(projectRoot, host, args);
@@ -18628,6 +19736,7 @@ ${formatCodebasePeek(results)}`;
18628
19736
  {
18629
19737
  force: allowNullAsUndefined(import_zod2.z.boolean().optional().default(false)).describe("Force reindex even if already indexed"),
18630
19738
  estimateOnly: allowNullAsUndefined(import_zod2.z.boolean().optional().default(false)).describe("Only show cost estimate without indexing"),
19739
+ dryRun: allowNullAsUndefined(import_zod2.z.boolean().optional().default(false)).describe("Parse the file set and report the exact embedding token total without indexing. Read-only; the index is not changed. The total is the value 'Tokens used' climbs to for a force index (and an upper bound for an incremental)."),
18631
19740
  verbose: allowNullAsUndefined(import_zod2.z.boolean().optional().default(false)).describe("Show detailed info about skipped files and parsing failures")
18632
19741
  },
18633
19742
  async (args) => {
@@ -18840,9 +19949,64 @@ ${formatSearchResults(results)}` }] };
18840
19949
  }
18841
19950
 
18842
19951
  // src/adapters/mcp/server.ts
19952
+ var mcpWorkerReferences = /* @__PURE__ */ new Map();
19953
+ var mcpWorkerTeardowns = /* @__PURE__ */ new Map();
19954
+ function retainMcpBackgroundWorker(projectRoot, host) {
19955
+ const key = getBackgroundWorkerProjectKey(projectRoot, host);
19956
+ mcpWorkerReferences.set(key, (mcpWorkerReferences.get(key) ?? 0) + 1);
19957
+ }
19958
+ async function releaseMcpBackgroundWorker(projectRoot, host) {
19959
+ const key = getBackgroundWorkerProjectKey(projectRoot, host);
19960
+ const references = mcpWorkerReferences.get(key) ?? 0;
19961
+ if (references > 1) {
19962
+ mcpWorkerReferences.set(key, references - 1);
19963
+ return;
19964
+ }
19965
+ mcpWorkerReferences.delete(key);
19966
+ const teardown = stopBackgroundWorker(projectRoot, host);
19967
+ mcpWorkerTeardowns.set(key, teardown);
19968
+ try {
19969
+ await teardown;
19970
+ } finally {
19971
+ if (mcpWorkerTeardowns.get(key) === teardown) {
19972
+ mcpWorkerTeardowns.delete(key);
19973
+ }
19974
+ }
19975
+ }
18843
19976
  function getServerInstructions(host) {
18844
19977
  const hostText = `host ${host}`;
18845
- return `This MCP server is the preferred codebase-understanding path for ${hostText}. Start a repository task with index_status when index readiness or freshness is unknown. Use codebase_context as the preferred first entry point because it returns a token-budgeted location pack and routes to definitions or call-graph helpers when symbol intent is present. Keep the default tokenBudget for normal discovery, then use implementation_lookup, codebase_search, or a targeted file read only for selected locations that need source content. Use codebase_peek for direct conceptual location lookup. For exact identifiers or exhaustive matches, use grep. After identifying symbols, use call_graph or call_graph_path to trace dependencies. If the index is unavailable, run index_codebase, then retry the retrieval tool.`;
19978
+ return `This MCP server is the preferred codebase-understanding path for ${hostText}. Start a repository task with index_status when index readiness or freshness is unknown. Use codebase_context as the preferred first entry point because it returns a token-budgeted location pack and routes to definitions or call-graph helpers when symbol intent is present. For code changes with a known or suspected symbol target, optionally call codebase_edit_context as a compact pre-edit step for bounded source plus direct callers and callees before broad file reads. Keep the default tokenBudget for normal discovery, then use implementation_lookup, codebase_search, or a targeted file read only for selected locations that need source content. Use codebase_peek for direct conceptual location lookup. For exact identifiers or exhaustive matches, use grep. After identifying symbols, use call_graph or call_graph_path to trace dependencies. If the index is unavailable, run index_codebase, then retry the retrieval tool.`;
19979
+ }
19980
+ function configureMcpBackgroundWorker(projectRoot, config, host, watcherFactory, watcherFactoryForConfig) {
19981
+ if (!getProjectSafety(projectRoot, config).safeToRun) {
19982
+ return { managesWorker: false };
19983
+ }
19984
+ if (isBackgroundWorkerManaged(projectRoot, host)) {
19985
+ const key = getBackgroundWorkerProjectKey(projectRoot, host);
19986
+ if ((mcpWorkerReferences.get(key) ?? 0) === 0 && !mcpWorkerTeardowns.has(key)) {
19987
+ return { managesWorker: false };
19988
+ }
19989
+ if (watcherFactory !== void 0) {
19990
+ attachBackgroundWorkerWatcher(projectRoot, host, watcherFactory, watcherFactoryForConfig);
19991
+ }
19992
+ if (mcpWorkerTeardowns.has(key) || isBackgroundWorkerStopping(projectRoot, host)) {
19993
+ requestBackgroundWorker(projectRoot, host);
19994
+ }
19995
+ return { managesWorker: true };
19996
+ }
19997
+ configureBackgroundWorker(projectRoot, host, config, {
19998
+ startAutoIndex: (source, allowDisabledAutoIndex) => {
19999
+ startAutoIndexForBackgroundWorker(projectRoot, host, source, allowDisabledAutoIndex);
20000
+ },
20001
+ stopAutoIndex: () => stopAutoIndexForBackgroundWorker(projectRoot, host),
20002
+ watcherFactory,
20003
+ watcherFactoryForConfig
20004
+ });
20005
+ return { managesWorker: true };
20006
+ }
20007
+ function attachMcpBackgroundWatcher(projectRoot, config, host, watcherFactory, watcherFactoryForConfig) {
20008
+ configureMcpBackgroundWorker(projectRoot, config, host, watcherFactory, watcherFactoryForConfig);
20009
+ return waitForBackgroundWorkerStart(projectRoot, host);
18846
20010
  }
18847
20011
  function createMcpServer(projectRoot, config, host) {
18848
20012
  const server = new import_mcp.McpServer({
@@ -18851,11 +20015,14 @@ function createMcpServer(projectRoot, config, host) {
18851
20015
  }, {
18852
20016
  instructions: getServerInstructions(host)
18853
20017
  });
18854
- initializeTools(projectRoot, config, host);
18855
- startAutoIndex(projectRoot, host, "startup");
20018
+ initializeTools(projectRoot, config, host, { preserveManagedWorker: true });
20019
+ const backgroundWorker = configureMcpBackgroundWorker(projectRoot, config, host);
20020
+ if (backgroundWorker.managesWorker) {
20021
+ retainMcpBackgroundWorker(projectRoot, host);
20022
+ }
18856
20023
  let stopCoordinationPromise = null;
18857
20024
  const stopCoordination = () => {
18858
- stopCoordinationPromise ??= stopAutoIndex(projectRoot, host);
20025
+ stopCoordinationPromise ??= backgroundWorker.managesWorker ? releaseMcpBackgroundWorker(projectRoot, host) : Promise.resolve();
18859
20026
  return stopCoordinationPromise;
18860
20027
  };
18861
20028
  const closeProtocol = server.server.close.bind(server.server);
@@ -18871,7 +20038,9 @@ function createMcpServer(projectRoot, config, host) {
18871
20038
  const onServerClose = server.server.onclose;
18872
20039
  server.server.onclose = () => {
18873
20040
  onServerClose?.();
18874
- void stopCoordination();
20041
+ void stopCoordination().catch((error) => {
20042
+ console.error("[codebase-index] Failed to stop MCP background worker after transport close:", error);
20043
+ });
18875
20044
  };
18876
20045
  registerMcpTools(server, {
18877
20046
  projectRoot,
@@ -18886,7 +20055,7 @@ var import_fs19 = require("fs");
18886
20055
 
18887
20056
  // node_modules/chokidar/index.js
18888
20057
  var import_node_events = require("events");
18889
- var import_node_fs2 = require("fs");
20058
+ var import_node_fs3 = require("fs");
18890
20059
  var import_promises3 = require("fs/promises");
18891
20060
  var sp2 = __toESM(require("path"), 1);
18892
20061
 
@@ -18974,7 +20143,7 @@ var ReaddirpStream = class extends import_node_stream.Readable {
18974
20143
  this._directoryFilter = normalizeFilter(opts.directoryFilter);
18975
20144
  const statMethod = opts.lstat ? import_promises.lstat : import_promises.stat;
18976
20145
  if (wantBigintFsStats) {
18977
- this._stat = (path33) => statMethod(path33, { bigint: true });
20146
+ this._stat = (path34) => statMethod(path34, { bigint: true });
18978
20147
  } else {
18979
20148
  this._stat = statMethod;
18980
20149
  }
@@ -18999,8 +20168,8 @@ var ReaddirpStream = class extends import_node_stream.Readable {
18999
20168
  const par = this.parent;
19000
20169
  const fil = par && par.files;
19001
20170
  if (fil && fil.length > 0) {
19002
- const { path: path33, depth } = par;
19003
- const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path33));
20171
+ const { path: path34, depth } = par;
20172
+ const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path34));
19004
20173
  const awaited = await Promise.all(slice);
19005
20174
  for (const entry of awaited) {
19006
20175
  if (!entry)
@@ -19040,21 +20209,21 @@ var ReaddirpStream = class extends import_node_stream.Readable {
19040
20209
  this.reading = false;
19041
20210
  }
19042
20211
  }
19043
- async _exploreDir(path33, depth) {
20212
+ async _exploreDir(path34, depth) {
19044
20213
  let files;
19045
20214
  try {
19046
- files = await (0, import_promises.readdir)(path33, this._rdOptions);
20215
+ files = await (0, import_promises.readdir)(path34, this._rdOptions);
19047
20216
  } catch (error) {
19048
20217
  this._onError(error);
19049
20218
  }
19050
- return { files, depth, path: path33 };
20219
+ return { files, depth, path: path34 };
19051
20220
  }
19052
- async _formatEntry(dirent, path33) {
20221
+ async _formatEntry(dirent, path34) {
19053
20222
  let entry;
19054
- const basename8 = this._isDirent ? dirent.name : dirent;
20223
+ const basename9 = this._isDirent ? dirent.name : dirent;
19055
20224
  try {
19056
- const fullPath = (0, import_node_path.resolve)((0, import_node_path.join)(path33, basename8));
19057
- entry = { path: (0, import_node_path.relative)(this._root, fullPath), fullPath, basename: basename8 };
20225
+ const fullPath = (0, import_node_path.resolve)((0, import_node_path.join)(path34, basename9));
20226
+ entry = { path: (0, import_node_path.relative)(this._root, fullPath), fullPath, basename: basename9 };
19058
20227
  entry[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath);
19059
20228
  } catch (err) {
19060
20229
  this._onError(err);
@@ -19124,7 +20293,7 @@ function readdirp(root, options = {}) {
19124
20293
  }
19125
20294
 
19126
20295
  // node_modules/chokidar/handler.js
19127
- var import_node_fs = require("fs");
20296
+ var import_node_fs2 = require("fs");
19128
20297
  var import_promises2 = require("fs/promises");
19129
20298
  var import_node_os = require("os");
19130
20299
  var sp = __toESM(require("path"), 1);
@@ -19453,16 +20622,16 @@ var delFromSet = (main, prop, item) => {
19453
20622
  };
19454
20623
  var isEmptySet = (val) => val instanceof Set ? val.size === 0 : !val;
19455
20624
  var FsWatchInstances = /* @__PURE__ */ new Map();
19456
- function createFsWatchInstance(path33, options, listener, errHandler, emitRaw) {
20625
+ function createFsWatchInstance(path34, options, listener, errHandler, emitRaw) {
19457
20626
  const handleEvent = (rawEvent, evPath) => {
19458
- listener(path33);
19459
- emitRaw(rawEvent, evPath, { watchedPath: path33 });
19460
- if (evPath && path33 !== evPath) {
19461
- fsWatchBroadcast(sp.resolve(path33, evPath), KEY_LISTENERS, sp.join(path33, evPath));
20627
+ listener(path34);
20628
+ emitRaw(rawEvent, evPath, { watchedPath: path34 });
20629
+ if (evPath && path34 !== evPath) {
20630
+ fsWatchBroadcast(sp.resolve(path34, evPath), KEY_LISTENERS, sp.join(path34, evPath));
19462
20631
  }
19463
20632
  };
19464
20633
  try {
19465
- return (0, import_node_fs.watch)(path33, {
20634
+ return (0, import_node_fs2.watch)(path34, {
19466
20635
  persistent: options.persistent
19467
20636
  }, handleEvent);
19468
20637
  } catch (error) {
@@ -19478,12 +20647,12 @@ var fsWatchBroadcast = (fullPath, listenerType, val1, val2, val3) => {
19478
20647
  listener(val1, val2, val3);
19479
20648
  });
19480
20649
  };
19481
- var setFsWatchListener = (path33, fullPath, options, handlers) => {
20650
+ var setFsWatchListener = (path34, fullPath, options, handlers) => {
19482
20651
  const { listener, errHandler, rawEmitter } = handlers;
19483
20652
  let cont = FsWatchInstances.get(fullPath);
19484
20653
  let watcher;
19485
20654
  if (!options.persistent) {
19486
- watcher = createFsWatchInstance(path33, options, listener, errHandler, rawEmitter);
20655
+ watcher = createFsWatchInstance(path34, options, listener, errHandler, rawEmitter);
19487
20656
  if (!watcher)
19488
20657
  return;
19489
20658
  return watcher.close.bind(watcher);
@@ -19494,7 +20663,7 @@ var setFsWatchListener = (path33, fullPath, options, handlers) => {
19494
20663
  addAndConvert(cont, KEY_RAW, rawEmitter);
19495
20664
  } else {
19496
20665
  watcher = createFsWatchInstance(
19497
- path33,
20666
+ path34,
19498
20667
  options,
19499
20668
  fsWatchBroadcast.bind(null, fullPath, KEY_LISTENERS),
19500
20669
  errHandler,
@@ -19509,7 +20678,7 @@ var setFsWatchListener = (path33, fullPath, options, handlers) => {
19509
20678
  cont.watcherUnusable = true;
19510
20679
  if (isWindows && error.code === "EPERM") {
19511
20680
  try {
19512
- const fd = await (0, import_promises2.open)(path33, "r");
20681
+ const fd = await (0, import_promises2.open)(path34, "r");
19513
20682
  await fd.close();
19514
20683
  broadcastErr(error);
19515
20684
  } catch (err) {
@@ -19540,12 +20709,12 @@ var setFsWatchListener = (path33, fullPath, options, handlers) => {
19540
20709
  };
19541
20710
  };
19542
20711
  var FsWatchFileInstances = /* @__PURE__ */ new Map();
19543
- var setFsWatchFileListener = (path33, fullPath, options, handlers) => {
20712
+ var setFsWatchFileListener = (path34, fullPath, options, handlers) => {
19544
20713
  const { listener, rawEmitter } = handlers;
19545
20714
  let cont = FsWatchFileInstances.get(fullPath);
19546
20715
  const copts = cont && cont.options;
19547
20716
  if (copts && (copts.persistent < options.persistent || copts.interval > options.interval)) {
19548
- (0, import_node_fs.unwatchFile)(fullPath);
20717
+ (0, import_node_fs2.unwatchFile)(fullPath);
19549
20718
  cont = void 0;
19550
20719
  }
19551
20720
  if (cont) {
@@ -19556,13 +20725,13 @@ var setFsWatchFileListener = (path33, fullPath, options, handlers) => {
19556
20725
  listeners: listener,
19557
20726
  rawEmitters: rawEmitter,
19558
20727
  options,
19559
- watcher: (0, import_node_fs.watchFile)(fullPath, options, (curr, prev) => {
20728
+ watcher: (0, import_node_fs2.watchFile)(fullPath, options, (curr, prev) => {
19560
20729
  foreach(cont.rawEmitters, (rawEmitter2) => {
19561
20730
  rawEmitter2(EV.CHANGE, fullPath, { curr, prev });
19562
20731
  });
19563
20732
  const currmtime = curr.mtimeMs;
19564
20733
  if (curr.size !== prev.size || currmtime > prev.mtimeMs || currmtime === 0) {
19565
- foreach(cont.listeners, (listener2) => listener2(path33, curr));
20734
+ foreach(cont.listeners, (listener2) => listener2(path34, curr));
19566
20735
  }
19567
20736
  })
19568
20737
  };
@@ -19573,7 +20742,7 @@ var setFsWatchFileListener = (path33, fullPath, options, handlers) => {
19573
20742
  delFromSet(cont, KEY_RAW, rawEmitter);
19574
20743
  if (isEmptySet(cont.listeners)) {
19575
20744
  FsWatchFileInstances.delete(fullPath);
19576
- (0, import_node_fs.unwatchFile)(fullPath);
20745
+ (0, import_node_fs2.unwatchFile)(fullPath);
19577
20746
  cont.options = cont.watcher = void 0;
19578
20747
  Object.freeze(cont);
19579
20748
  }
@@ -19592,13 +20761,13 @@ var NodeFsHandler = class {
19592
20761
  * @param listener on fs change
19593
20762
  * @returns closer for the watcher instance
19594
20763
  */
19595
- _watchWithNodeFs(path33, listener) {
20764
+ _watchWithNodeFs(path34, listener) {
19596
20765
  const opts = this.fsw.options;
19597
- const directory = sp.dirname(path33);
19598
- const basename8 = sp.basename(path33);
20766
+ const directory = sp.dirname(path34);
20767
+ const basename9 = sp.basename(path34);
19599
20768
  const parent = this.fsw._getWatchedDir(directory);
19600
- parent.add(basename8);
19601
- const absolutePath = sp.resolve(path33);
20769
+ parent.add(basename9);
20770
+ const absolutePath = sp.resolve(path34);
19602
20771
  const options = {
19603
20772
  persistent: opts.persistent
19604
20773
  };
@@ -19607,13 +20776,13 @@ var NodeFsHandler = class {
19607
20776
  let closer;
19608
20777
  if (opts.usePolling) {
19609
20778
  const enableBin = opts.interval !== opts.binaryInterval;
19610
- options.interval = enableBin && isBinaryPath(basename8) ? opts.binaryInterval : opts.interval;
19611
- closer = setFsWatchFileListener(path33, absolutePath, options, {
20779
+ options.interval = enableBin && isBinaryPath(basename9) ? opts.binaryInterval : opts.interval;
20780
+ closer = setFsWatchFileListener(path34, absolutePath, options, {
19612
20781
  listener,
19613
20782
  rawEmitter: this.fsw._emitRaw
19614
20783
  });
19615
20784
  } else {
19616
- closer = setFsWatchListener(path33, absolutePath, options, {
20785
+ closer = setFsWatchListener(path34, absolutePath, options, {
19617
20786
  listener,
19618
20787
  errHandler: this._boundHandleError,
19619
20788
  rawEmitter: this.fsw._emitRaw
@@ -19629,13 +20798,13 @@ var NodeFsHandler = class {
19629
20798
  if (this.fsw.closed) {
19630
20799
  return;
19631
20800
  }
19632
- const dirname15 = sp.dirname(file);
19633
- const basename8 = sp.basename(file);
19634
- const parent = this.fsw._getWatchedDir(dirname15);
20801
+ const dirname16 = sp.dirname(file);
20802
+ const basename9 = sp.basename(file);
20803
+ const parent = this.fsw._getWatchedDir(dirname16);
19635
20804
  let prevStats = stats;
19636
- if (parent.has(basename8))
20805
+ if (parent.has(basename9))
19637
20806
  return;
19638
- const listener = async (path33, newStats) => {
20807
+ const listener = async (path34, newStats) => {
19639
20808
  if (!this.fsw._throttle(THROTTLE_MODE_WATCH, file, 5))
19640
20809
  return;
19641
20810
  if (!newStats || newStats.mtimeMs === 0) {
@@ -19649,18 +20818,18 @@ var NodeFsHandler = class {
19649
20818
  this.fsw._emit(EV.CHANGE, file, newStats2);
19650
20819
  }
19651
20820
  if ((isMacos || isLinux || isFreeBSD) && prevStats.ino !== newStats2.ino) {
19652
- this.fsw._closeFile(path33);
20821
+ this.fsw._closeFile(path34);
19653
20822
  prevStats = newStats2;
19654
20823
  const closer2 = this._watchWithNodeFs(file, listener);
19655
20824
  if (closer2)
19656
- this.fsw._addPathCloser(path33, closer2);
20825
+ this.fsw._addPathCloser(path34, closer2);
19657
20826
  } else {
19658
20827
  prevStats = newStats2;
19659
20828
  }
19660
20829
  } catch (error) {
19661
- this.fsw._remove(dirname15, basename8);
20830
+ this.fsw._remove(dirname16, basename9);
19662
20831
  }
19663
- } else if (parent.has(basename8)) {
20832
+ } else if (parent.has(basename9)) {
19664
20833
  const at = newStats.atimeMs;
19665
20834
  const mt = newStats.mtimeMs;
19666
20835
  if (!at || at <= mt || mt !== prevStats.mtimeMs) {
@@ -19685,7 +20854,7 @@ var NodeFsHandler = class {
19685
20854
  * @param item basename of this item
19686
20855
  * @returns true if no more processing is needed for this entry.
19687
20856
  */
19688
- async _handleSymlink(entry, directory, path33, item) {
20857
+ async _handleSymlink(entry, directory, path34, item) {
19689
20858
  if (this.fsw.closed) {
19690
20859
  return;
19691
20860
  }
@@ -19695,7 +20864,7 @@ var NodeFsHandler = class {
19695
20864
  this.fsw._incrReadyCount();
19696
20865
  let linkPath;
19697
20866
  try {
19698
- linkPath = await (0, import_promises2.realpath)(path33);
20867
+ linkPath = await (0, import_promises2.realpath)(path34);
19699
20868
  } catch (e) {
19700
20869
  this.fsw._emitReady();
19701
20870
  return true;
@@ -19705,12 +20874,12 @@ var NodeFsHandler = class {
19705
20874
  if (dir.has(item)) {
19706
20875
  if (this.fsw._symlinkPaths.get(full) !== linkPath) {
19707
20876
  this.fsw._symlinkPaths.set(full, linkPath);
19708
- this.fsw._emit(EV.CHANGE, path33, entry.stats);
20877
+ this.fsw._emit(EV.CHANGE, path34, entry.stats);
19709
20878
  }
19710
20879
  } else {
19711
20880
  dir.add(item);
19712
20881
  this.fsw._symlinkPaths.set(full, linkPath);
19713
- this.fsw._emit(EV.ADD, path33, entry.stats);
20882
+ this.fsw._emit(EV.ADD, path34, entry.stats);
19714
20883
  }
19715
20884
  this.fsw._emitReady();
19716
20885
  return true;
@@ -19740,9 +20909,9 @@ var NodeFsHandler = class {
19740
20909
  return;
19741
20910
  }
19742
20911
  const item = entry.path;
19743
- let path33 = sp.join(directory, item);
20912
+ let path34 = sp.join(directory, item);
19744
20913
  current.add(item);
19745
- if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path33, item)) {
20914
+ if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path34, item)) {
19746
20915
  return;
19747
20916
  }
19748
20917
  if (this.fsw.closed) {
@@ -19751,11 +20920,11 @@ var NodeFsHandler = class {
19751
20920
  }
19752
20921
  if (item === target || !target && !previous.has(item)) {
19753
20922
  this.fsw._incrReadyCount();
19754
- path33 = sp.join(dir, sp.relative(dir, path33));
19755
- this._addToNodeFs(path33, initialAdd, wh, depth + 1);
20923
+ path34 = sp.join(dir, sp.relative(dir, path34));
20924
+ this._addToNodeFs(path34, initialAdd, wh, depth + 1);
19756
20925
  }
19757
20926
  }).on(EV.ERROR, this._boundHandleError);
19758
- return new Promise((resolve20, reject) => {
20927
+ return new Promise((resolve21, reject) => {
19759
20928
  if (!stream)
19760
20929
  return reject();
19761
20930
  stream.once(STR_END, () => {
@@ -19764,7 +20933,7 @@ var NodeFsHandler = class {
19764
20933
  return;
19765
20934
  }
19766
20935
  const wasThrottled = throttler ? throttler.clear() : false;
19767
- resolve20(void 0);
20936
+ resolve21(void 0);
19768
20937
  previous.getChildren().filter((item) => {
19769
20938
  return item !== directory && !current.has(item);
19770
20939
  }).forEach((item) => {
@@ -19821,13 +20990,13 @@ var NodeFsHandler = class {
19821
20990
  * @param depth Child path actually targeted for watch
19822
20991
  * @param target Child path actually targeted for watch
19823
20992
  */
19824
- async _addToNodeFs(path33, initialAdd, priorWh, depth, target) {
20993
+ async _addToNodeFs(path34, initialAdd, priorWh, depth, target) {
19825
20994
  const ready = this.fsw._emitReady;
19826
- if (this.fsw._isIgnored(path33) || this.fsw.closed) {
20995
+ if (this.fsw._isIgnored(path34) || this.fsw.closed) {
19827
20996
  ready();
19828
20997
  return false;
19829
20998
  }
19830
- const wh = this.fsw._getWatchHelpers(path33);
20999
+ const wh = this.fsw._getWatchHelpers(path34);
19831
21000
  if (priorWh) {
19832
21001
  wh.filterPath = (entry) => priorWh.filterPath(entry);
19833
21002
  wh.filterDir = (entry) => priorWh.filterDir(entry);
@@ -19843,8 +21012,8 @@ var NodeFsHandler = class {
19843
21012
  const follow = this.fsw.options.followSymlinks;
19844
21013
  let closer;
19845
21014
  if (stats.isDirectory()) {
19846
- const absPath = sp.resolve(path33);
19847
- const targetPath = follow ? await (0, import_promises2.realpath)(path33) : path33;
21015
+ const absPath = sp.resolve(path34);
21016
+ const targetPath = follow ? await (0, import_promises2.realpath)(path34) : path34;
19848
21017
  if (this.fsw.closed)
19849
21018
  return;
19850
21019
  closer = await this._handleDir(wh.watchPath, stats, initialAdd, depth, target, wh, targetPath);
@@ -19854,29 +21023,29 @@ var NodeFsHandler = class {
19854
21023
  this.fsw._symlinkPaths.set(absPath, targetPath);
19855
21024
  }
19856
21025
  } else if (stats.isSymbolicLink()) {
19857
- const targetPath = follow ? await (0, import_promises2.realpath)(path33) : path33;
21026
+ const targetPath = follow ? await (0, import_promises2.realpath)(path34) : path34;
19858
21027
  if (this.fsw.closed)
19859
21028
  return;
19860
21029
  const parent = sp.dirname(wh.watchPath);
19861
21030
  this.fsw._getWatchedDir(parent).add(wh.watchPath);
19862
21031
  this.fsw._emit(EV.ADD, wh.watchPath, stats);
19863
- closer = await this._handleDir(parent, stats, initialAdd, depth, path33, wh, targetPath);
21032
+ closer = await this._handleDir(parent, stats, initialAdd, depth, path34, wh, targetPath);
19864
21033
  if (this.fsw.closed)
19865
21034
  return;
19866
21035
  if (targetPath !== void 0) {
19867
- this.fsw._symlinkPaths.set(sp.resolve(path33), targetPath);
21036
+ this.fsw._symlinkPaths.set(sp.resolve(path34), targetPath);
19868
21037
  }
19869
21038
  } else {
19870
21039
  closer = this._handleFile(wh.watchPath, stats, initialAdd);
19871
21040
  }
19872
21041
  ready();
19873
21042
  if (closer)
19874
- this.fsw._addPathCloser(path33, closer);
21043
+ this.fsw._addPathCloser(path34, closer);
19875
21044
  return false;
19876
21045
  } catch (error) {
19877
21046
  if (this.fsw._handleError(error)) {
19878
21047
  ready();
19879
- return path33;
21048
+ return path34;
19880
21049
  }
19881
21050
  }
19882
21051
  }
@@ -19919,24 +21088,24 @@ function createPattern(matcher) {
19919
21088
  }
19920
21089
  return () => false;
19921
21090
  }
19922
- function normalizePath3(path33) {
19923
- if (typeof path33 !== "string")
21091
+ function normalizePath3(path34) {
21092
+ if (typeof path34 !== "string")
19924
21093
  throw new Error("string expected");
19925
- path33 = sp2.normalize(path33);
19926
- path33 = path33.replace(/\\/g, "/");
21094
+ path34 = sp2.normalize(path34);
21095
+ path34 = path34.replace(/\\/g, "/");
19927
21096
  let prepend = false;
19928
- if (path33.startsWith("//"))
21097
+ if (path34.startsWith("//"))
19929
21098
  prepend = true;
19930
- path33 = path33.replace(DOUBLE_SLASH_RE, "/");
21099
+ path34 = path34.replace(DOUBLE_SLASH_RE, "/");
19931
21100
  if (prepend)
19932
- path33 = "/" + path33;
19933
- return path33;
21101
+ path34 = "/" + path34;
21102
+ return path34;
19934
21103
  }
19935
21104
  function matchPatterns(patterns, testString, stats) {
19936
- const path33 = normalizePath3(testString);
21105
+ const path34 = normalizePath3(testString);
19937
21106
  for (let index = 0; index < patterns.length; index++) {
19938
21107
  const pattern = patterns[index];
19939
- if (pattern(path33, stats)) {
21108
+ if (pattern(path34, stats)) {
19940
21109
  return true;
19941
21110
  }
19942
21111
  }
@@ -19974,19 +21143,19 @@ var toUnix = (string) => {
19974
21143
  }
19975
21144
  return str;
19976
21145
  };
19977
- var normalizePathToUnix = (path33) => toUnix(sp2.normalize(toUnix(path33)));
19978
- var normalizeIgnored = (cwd = "") => (path33) => {
19979
- if (typeof path33 === "string") {
19980
- return normalizePathToUnix(sp2.isAbsolute(path33) ? path33 : sp2.join(cwd, path33));
21146
+ var normalizePathToUnix = (path34) => toUnix(sp2.normalize(toUnix(path34)));
21147
+ var normalizeIgnored = (cwd = "") => (path34) => {
21148
+ if (typeof path34 === "string") {
21149
+ return normalizePathToUnix(sp2.isAbsolute(path34) ? path34 : sp2.join(cwd, path34));
19981
21150
  } else {
19982
- return path33;
21151
+ return path34;
19983
21152
  }
19984
21153
  };
19985
- var getAbsolutePath = (path33, cwd) => {
19986
- if (sp2.isAbsolute(path33)) {
19987
- return path33;
21154
+ var getAbsolutePath = (path34, cwd) => {
21155
+ if (sp2.isAbsolute(path34)) {
21156
+ return path34;
19988
21157
  }
19989
- return sp2.join(cwd, path33);
21158
+ return sp2.join(cwd, path34);
19990
21159
  };
19991
21160
  var EMPTY_SET = Object.freeze(/* @__PURE__ */ new Set());
19992
21161
  var DirEntry = class {
@@ -20051,10 +21220,10 @@ var WatchHelper = class {
20051
21220
  dirParts;
20052
21221
  followSymlinks;
20053
21222
  statMethod;
20054
- constructor(path33, follow, fsw) {
21223
+ constructor(path34, follow, fsw) {
20055
21224
  this.fsw = fsw;
20056
- const watchPath = path33;
20057
- this.path = path33 = path33.replace(REPLACER_RE, "");
21225
+ const watchPath = path34;
21226
+ this.path = path34 = path34.replace(REPLACER_RE, "");
20058
21227
  this.watchPath = watchPath;
20059
21228
  this.fullWatchPath = sp2.resolve(watchPath);
20060
21229
  this.dirParts = [];
@@ -20194,20 +21363,20 @@ var FSWatcher = class extends import_node_events.EventEmitter {
20194
21363
  this._closePromise = void 0;
20195
21364
  let paths = unifyPaths(paths_);
20196
21365
  if (cwd) {
20197
- paths = paths.map((path33) => {
20198
- const absPath = getAbsolutePath(path33, cwd);
21366
+ paths = paths.map((path34) => {
21367
+ const absPath = getAbsolutePath(path34, cwd);
20199
21368
  return absPath;
20200
21369
  });
20201
21370
  }
20202
- paths.forEach((path33) => {
20203
- this._removeIgnoredPath(path33);
21371
+ paths.forEach((path34) => {
21372
+ this._removeIgnoredPath(path34);
20204
21373
  });
20205
21374
  this._userIgnored = void 0;
20206
21375
  if (!this._readyCount)
20207
21376
  this._readyCount = 0;
20208
21377
  this._readyCount += paths.length;
20209
- Promise.all(paths.map(async (path33) => {
20210
- const res = await this._nodeFsHandler._addToNodeFs(path33, !_internal, void 0, 0, _origAdd);
21378
+ Promise.all(paths.map(async (path34) => {
21379
+ const res = await this._nodeFsHandler._addToNodeFs(path34, !_internal, void 0, 0, _origAdd);
20211
21380
  if (res)
20212
21381
  this._emitReady();
20213
21382
  return res;
@@ -20229,17 +21398,17 @@ var FSWatcher = class extends import_node_events.EventEmitter {
20229
21398
  return this;
20230
21399
  const paths = unifyPaths(paths_);
20231
21400
  const { cwd } = this.options;
20232
- paths.forEach((path33) => {
20233
- if (!sp2.isAbsolute(path33) && !this._closers.has(path33)) {
21401
+ paths.forEach((path34) => {
21402
+ if (!sp2.isAbsolute(path34) && !this._closers.has(path34)) {
20234
21403
  if (cwd)
20235
- path33 = sp2.join(cwd, path33);
20236
- path33 = sp2.resolve(path33);
21404
+ path34 = sp2.join(cwd, path34);
21405
+ path34 = sp2.resolve(path34);
20237
21406
  }
20238
- this._closePath(path33);
20239
- this._addIgnoredPath(path33);
20240
- if (this._watched.has(path33)) {
21407
+ this._closePath(path34);
21408
+ this._addIgnoredPath(path34);
21409
+ if (this._watched.has(path34)) {
20241
21410
  this._addIgnoredPath({
20242
- path: path33,
21411
+ path: path34,
20243
21412
  recursive: true
20244
21413
  });
20245
21414
  }
@@ -20303,38 +21472,38 @@ var FSWatcher = class extends import_node_events.EventEmitter {
20303
21472
  * @param stats arguments to be passed with event
20304
21473
  * @returns the error if defined, otherwise the value of the FSWatcher instance's `closed` flag
20305
21474
  */
20306
- async _emit(event, path33, stats) {
21475
+ async _emit(event, path34, stats) {
20307
21476
  if (this.closed)
20308
21477
  return;
20309
21478
  const opts = this.options;
20310
21479
  if (isWindows)
20311
- path33 = sp2.normalize(path33);
21480
+ path34 = sp2.normalize(path34);
20312
21481
  if (opts.cwd)
20313
- path33 = sp2.relative(opts.cwd, path33);
20314
- const args = [path33];
21482
+ path34 = sp2.relative(opts.cwd, path34);
21483
+ const args = [path34];
20315
21484
  if (stats != null)
20316
21485
  args.push(stats);
20317
21486
  const awf = opts.awaitWriteFinish;
20318
21487
  let pw;
20319
- if (awf && (pw = this._pendingWrites.get(path33))) {
21488
+ if (awf && (pw = this._pendingWrites.get(path34))) {
20320
21489
  pw.lastChange = /* @__PURE__ */ new Date();
20321
21490
  return this;
20322
21491
  }
20323
21492
  if (opts.atomic) {
20324
21493
  if (event === EVENTS.UNLINK) {
20325
- this._pendingUnlinks.set(path33, [event, ...args]);
21494
+ this._pendingUnlinks.set(path34, [event, ...args]);
20326
21495
  setTimeout(() => {
20327
- this._pendingUnlinks.forEach((entry, path34) => {
21496
+ this._pendingUnlinks.forEach((entry, path35) => {
20328
21497
  this.emit(...entry);
20329
21498
  this.emit(EVENTS.ALL, ...entry);
20330
- this._pendingUnlinks.delete(path34);
21499
+ this._pendingUnlinks.delete(path35);
20331
21500
  });
20332
21501
  }, typeof opts.atomic === "number" ? opts.atomic : 100);
20333
21502
  return this;
20334
21503
  }
20335
- if (event === EVENTS.ADD && this._pendingUnlinks.has(path33)) {
21504
+ if (event === EVENTS.ADD && this._pendingUnlinks.has(path34)) {
20336
21505
  event = EVENTS.CHANGE;
20337
- this._pendingUnlinks.delete(path33);
21506
+ this._pendingUnlinks.delete(path34);
20338
21507
  }
20339
21508
  }
20340
21509
  if (awf && (event === EVENTS.ADD || event === EVENTS.CHANGE) && this._readyEmitted) {
@@ -20352,16 +21521,16 @@ var FSWatcher = class extends import_node_events.EventEmitter {
20352
21521
  this.emitWithAll(event, args);
20353
21522
  }
20354
21523
  };
20355
- this._awaitWriteFinish(path33, awf.stabilityThreshold, event, awfEmit);
21524
+ this._awaitWriteFinish(path34, awf.stabilityThreshold, event, awfEmit);
20356
21525
  return this;
20357
21526
  }
20358
21527
  if (event === EVENTS.CHANGE) {
20359
- const isThrottled = !this._throttle(EVENTS.CHANGE, path33, 50);
21528
+ const isThrottled = !this._throttle(EVENTS.CHANGE, path34, 50);
20360
21529
  if (isThrottled)
20361
21530
  return this;
20362
21531
  }
20363
21532
  if (opts.alwaysStat && stats === void 0 && (event === EVENTS.ADD || event === EVENTS.ADD_DIR || event === EVENTS.CHANGE)) {
20364
- const fullPath = opts.cwd ? sp2.join(opts.cwd, path33) : path33;
21533
+ const fullPath = opts.cwd ? sp2.join(opts.cwd, path34) : path34;
20365
21534
  let stats2;
20366
21535
  try {
20367
21536
  stats2 = await (0, import_promises3.stat)(fullPath);
@@ -20392,23 +21561,23 @@ var FSWatcher = class extends import_node_events.EventEmitter {
20392
21561
  * @param timeout duration of time to suppress duplicate actions
20393
21562
  * @returns tracking object or false if action should be suppressed
20394
21563
  */
20395
- _throttle(actionType, path33, timeout) {
21564
+ _throttle(actionType, path34, timeout) {
20396
21565
  if (!this._throttled.has(actionType)) {
20397
21566
  this._throttled.set(actionType, /* @__PURE__ */ new Map());
20398
21567
  }
20399
21568
  const action = this._throttled.get(actionType);
20400
21569
  if (!action)
20401
21570
  throw new Error("invalid throttle");
20402
- const actionPath = action.get(path33);
21571
+ const actionPath = action.get(path34);
20403
21572
  if (actionPath) {
20404
21573
  actionPath.count++;
20405
21574
  return false;
20406
21575
  }
20407
21576
  let timeoutObject;
20408
21577
  const clear = () => {
20409
- const item = action.get(path33);
21578
+ const item = action.get(path34);
20410
21579
  const count = item ? item.count : 0;
20411
- action.delete(path33);
21580
+ action.delete(path34);
20412
21581
  clearTimeout(timeoutObject);
20413
21582
  if (item)
20414
21583
  clearTimeout(item.timeoutObject);
@@ -20416,7 +21585,7 @@ var FSWatcher = class extends import_node_events.EventEmitter {
20416
21585
  };
20417
21586
  timeoutObject = setTimeout(clear, timeout);
20418
21587
  const thr = { timeoutObject, clear, count: 0 };
20419
- action.set(path33, thr);
21588
+ action.set(path34, thr);
20420
21589
  return thr;
20421
21590
  }
20422
21591
  _incrReadyCount() {
@@ -20430,44 +21599,44 @@ var FSWatcher = class extends import_node_events.EventEmitter {
20430
21599
  * @param event
20431
21600
  * @param awfEmit Callback to be called when ready for event to be emitted.
20432
21601
  */
20433
- _awaitWriteFinish(path33, threshold, event, awfEmit) {
21602
+ _awaitWriteFinish(path34, threshold, event, awfEmit) {
20434
21603
  const awf = this.options.awaitWriteFinish;
20435
21604
  if (typeof awf !== "object")
20436
21605
  return;
20437
21606
  const pollInterval = awf.pollInterval;
20438
21607
  let timeoutHandler;
20439
- let fullPath = path33;
20440
- if (this.options.cwd && !sp2.isAbsolute(path33)) {
20441
- fullPath = sp2.join(this.options.cwd, path33);
21608
+ let fullPath = path34;
21609
+ if (this.options.cwd && !sp2.isAbsolute(path34)) {
21610
+ fullPath = sp2.join(this.options.cwd, path34);
20442
21611
  }
20443
21612
  const now2 = /* @__PURE__ */ new Date();
20444
21613
  const writes = this._pendingWrites;
20445
21614
  function awaitWriteFinishFn(prevStat) {
20446
- (0, import_node_fs2.stat)(fullPath, (err, curStat) => {
20447
- if (err || !writes.has(path33)) {
21615
+ (0, import_node_fs3.stat)(fullPath, (err, curStat) => {
21616
+ if (err || !writes.has(path34)) {
20448
21617
  if (err && err.code !== "ENOENT")
20449
21618
  awfEmit(err);
20450
21619
  return;
20451
21620
  }
20452
21621
  const now3 = Number(/* @__PURE__ */ new Date());
20453
21622
  if (prevStat && curStat.size !== prevStat.size) {
20454
- writes.get(path33).lastChange = now3;
21623
+ writes.get(path34).lastChange = now3;
20455
21624
  }
20456
- const pw = writes.get(path33);
21625
+ const pw = writes.get(path34);
20457
21626
  const df = now3 - pw.lastChange;
20458
21627
  if (df >= threshold) {
20459
- writes.delete(path33);
21628
+ writes.delete(path34);
20460
21629
  awfEmit(void 0, curStat);
20461
21630
  } else {
20462
21631
  timeoutHandler = setTimeout(awaitWriteFinishFn, pollInterval, curStat);
20463
21632
  }
20464
21633
  });
20465
21634
  }
20466
- if (!writes.has(path33)) {
20467
- writes.set(path33, {
21635
+ if (!writes.has(path34)) {
21636
+ writes.set(path34, {
20468
21637
  lastChange: now2,
20469
21638
  cancelWait: () => {
20470
- writes.delete(path33);
21639
+ writes.delete(path34);
20471
21640
  clearTimeout(timeoutHandler);
20472
21641
  return event;
20473
21642
  }
@@ -20478,8 +21647,8 @@ var FSWatcher = class extends import_node_events.EventEmitter {
20478
21647
  /**
20479
21648
  * Determines whether user has asked to ignore this path.
20480
21649
  */
20481
- _isIgnored(path33, stats) {
20482
- if (this.options.atomic && DOT_RE.test(path33))
21650
+ _isIgnored(path34, stats) {
21651
+ if (this.options.atomic && DOT_RE.test(path34))
20483
21652
  return true;
20484
21653
  if (!this._userIgnored) {
20485
21654
  const { cwd } = this.options;
@@ -20489,17 +21658,17 @@ var FSWatcher = class extends import_node_events.EventEmitter {
20489
21658
  const list = [...ignoredPaths.map(normalizeIgnored(cwd)), ...ignored];
20490
21659
  this._userIgnored = anymatch(list, void 0);
20491
21660
  }
20492
- return this._userIgnored(path33, stats);
21661
+ return this._userIgnored(path34, stats);
20493
21662
  }
20494
- _isntIgnored(path33, stat5) {
20495
- return !this._isIgnored(path33, stat5);
21663
+ _isntIgnored(path34, stat5) {
21664
+ return !this._isIgnored(path34, stat5);
20496
21665
  }
20497
21666
  /**
20498
21667
  * Provides a set of common helpers and properties relating to symlink handling.
20499
21668
  * @param path file or directory pattern being watched
20500
21669
  */
20501
- _getWatchHelpers(path33) {
20502
- return new WatchHelper(path33, this.options.followSymlinks, this);
21670
+ _getWatchHelpers(path34) {
21671
+ return new WatchHelper(path34, this.options.followSymlinks, this);
20503
21672
  }
20504
21673
  // Directory helpers
20505
21674
  // -----------------
@@ -20531,63 +21700,63 @@ var FSWatcher = class extends import_node_events.EventEmitter {
20531
21700
  * @param item base path of item/directory
20532
21701
  */
20533
21702
  _remove(directory, item, isDirectory) {
20534
- const path33 = sp2.join(directory, item);
20535
- const fullPath = sp2.resolve(path33);
20536
- isDirectory = isDirectory != null ? isDirectory : this._watched.has(path33) || this._watched.has(fullPath);
20537
- if (!this._throttle("remove", path33, 100))
21703
+ const path34 = sp2.join(directory, item);
21704
+ const fullPath = sp2.resolve(path34);
21705
+ isDirectory = isDirectory != null ? isDirectory : this._watched.has(path34) || this._watched.has(fullPath);
21706
+ if (!this._throttle("remove", path34, 100))
20538
21707
  return;
20539
21708
  if (!isDirectory && this._watched.size === 1) {
20540
21709
  this.add(directory, item, true);
20541
21710
  }
20542
- const wp = this._getWatchedDir(path33);
21711
+ const wp = this._getWatchedDir(path34);
20543
21712
  const nestedDirectoryChildren = wp.getChildren();
20544
- nestedDirectoryChildren.forEach((nested) => this._remove(path33, nested));
21713
+ nestedDirectoryChildren.forEach((nested) => this._remove(path34, nested));
20545
21714
  const parent = this._getWatchedDir(directory);
20546
21715
  const wasTracked = parent.has(item);
20547
21716
  parent.remove(item);
20548
21717
  if (this._symlinkPaths.has(fullPath)) {
20549
21718
  this._symlinkPaths.delete(fullPath);
20550
21719
  }
20551
- let relPath = path33;
21720
+ let relPath = path34;
20552
21721
  if (this.options.cwd)
20553
- relPath = sp2.relative(this.options.cwd, path33);
21722
+ relPath = sp2.relative(this.options.cwd, path34);
20554
21723
  if (this.options.awaitWriteFinish && this._pendingWrites.has(relPath)) {
20555
21724
  const event = this._pendingWrites.get(relPath).cancelWait();
20556
21725
  if (event === EVENTS.ADD)
20557
21726
  return;
20558
21727
  }
20559
- this._watched.delete(path33);
21728
+ this._watched.delete(path34);
20560
21729
  this._watched.delete(fullPath);
20561
21730
  const eventName = isDirectory ? EVENTS.UNLINK_DIR : EVENTS.UNLINK;
20562
- if (wasTracked && !this._isIgnored(path33))
20563
- this._emit(eventName, path33);
20564
- this._closePath(path33);
21731
+ if (wasTracked && !this._isIgnored(path34))
21732
+ this._emit(eventName, path34);
21733
+ this._closePath(path34);
20565
21734
  }
20566
21735
  /**
20567
21736
  * Closes all watchers for a path
20568
21737
  */
20569
- _closePath(path33) {
20570
- this._closeFile(path33);
20571
- const dir = sp2.dirname(path33);
20572
- this._getWatchedDir(dir).remove(sp2.basename(path33));
21738
+ _closePath(path34) {
21739
+ this._closeFile(path34);
21740
+ const dir = sp2.dirname(path34);
21741
+ this._getWatchedDir(dir).remove(sp2.basename(path34));
20573
21742
  }
20574
21743
  /**
20575
21744
  * Closes only file-specific watchers
20576
21745
  */
20577
- _closeFile(path33) {
20578
- const closers = this._closers.get(path33);
21746
+ _closeFile(path34) {
21747
+ const closers = this._closers.get(path34);
20579
21748
  if (!closers)
20580
21749
  return;
20581
21750
  closers.forEach((closer) => closer());
20582
- this._closers.delete(path33);
21751
+ this._closers.delete(path34);
20583
21752
  }
20584
- _addPathCloser(path33, closer) {
21753
+ _addPathCloser(path34, closer) {
20585
21754
  if (!closer)
20586
21755
  return;
20587
- let list = this._closers.get(path33);
21756
+ let list = this._closers.get(path34);
20588
21757
  if (!list) {
20589
21758
  list = [];
20590
- this._closers.set(path33, list);
21759
+ this._closers.set(path34, list);
20591
21760
  }
20592
21761
  list.push(closer);
20593
21762
  }
@@ -20617,11 +21786,11 @@ function watch(paths, options = {}) {
20617
21786
  var chokidar_default = { watch, FSWatcher };
20618
21787
 
20619
21788
  // src/watcher/file-watcher.ts
20620
- var path28 = __toESM(require("path"), 1);
21789
+ var path29 = __toESM(require("path"), 1);
20621
21790
 
20622
21791
  // src/watcher/native-recursive-watcher.ts
20623
- var import_node_fs3 = require("fs");
20624
- var path26 = __toESM(require("path"), 1);
21792
+ var import_node_fs4 = require("fs");
21793
+ var path27 = __toESM(require("path"), 1);
20625
21794
  var NativeRecursiveWatcher = class {
20626
21795
  constructor(root, onChange, options = {}) {
20627
21796
  this.root = root;
@@ -20669,26 +21838,26 @@ var NativeRecursiveWatcher = class {
20669
21838
  toAbsolutePath(filename) {
20670
21839
  if (filename == null) return null;
20671
21840
  const normalizedFilename = typeof filename === "string" ? filename : filename.toString();
20672
- const absolutePath = path26.resolve(this.root, normalizedFilename);
20673
- const relativePath = path26.relative(this.root, absolutePath);
20674
- const outsideRoot = relativePath === ".." || relativePath.startsWith(`..${path26.sep}`) || path26.isAbsolute(relativePath);
21841
+ const absolutePath = path27.resolve(this.root, normalizedFilename);
21842
+ const relativePath = path27.relative(this.root, absolutePath);
21843
+ const outsideRoot = relativePath === ".." || relativePath.startsWith(`..${path27.sep}`) || path27.isAbsolute(relativePath);
20675
21844
  return outsideRoot ? null : absolutePath;
20676
21845
  }
20677
- defaultWatchFactory = (root, listener, options) => (0, import_node_fs3.watch)(root, options, listener);
21846
+ defaultWatchFactory = (root, listener, options) => (0, import_node_fs4.watch)(root, options, listener);
20678
21847
  };
20679
21848
 
20680
21849
  // src/watcher/snapshot.ts
20681
21850
  var fsPromises4 = __toESM(require("fs/promises"), 1);
20682
- var path27 = __toESM(require("path"), 1);
21851
+ var path28 = __toESM(require("path"), 1);
20683
21852
  async function buildFileSnapshotScan(projectRoot, config, configPaths = []) {
20684
- const normalizedProjectRoot = path27.resolve(projectRoot);
21853
+ const normalizedProjectRoot = path28.resolve(projectRoot);
20685
21854
  const ignoreFilter = createIgnoreFilter(normalizedProjectRoot);
20686
21855
  const includePatterns = [...config.include, ...config.additionalInclude ?? []];
20687
21856
  const maxDepth = config.indexing?.maxDepth ?? -1;
20688
21857
  const snapshot = /* @__PURE__ */ new Map();
20689
21858
  const unreadablePrefixes = /* @__PURE__ */ new Set();
20690
21859
  const includeFile = async (filePath) => {
20691
- const normalizedPath3 = path27.resolve(filePath);
21860
+ const normalizedPath3 = path28.resolve(filePath);
20692
21861
  if (!shouldIncludeFile(normalizedPath3, normalizedProjectRoot, includePatterns, config.exclude, ignoreFilter)) return;
20693
21862
  const stat5 = await readStatIfFile(normalizedPath3, unreadablePrefixes);
20694
21863
  if (stat5) snapshot.set(normalizedPath3, { size: stat5.size, mtimeMs: stat5.mtimeMs });
@@ -20700,16 +21869,16 @@ async function buildFileSnapshotScan(projectRoot, config, configPaths = []) {
20700
21869
  } catch (error) {
20701
21870
  if (isMissingFsError(error)) return;
20702
21871
  if (isPermissionFsError(error)) {
20703
- unreadablePrefixes.add(path27.resolve(directoryPath));
21872
+ unreadablePrefixes.add(path28.resolve(directoryPath));
20704
21873
  return;
20705
21874
  }
20706
21875
  throw error;
20707
21876
  }
20708
21877
  for (const entry of entries) {
20709
- const fullPath = path27.join(directoryPath, entry.name);
20710
- const relativePath = path27.relative(normalizedProjectRoot, fullPath);
21878
+ const fullPath = path28.join(directoryPath, entry.name);
21879
+ const relativePath = path28.relative(normalizedProjectRoot, fullPath);
20711
21880
  if (entry.isDirectory()) {
20712
- if (hasFilteredPathSegment(relativePath, path27.sep) || isRestrictedDirectory(relativePath, path27.sep)) continue;
21881
+ if (hasFilteredPathSegment(relativePath, path28.sep) || isRestrictedDirectory(relativePath, path28.sep)) continue;
20713
21882
  if (ignoreFilter.ignores(relativePath)) continue;
20714
21883
  if (maxDepth === -1 || depth < maxDepth) await walk(fullPath, depth + 1);
20715
21884
  } else if (entry.isFile()) {
@@ -20722,19 +21891,19 @@ async function buildFileSnapshotScan(projectRoot, config, configPaths = []) {
20722
21891
  return { entries: snapshot, unreadablePrefixes };
20723
21892
  }
20724
21893
  async function buildFileSnapshotForPathScan(projectRoot, config, configPaths, targetPath) {
20725
- const normalizedProjectRoot = path27.resolve(projectRoot);
20726
- const normalizedTargetPath = path27.resolve(targetPath);
21894
+ const normalizedProjectRoot = path28.resolve(projectRoot);
21895
+ const normalizedTargetPath = path28.resolve(targetPath);
20727
21896
  if (!isWithinPath(normalizedProjectRoot, normalizedTargetPath)) {
20728
21897
  return { entries: /* @__PURE__ */ new Map(), unreadablePrefixes: /* @__PURE__ */ new Set() };
20729
21898
  }
20730
21899
  const ignoreFilter = createIgnoreFilter(normalizedProjectRoot);
20731
21900
  const includePatterns = [...config.include, ...config.additionalInclude ?? []];
20732
21901
  const maxDepth = config.indexing?.maxDepth ?? -1;
20733
- const explicitConfigPaths = new Set(configPaths.map((configPath) => path27.resolve(configPath)));
21902
+ const explicitConfigPaths = new Set(configPaths.map((configPath) => path28.resolve(configPath)));
20734
21903
  const snapshot = /* @__PURE__ */ new Map();
20735
21904
  const unreadablePrefixes = /* @__PURE__ */ new Set();
20736
21905
  const includeFile = async (filePath) => {
20737
- const normalizedPath3 = path27.resolve(filePath);
21906
+ const normalizedPath3 = path28.resolve(filePath);
20738
21907
  if (!explicitConfigPaths.has(normalizedPath3) && !shouldIncludeFile(
20739
21908
  normalizedPath3,
20740
21909
  normalizedProjectRoot,
@@ -20752,16 +21921,16 @@ async function buildFileSnapshotForPathScan(projectRoot, config, configPaths, ta
20752
21921
  } catch (error) {
20753
21922
  if (isMissingFsError(error)) return;
20754
21923
  if (isPermissionFsError(error)) {
20755
- unreadablePrefixes.add(path27.resolve(directoryPath));
21924
+ unreadablePrefixes.add(path28.resolve(directoryPath));
20756
21925
  return;
20757
21926
  }
20758
21927
  throw error;
20759
21928
  }
20760
21929
  for (const entry of entries) {
20761
- const fullPath = path27.join(directoryPath, entry.name);
20762
- const relativePath = path27.relative(normalizedProjectRoot, fullPath);
21930
+ const fullPath = path28.join(directoryPath, entry.name);
21931
+ const relativePath = path28.relative(normalizedProjectRoot, fullPath);
20763
21932
  if (entry.isDirectory()) {
20764
- if (hasFilteredPathSegment(relativePath, path27.sep) || isRestrictedDirectory(relativePath, path27.sep)) continue;
21933
+ if (hasFilteredPathSegment(relativePath, path28.sep) || isRestrictedDirectory(relativePath, path28.sep)) continue;
20765
21934
  if (ignoreFilter.ignores(relativePath)) continue;
20766
21935
  if (maxDepth === -1 || depth < maxDepth) await walk(fullPath, depth + 1);
20767
21936
  } else if (entry.isFile()) {
@@ -20785,7 +21954,7 @@ function completeFileSnapshot(previous, scan) {
20785
21954
  return completed;
20786
21955
  }
20787
21956
  async function includeExplicitConfigPaths(snapshot, unreadablePrefixes, configPaths) {
20788
- for (const configPath of [...new Set(configPaths.map((value) => path27.resolve(value)))]) {
21957
+ for (const configPath of [...new Set(configPaths.map((value) => path28.resolve(value)))]) {
20789
21958
  if (snapshot.has(configPath)) continue;
20790
21959
  const stat5 = await readStatIfFile(configPath, unreadablePrefixes);
20791
21960
  if (stat5) snapshot.set(configPath, { size: stat5.size, mtimeMs: stat5.mtimeMs });
@@ -20795,12 +21964,12 @@ async function includeExplicitConfigPathsInPath(snapshot, unreadablePrefixes, co
20795
21964
  await includeExplicitConfigPaths(
20796
21965
  snapshot,
20797
21966
  unreadablePrefixes,
20798
- configPaths.filter((configPath) => isWithinPath(targetPath, path27.resolve(configPath)))
21967
+ configPaths.filter((configPath) => isWithinPath(targetPath, path28.resolve(configPath)))
20799
21968
  );
20800
21969
  }
20801
21970
  function isWithinPath(parentPath, childPath) {
20802
- const relativePath = path27.relative(parentPath, childPath);
20803
- return relativePath === "" || !relativePath.startsWith(`..${path27.sep}`) && relativePath !== ".." && !path27.isAbsolute(relativePath);
21971
+ const relativePath = path28.relative(parentPath, childPath);
21972
+ return relativePath === "" || !relativePath.startsWith(`..${path28.sep}`) && relativePath !== ".." && !path28.isAbsolute(relativePath);
20804
21973
  }
20805
21974
  async function readStatIfFile(filePath, unreadablePrefixes) {
20806
21975
  try {
@@ -20809,7 +21978,7 @@ async function readStatIfFile(filePath, unreadablePrefixes) {
20809
21978
  } catch (error) {
20810
21979
  if (isMissingFsError(error)) return null;
20811
21980
  if (isPermissionFsError(error)) {
20812
- unreadablePrefixes.add(path27.resolve(filePath));
21981
+ unreadablePrefixes.add(path28.resolve(filePath));
20813
21982
  return null;
20814
21983
  }
20815
21984
  throw error;
@@ -20946,8 +22115,8 @@ var FileWatcher = class {
20946
22115
  this.createWatcher();
20947
22116
  }
20948
22117
  resetReady() {
20949
- this.readyPromise = new Promise((resolve20) => {
20950
- this.resolveReady = resolve20;
22118
+ this.readyPromise = new Promise((resolve21) => {
22119
+ this.resolveReady = resolve21;
20951
22120
  });
20952
22121
  this.startupReadySignals = 1;
20953
22122
  }
@@ -20978,7 +22147,7 @@ var FileWatcher = class {
20978
22147
  const resolvedWatchTargets = watchTargets ?? this.getFullChokidarWatchTargets();
20979
22148
  const watcherOptions = {
20980
22149
  ignored: (filePath) => {
20981
- const relativePath = path28.relative(this.projectRoot, filePath);
22150
+ const relativePath = path29.relative(this.projectRoot, filePath);
20982
22151
  if (!relativePath) return false;
20983
22152
  if (this.isProjectConfigPathOrAncestor(relativePath)) {
20984
22153
  return false;
@@ -20986,10 +22155,10 @@ var FileWatcher = class {
20986
22155
  if (this.isOutsideProjectPath(relativePath)) {
20987
22156
  return true;
20988
22157
  }
20989
- if (hasFilteredPathSegment(relativePath, path28.sep)) {
22158
+ if (hasFilteredPathSegment(relativePath, path29.sep)) {
20990
22159
  return true;
20991
22160
  }
20992
- if (isRestrictedDirectory(relativePath, path28.sep)) {
22161
+ if (isRestrictedDirectory(relativePath, path29.sep)) {
20993
22162
  return true;
20994
22163
  }
20995
22164
  if (ignoreFilter.ignores(relativePath)) {
@@ -21080,13 +22249,13 @@ var FileWatcher = class {
21080
22249
  getExternalConfigWatchTargets() {
21081
22250
  return [...new Set(
21082
22251
  this.projectConfigPaths.filter((projectConfigPath) => {
21083
- const relativeConfigPath = path28.relative(this.projectRoot, projectConfigPath);
22252
+ const relativeConfigPath = path29.relative(this.projectRoot, projectConfigPath);
21084
22253
  return this.isOutsideProjectPath(relativeConfigPath);
21085
22254
  }).map((projectConfigPath) => {
21086
22255
  if ((0, import_fs19.existsSync)(projectConfigPath)) {
21087
22256
  return projectConfigPath;
21088
22257
  }
21089
- return this.getNearestExistingDirectory(path28.dirname(projectConfigPath));
22258
+ return this.getNearestExistingDirectory(path29.dirname(projectConfigPath));
21090
22259
  })
21091
22260
  )];
21092
22261
  }
@@ -21148,7 +22317,7 @@ var FileWatcher = class {
21148
22317
  }
21149
22318
  scheduleNativeReconciliation(generation, filePath) {
21150
22319
  if (!this.isCurrentNativeSetup(generation)) return;
21151
- const requiresFullReconciliation = filePath === path28.join(this.projectRoot, ".gitignore");
22320
+ const requiresFullReconciliation = filePath === path29.join(this.projectRoot, ".gitignore");
21152
22321
  const invalidatedPath = requiresFullReconciliation ? null : filePath;
21153
22322
  this.nativeInvalidatedPaths.set(invalidatedPath, invalidatedPath !== null);
21154
22323
  if (this.nativeReconcileTimer) {
@@ -21243,23 +22412,23 @@ var FileWatcher = class {
21243
22412
  this.scheduleFlush();
21244
22413
  }
21245
22414
  isProjectConfigPath(filePath) {
21246
- const relativePath = path28.relative(this.projectRoot, filePath);
21247
- const normalizedRelativePath = path28.normalize(relativePath);
22415
+ const relativePath = path29.relative(this.projectRoot, filePath);
22416
+ const normalizedRelativePath = path29.normalize(relativePath);
21248
22417
  return this.getProjectConfigRelativePaths().some((configPath) => configPath === normalizedRelativePath);
21249
22418
  }
21250
22419
  isProjectConfigPathOrAncestor(relativePath) {
21251
- const normalizedRelativePath = path28.normalize(relativePath);
22420
+ const normalizedRelativePath = path29.normalize(relativePath);
21252
22421
  return this.getProjectConfigRelativePaths().some(
21253
- (configPath) => configPath === normalizedRelativePath || configPath.startsWith(`${normalizedRelativePath}${path28.sep}`)
22422
+ (configPath) => configPath === normalizedRelativePath || configPath.startsWith(`${normalizedRelativePath}${path29.sep}`)
21254
22423
  );
21255
22424
  }
21256
22425
  isOutsideProjectPath(relativePath) {
21257
- return relativePath === ".." || relativePath.startsWith(`..${path28.sep}`) || path28.isAbsolute(relativePath);
22426
+ return relativePath === ".." || relativePath.startsWith(`..${path29.sep}`) || path29.isAbsolute(relativePath);
21258
22427
  }
21259
22428
  getNearestExistingDirectory(directoryPath) {
21260
22429
  let candidate = directoryPath;
21261
22430
  while (!(0, import_fs19.existsSync)(candidate)) {
21262
- const parent = path28.dirname(candidate);
22431
+ const parent = path29.dirname(candidate);
21263
22432
  if (parent === candidate) break;
21264
22433
  candidate = parent;
21265
22434
  }
@@ -21267,7 +22436,7 @@ var FileWatcher = class {
21267
22436
  }
21268
22437
  getProjectConfigRelativePaths() {
21269
22438
  return this.projectConfigPaths.map(
21270
- (configPath) => path28.normalize(path28.relative(this.projectRoot, configPath))
22439
+ (configPath) => path29.normalize(path29.relative(this.projectRoot, configPath))
21271
22440
  );
21272
22441
  }
21273
22442
  getConfigPathStates() {
@@ -21325,7 +22494,7 @@ var FileWatcher = class {
21325
22494
  return;
21326
22495
  }
21327
22496
  const changes = Array.from(this.pendingChanges.entries()).map(
21328
- ([path33, type]) => ({ path: path33, type })
22497
+ ([path34, type]) => ({ path: path34, type })
21329
22498
  );
21330
22499
  this.pendingChanges.clear();
21331
22500
  try {
@@ -21371,7 +22540,7 @@ var FileWatcher = class {
21371
22540
  };
21372
22541
 
21373
22542
  // src/watcher/git-head-watcher.ts
21374
- var path29 = __toESM(require("path"), 1);
22543
+ var path30 = __toESM(require("path"), 1);
21375
22544
  var GitHeadWatcher = class {
21376
22545
  watcher = null;
21377
22546
  projectRoot;
@@ -21393,13 +22562,13 @@ var GitHeadWatcher = class {
21393
22562
  this.readyPromise = Promise.resolve();
21394
22563
  return;
21395
22564
  }
21396
- this.readyPromise = new Promise((resolve20) => {
21397
- this.resolveReady = resolve20;
22565
+ this.readyPromise = new Promise((resolve21) => {
22566
+ this.resolveReady = resolve21;
21398
22567
  });
21399
22568
  this.onBranchChange = handler;
21400
22569
  this.currentBranch = getCurrentBranch(this.projectRoot);
21401
22570
  const headPath = getHeadPath(this.projectRoot);
21402
- const refsPath = path29.join(this.projectRoot, ".git", "refs", "heads");
22571
+ const refsPath = path30.join(this.projectRoot, ".git", "refs", "heads");
21403
22572
  this.watcher = chokidar_default.watch([headPath, refsPath], {
21404
22573
  persistent: true,
21405
22574
  ignoreInitial: true,
@@ -21467,7 +22636,9 @@ var GitHeadWatcher = class {
21467
22636
  function createWatcherWithIndexer(getIndexer, projectRoot, config, host, options = {}) {
21468
22637
  const fileWatcher = new FileWatcher(projectRoot, config, host, options);
21469
22638
  const configPaths = getConfigPaths(projectRoot, host, options);
21470
- configureAutoIndex(projectRoot, host, parseConfig(config), getIndexer);
22639
+ configureAutoIndex(projectRoot, host, parseConfig(config), getIndexer, {
22640
+ synchronizeBackgroundWorker: false
22641
+ });
21471
22642
  let stopped = false;
21472
22643
  const requestReindex = () => {
21473
22644
  if (stopped) return;
@@ -21487,7 +22658,9 @@ function createWatcherWithIndexer(getIndexer, projectRoot, config, host, options
21487
22658
  const parsedConfig = options.configPath ? parseConfig(loadConfigFile(options.configPath)) : void 0;
21488
22659
  const refreshedConfig = refreshIndexerForDirectory(projectRoot, host, parsedConfig);
21489
22660
  if (refreshedConfig) {
21490
- configureAutoIndex(projectRoot, host, refreshedConfig, getIndexer);
22661
+ configureAutoIndex(projectRoot, host, refreshedConfig, getIndexer, {
22662
+ synchronizeBackgroundWorker: false
22663
+ });
21491
22664
  }
21492
22665
  }
21493
22666
  requestReindex();
@@ -21535,7 +22708,7 @@ function getConfigPaths(projectRoot, host, options) {
21535
22708
 
21536
22709
  // src/tools/visualize/activity.ts
21537
22710
  var import_child_process5 = require("child_process");
21538
- var path30 = __toESM(require("path"), 1);
22711
+ var path31 = __toESM(require("path"), 1);
21539
22712
  function attachRecentActivity(data, projectRoot) {
21540
22713
  const activity = readGitActivity(projectRoot);
21541
22714
  const changes = activity.size > 0 ? buildGitChanges(data, activity, projectRoot) : buildGraphChanges(data);
@@ -21697,7 +22870,7 @@ function normalizePath4(filePath) {
21697
22870
  return filePath.replace(/\\/g, "/");
21698
22871
  }
21699
22872
  function toGitRelativePath(projectRoot, filePath) {
21700
- const relativePath = path30.isAbsolute(filePath) ? path30.relative(projectRoot, filePath) : filePath;
22873
+ const relativePath = path31.isAbsolute(filePath) ? path31.relative(projectRoot, filePath) : filePath;
21701
22874
  return normalizePath4(relativePath);
21702
22875
  }
21703
22876
 
@@ -21955,7 +23128,7 @@ render();
21955
23128
  }
21956
23129
 
21957
23130
  // src/tools/visualize/transform.ts
21958
- var path31 = __toESM(require("path"), 1);
23131
+ var path32 = __toESM(require("path"), 1);
21959
23132
 
21960
23133
  // src/tools/visualize/modules.ts
21961
23134
  var MAX_MODULES = 18;
@@ -22215,7 +23388,7 @@ function transformForVisualization(symbols, edges, options = {}) {
22215
23388
  filePath: s.filePath,
22216
23389
  kind: s.kind,
22217
23390
  line: s.startLine,
22218
- directory: path31.dirname(s.filePath),
23391
+ directory: path32.dirname(s.filePath),
22219
23392
  moduleId: "",
22220
23393
  moduleLabel: ""
22221
23394
  }));
@@ -22243,9 +23416,9 @@ function parseArgs(argv) {
22243
23416
  let host = "opencode";
22244
23417
  for (let i = 2; i < argv.length; i++) {
22245
23418
  if (argv[i] === "--project" && argv[i + 1]) {
22246
- project = path32.resolve(argv[++i]);
23419
+ project = path33.resolve(argv[++i]);
22247
23420
  } else if (argv[i] === "--config" && argv[i + 1]) {
22248
- config = path32.resolve(argv[++i]);
23421
+ config = path33.resolve(argv[++i]);
22249
23422
  } else if (argv[i] === "--host" && argv[i + 1]) {
22250
23423
  host = parseHostMode(argv[++i]);
22251
23424
  } else if (argv[i] === "--host") {
@@ -22260,6 +23433,7 @@ function parseIndexArgs(argv, cwd) {
22260
23433
  let config;
22261
23434
  let force = false;
22262
23435
  let estimateOnly = false;
23436
+ let dryRun = false;
22263
23437
  let verbose = false;
22264
23438
  for (let i = 0; i < argv.length; i += 1) {
22265
23439
  const arg = argv[i];
@@ -22272,7 +23446,7 @@ function parseIndexArgs(argv, cwd) {
22272
23446
  if (!arg.startsWith("--project=")) {
22273
23447
  i += 1;
22274
23448
  }
22275
- project = path32.resolve(cwd, value);
23449
+ project = path33.resolve(cwd, value);
22276
23450
  continue;
22277
23451
  }
22278
23452
  if (arg === "--config" || arg.startsWith("--config=")) {
@@ -22283,7 +23457,7 @@ function parseIndexArgs(argv, cwd) {
22283
23457
  if (!arg.startsWith("--config=")) {
22284
23458
  i += 1;
22285
23459
  }
22286
- config = path32.resolve(cwd, value);
23460
+ config = path33.resolve(cwd, value);
22287
23461
  continue;
22288
23462
  }
22289
23463
  if (arg === "--host" || arg.startsWith("--host=")) {
@@ -22297,13 +23471,16 @@ function parseIndexArgs(argv, cwd) {
22297
23471
  host = parseHostMode(value);
22298
23472
  continue;
22299
23473
  }
22300
- if (arg === "--force" || arg === "--estimate-only" || arg === "--verbose") {
23474
+ if (arg === "--force" || arg === "--estimate-only" || arg === "--dry-run" || arg === "--verbose") {
22301
23475
  if (arg === "--force") {
22302
23476
  force = true;
22303
23477
  }
22304
23478
  if (arg === "--estimate-only") {
22305
23479
  estimateOnly = true;
22306
23480
  }
23481
+ if (arg === "--dry-run") {
23482
+ dryRun = true;
23483
+ }
22307
23484
  if (arg === "--verbose") {
22308
23485
  verbose = true;
22309
23486
  }
@@ -22314,7 +23491,7 @@ function parseIndexArgs(argv, cwd) {
22314
23491
  }
22315
23492
  throw new Error(`Unknown index option: ${arg}`);
22316
23493
  }
22317
- return { project, host, config, force, estimateOnly, verbose };
23494
+ return { project, host, config, force, estimateOnly, dryRun, verbose };
22318
23495
  }
22319
23496
  function loadCliRawConfig(args) {
22320
23497
  return args.config ? loadConfigFile(args.config) : loadMergedConfig(args.project, args.host);
@@ -22331,6 +23508,7 @@ Options:
22331
23508
  --config <path> Explicit JSON config path
22332
23509
  --force Rebuild index even if already up to date
22333
23510
  --estimate-only Estimate indexing cost only
23511
+ --dry-run Parse only; report the exact embedding token total without indexing
22334
23512
  --verbose Include detailed final index statistics
22335
23513
  --help Show this message
22336
23514
 
@@ -22349,7 +23527,7 @@ function parseVisualizeArgs(argv, cwd) {
22349
23527
  for (let i = 0; i < argv.length; i++) {
22350
23528
  const arg = argv[i];
22351
23529
  if (arg === "--project" && argv[i + 1]) {
22352
- project = path32.resolve(argv[++i]);
23530
+ project = path33.resolve(argv[++i]);
22353
23531
  } else if (arg === "--max" && argv[i + 1]) {
22354
23532
  maxNodes = Number(argv[++i]);
22355
23533
  } else if (arg.startsWith("--max=") || arg.startsWith("max=")) {
@@ -22386,7 +23564,7 @@ async function handleVisualizeCommand(argv, cwd) {
22386
23564
  console.error("No connected symbols found. Retry with: npm run visualize -- orphans");
22387
23565
  return 1;
22388
23566
  }
22389
- const outputPath = path32.join(os8.tmpdir(), `call-graph-${Date.now()}.html`);
23567
+ const outputPath = path33.join(os9.tmpdir(), `call-graph-${Date.now()}.html`);
22390
23568
  (0, import_fs20.writeFileSync)(outputPath, generateVisualizationHtml(vizData), "utf-8");
22391
23569
  console.log(`Temporal call graph visualization generated: ${outputPath}`);
22392
23570
  console.log(`Nodes: ${vizData.nodes.length} | Edges: ${vizData.edges.length}`);
@@ -22418,7 +23596,6 @@ async function runMcpCli(argv) {
22418
23596
  const config = parseConfig(rawConfig);
22419
23597
  const server = createMcpServer(args.project, config, args.host);
22420
23598
  const transport = new import_stdio.StdioServerTransport();
22421
- let watcher = null;
22422
23599
  let shutdownPromise;
22423
23600
  const onServerClose = server.server.onclose;
22424
23601
  const shutdown = () => {
@@ -22432,16 +23609,19 @@ async function runMcpCli(argv) {
22432
23609
  shutdownPromise = (async () => {
22433
23610
  let exitCode = 0;
22434
23611
  try {
22435
- await watcher?.stop();
22436
- } catch (error) {
22437
- exitCode = 1;
22438
- console.error("Failed to stop MCP file watcher cleanly:", error);
22439
- }
22440
- try {
22441
- await stopAutoIndex(args.project, args.host);
23612
+ await stopBackgroundWorker(args.project, args.host);
22442
23613
  } catch (error) {
22443
23614
  exitCode = 1;
22444
- console.error("Failed to stop automatic indexing cleanly:", error);
23615
+ if (error instanceof BackgroundWorkerStopError) {
23616
+ if (error.watcherError !== void 0) {
23617
+ console.error("Failed to stop MCP file watcher cleanly:", error.watcherError);
23618
+ }
23619
+ if (error.autoIndexError !== void 0) {
23620
+ console.error("Failed to stop automatic indexing cleanly:", error.autoIndexError);
23621
+ }
23622
+ } else {
23623
+ console.error("Failed to stop automatic indexing cleanly:", error);
23624
+ }
22445
23625
  }
22446
23626
  try {
22447
23627
  await server.close();
@@ -22470,19 +23650,25 @@ async function runMcpCli(argv) {
22470
23650
  process.once("SIGHUP", requestShutdown);
22471
23651
  process.once("SIGTERM", requestShutdown);
22472
23652
  }
22473
- await server.connect(transport);
22474
- if (shutdownPromise) return;
22475
23653
  const isHomeDir = isHomeDirectory(args.project);
22476
23654
  const isValidProject = !isHomeDir && (!config.indexing.requireProjectMarker || hasProjectMarker(args.project));
22477
- if (config.indexing.watchFiles && isValidProject) {
22478
- watcher = createWatcherWithIndexer(
22479
- () => getIndexerForProject(args.project, args.host),
22480
- args.project,
22481
- config,
22482
- args.host,
22483
- args.config ? { configPath: args.config } : {}
22484
- );
22485
- }
23655
+ const watcherFactoryForConfig = (refreshedConfig) => refreshedConfig.indexing.watchFiles && !isHomeDirectory(args.project) && (!refreshedConfig.indexing.requireProjectMarker || hasProjectMarker(args.project)) ? () => createWatcherWithIndexer(
23656
+ () => getIndexerForProject(args.project, args.host),
23657
+ args.project,
23658
+ refreshedConfig,
23659
+ args.host,
23660
+ args.config ? { configPath: args.config } : {}
23661
+ ) : null;
23662
+ await server.connect(transport);
23663
+ if (shutdownPromise) return;
23664
+ await attachMcpBackgroundWatcher(
23665
+ args.project,
23666
+ config,
23667
+ args.host,
23668
+ config.indexing.watchFiles && isValidProject ? watcherFactoryForConfig(config) : null,
23669
+ watcherFactoryForConfig
23670
+ );
23671
+ if (shutdownPromise) return;
22486
23672
  }
22487
23673
  function printIndexProgress(onProgress, title, metadata) {
22488
23674
  const details = Object.entries(metadata).filter(([, value]) => value !== void 0 && value !== null).map(([key, value]) => `${key}=${isSensitiveKey(key) ? "[REDACTED]" : String(value)}`).join(" ");
@@ -22526,6 +23712,7 @@ async function handleIndexCommand(argv, cwd, deps = {}) {
22526
23712
  const indexArgs = {
22527
23713
  force: parsedArgs.force,
22528
23714
  estimateOnly: parsedArgs.estimateOnly,
23715
+ dryRun: parsedArgs.dryRun,
22529
23716
  verbose: parsedArgs.verbose
22530
23717
  };
22531
23718
  const result = await runIndex(parsedArgs.project, parsedArgs.host, indexArgs, (title, metadata) => {