opencode-codebase-index 0.25.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.js CHANGED
@@ -491,7 +491,7 @@ var require_ignore = __commonJS({
491
491
  // path matching.
492
492
  // - check `string` either `MODE_IGNORE` or `MODE_CHECK_IGNORE`
493
493
  // @returns {TestResult} true if a file is ignored
494
- test(path33, checkUnignored, mode) {
494
+ test(path34, checkUnignored, mode) {
495
495
  let ignored = false;
496
496
  let unignored = false;
497
497
  let matchedRule;
@@ -500,7 +500,7 @@ var require_ignore = __commonJS({
500
500
  if (unignored === negative && ignored !== unignored || negative && !ignored && !unignored && !checkUnignored) {
501
501
  return;
502
502
  }
503
- const matched = rule[mode].test(path33);
503
+ const matched = rule[mode].test(path34);
504
504
  if (!matched) {
505
505
  return;
506
506
  }
@@ -521,17 +521,17 @@ var require_ignore = __commonJS({
521
521
  var throwError = (message, Ctor) => {
522
522
  throw new Ctor(message);
523
523
  };
524
- var checkPath = (path33, originalPath, doThrow) => {
525
- if (!isString(path33)) {
524
+ var checkPath = (path34, originalPath, doThrow) => {
525
+ if (!isString(path34)) {
526
526
  return doThrow(
527
527
  `path must be a string, but got \`${originalPath}\``,
528
528
  TypeError
529
529
  );
530
530
  }
531
- if (!path33) {
531
+ if (!path34) {
532
532
  return doThrow(`path must not be empty`, TypeError);
533
533
  }
534
- if (checkPath.isNotRelative(path33)) {
534
+ if (checkPath.isNotRelative(path34)) {
535
535
  const r = "`path.relative()`d";
536
536
  return doThrow(
537
537
  `path should be a ${r} string, but got "${originalPath}"`,
@@ -540,7 +540,7 @@ var require_ignore = __commonJS({
540
540
  }
541
541
  return true;
542
542
  };
543
- var isNotRelative = (path33) => REGEX_TEST_INVALID_PATH.test(path33);
543
+ var isNotRelative = (path34) => REGEX_TEST_INVALID_PATH.test(path34);
544
544
  checkPath.isNotRelative = isNotRelative;
545
545
  checkPath.convert = (p) => p;
546
546
  var Ignore2 = class {
@@ -570,19 +570,19 @@ var require_ignore = __commonJS({
570
570
  }
571
571
  // @returns {TestResult}
572
572
  _test(originalPath, cache, checkUnignored, slices) {
573
- const path33 = originalPath && checkPath.convert(originalPath);
573
+ const path34 = originalPath && checkPath.convert(originalPath);
574
574
  checkPath(
575
- path33,
575
+ path34,
576
576
  originalPath,
577
577
  this._strictPathCheck ? throwError : RETURN_FALSE
578
578
  );
579
- return this._t(path33, cache, checkUnignored, slices);
579
+ return this._t(path34, cache, checkUnignored, slices);
580
580
  }
581
- checkIgnore(path33) {
582
- if (!REGEX_TEST_TRAILING_SLASH.test(path33)) {
583
- return this.test(path33);
581
+ checkIgnore(path34) {
582
+ if (!REGEX_TEST_TRAILING_SLASH.test(path34)) {
583
+ return this.test(path34);
584
584
  }
585
- const slices = path33.split(SLASH2).filter(Boolean);
585
+ const slices = path34.split(SLASH2).filter(Boolean);
586
586
  slices.pop();
587
587
  if (slices.length) {
588
588
  const parent = this._t(
@@ -595,18 +595,18 @@ var require_ignore = __commonJS({
595
595
  return parent;
596
596
  }
597
597
  }
598
- return this._rules.test(path33, false, MODE_CHECK_IGNORE);
598
+ return this._rules.test(path34, false, MODE_CHECK_IGNORE);
599
599
  }
600
- _t(path33, cache, checkUnignored, slices) {
601
- if (path33 in cache) {
602
- return cache[path33];
600
+ _t(path34, cache, checkUnignored, slices) {
601
+ if (path34 in cache) {
602
+ return cache[path34];
603
603
  }
604
604
  if (!slices) {
605
- slices = path33.split(SLASH2).filter(Boolean);
605
+ slices = path34.split(SLASH2).filter(Boolean);
606
606
  }
607
607
  slices.pop();
608
608
  if (!slices.length) {
609
- return cache[path33] = this._rules.test(path33, checkUnignored, MODE_IGNORE);
609
+ return cache[path34] = this._rules.test(path34, checkUnignored, MODE_IGNORE);
610
610
  }
611
611
  const parent = this._t(
612
612
  slices.join(SLASH2) + SLASH2,
@@ -614,29 +614,29 @@ var require_ignore = __commonJS({
614
614
  checkUnignored,
615
615
  slices
616
616
  );
617
- return cache[path33] = parent.ignored ? parent : this._rules.test(path33, checkUnignored, MODE_IGNORE);
617
+ return cache[path34] = parent.ignored ? parent : this._rules.test(path34, checkUnignored, MODE_IGNORE);
618
618
  }
619
- ignores(path33) {
620
- return this._test(path33, this._ignoreCache, false).ignored;
619
+ ignores(path34) {
620
+ return this._test(path34, this._ignoreCache, false).ignored;
621
621
  }
622
622
  createFilter() {
623
- return (path33) => !this.ignores(path33);
623
+ return (path34) => !this.ignores(path34);
624
624
  }
625
625
  filter(paths) {
626
626
  return makeArray(paths).filter(this.createFilter());
627
627
  }
628
628
  // @returns {TestResult}
629
- test(path33) {
630
- return this._test(path33, this._testCache, true);
629
+ test(path34) {
630
+ return this._test(path34, this._testCache, true);
631
631
  }
632
632
  };
633
633
  var factory = (options) => new Ignore2(options);
634
- var isPathValid = (path33) => checkPath(path33 && checkPath.convert(path33), path33, RETURN_FALSE);
634
+ var isPathValid = (path34) => checkPath(path34 && checkPath.convert(path34), path34, RETURN_FALSE);
635
635
  var setupWindows = () => {
636
636
  const makePosix = (str) => /^\\\\\?\\/.test(str) || /["<>|\u0000-\u001F]+/u.test(str) ? str : str.replace(/\\/g, "/");
637
637
  checkPath.convert = makePosix;
638
638
  const REGEX_TEST_WINDOWS_PATH_ABSOLUTE = /^[a-z]:\//i;
639
- checkPath.isNotRelative = (path33) => REGEX_TEST_WINDOWS_PATH_ABSOLUTE.test(path33) || isNotRelative(path33);
639
+ checkPath.isNotRelative = (path34) => REGEX_TEST_WINDOWS_PATH_ABSOLUTE.test(path34) || isNotRelative(path34);
640
640
  };
641
641
  if (
642
642
  // Detect `process` so that it can run in browsers.
@@ -653,9 +653,9 @@ var require_ignore = __commonJS({
653
653
 
654
654
  // src/adapters/mcp/cli.ts
655
655
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
656
- import { realpathSync as realpathSync6, writeFileSync as writeFileSync6 } from "fs";
657
- import * as os8 from "os";
658
- import * as path32 from "path";
656
+ import { realpathSync as realpathSync7, writeFileSync as writeFileSync7 } from "fs";
657
+ import * as os9 from "os";
658
+ import * as path33 from "path";
659
659
  import { fileURLToPath as fileURLToPath2 } from "url";
660
660
 
661
661
  // src/config/constants.ts
@@ -1183,9 +1183,9 @@ import { mkdirSync, readFileSync, writeFileSync } from "fs";
1183
1183
  import * as path from "path";
1184
1184
 
1185
1185
  // src/eval/report-formatters.ts
1186
- function assertFiniteNumber(value, path33) {
1186
+ function assertFiniteNumber(value, path34) {
1187
1187
  if (typeof value !== "number" || Number.isNaN(value) || !Number.isFinite(value)) {
1188
- throw new Error(`${path33} must be a finite number`);
1188
+ throw new Error(`${path34} must be a finite number`);
1189
1189
  }
1190
1190
  return value;
1191
1191
  }
@@ -1424,8 +1424,8 @@ function buildPerQueryArtifact(perQuery) {
1424
1424
 
1425
1425
  // src/eval/runner.ts
1426
1426
  import * as crypto2 from "crypto";
1427
- import { existsSync as existsSync14 } from "fs";
1428
- import * as path23 from "path";
1427
+ import { existsSync as existsSync15 } from "fs";
1428
+ import * as path24 from "path";
1429
1429
  import { performance as performance3 } from "perf_hooks";
1430
1430
 
1431
1431
  // src/indexer/index.ts
@@ -1457,7 +1457,7 @@ function pTimeout(promise, options) {
1457
1457
  } = options;
1458
1458
  let timer;
1459
1459
  let abortHandler;
1460
- const wrappedPromise = new Promise((resolve20, reject) => {
1460
+ const wrappedPromise = new Promise((resolve21, reject) => {
1461
1461
  if (typeof milliseconds !== "number" || Math.sign(milliseconds) !== 1) {
1462
1462
  throw new TypeError(`Expected \`milliseconds\` to be a positive number, got \`${milliseconds}\``);
1463
1463
  }
@@ -1471,7 +1471,7 @@ function pTimeout(promise, options) {
1471
1471
  };
1472
1472
  signal.addEventListener("abort", abortHandler, { once: true });
1473
1473
  }
1474
- promise.then(resolve20, reject);
1474
+ promise.then(resolve21, reject);
1475
1475
  if (milliseconds === Number.POSITIVE_INFINITY) {
1476
1476
  return;
1477
1477
  }
@@ -1479,7 +1479,7 @@ function pTimeout(promise, options) {
1479
1479
  timer = customTimers.setTimeout.call(void 0, () => {
1480
1480
  if (fallback) {
1481
1481
  try {
1482
- resolve20(fallback());
1482
+ resolve21(fallback());
1483
1483
  } catch (error) {
1484
1484
  reject(error);
1485
1485
  }
@@ -1489,7 +1489,7 @@ function pTimeout(promise, options) {
1489
1489
  promise.cancel();
1490
1490
  }
1491
1491
  if (message === false) {
1492
- resolve20();
1492
+ resolve21();
1493
1493
  } else if (message instanceof Error) {
1494
1494
  reject(message);
1495
1495
  } else {
@@ -1891,7 +1891,7 @@ var PQueue = class extends import_index.default {
1891
1891
  // Assign unique ID if not provided
1892
1892
  id: options.id ?? (this.#idAssigner++).toString()
1893
1893
  };
1894
- return new Promise((resolve20, reject) => {
1894
+ return new Promise((resolve21, reject) => {
1895
1895
  const taskSymbol = /* @__PURE__ */ Symbol(`task-${options.id}`);
1896
1896
  let cleanupQueueAbortHandler = () => void 0;
1897
1897
  const run = async () => {
@@ -1931,7 +1931,7 @@ var PQueue = class extends import_index.default {
1931
1931
  })]);
1932
1932
  }
1933
1933
  const result = await operation;
1934
- resolve20(result);
1934
+ resolve21(result);
1935
1935
  this.emit("completed", result);
1936
1936
  } catch (error) {
1937
1937
  reject(error);
@@ -2119,13 +2119,13 @@ var PQueue = class extends import_index.default {
2119
2119
  });
2120
2120
  }
2121
2121
  async #onEvent(event, filter) {
2122
- return new Promise((resolve20) => {
2122
+ return new Promise((resolve21) => {
2123
2123
  const listener = () => {
2124
2124
  if (filter && !filter()) {
2125
2125
  return;
2126
2126
  }
2127
2127
  this.off(event, listener);
2128
- resolve20();
2128
+ resolve21();
2129
2129
  };
2130
2130
  this.on(event, listener);
2131
2131
  });
@@ -2411,7 +2411,7 @@ async function onAttemptFailure({ error, attemptNumber, retriesConsumed, startTi
2411
2411
  const finalDelay = Math.min(delayTime, remainingTime);
2412
2412
  options.signal?.throwIfAborted();
2413
2413
  if (finalDelay > 0) {
2414
- await new Promise((resolve20, reject) => {
2414
+ await new Promise((resolve21, reject) => {
2415
2415
  const onAbort = () => {
2416
2416
  clearTimeout(timeoutToken);
2417
2417
  options.signal?.removeEventListener("abort", onAbort);
@@ -2419,7 +2419,7 @@ async function onAttemptFailure({ error, attemptNumber, retriesConsumed, startTi
2419
2419
  };
2420
2420
  const timeoutToken = setTimeout(() => {
2421
2421
  options.signal?.removeEventListener("abort", onAbort);
2422
- resolve20();
2422
+ resolve21();
2423
2423
  }, finalDelay);
2424
2424
  if (options.unref) {
2425
2425
  timeoutToken.unref?.();
@@ -2781,17 +2781,17 @@ function validateExternalUrl(urlString) {
2781
2781
  if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
2782
2782
  return { valid: false, reason: `Blocked protocol: ${parsed.protocol}` };
2783
2783
  }
2784
- const hostname2 = parsed.hostname.toLowerCase();
2785
- if (BLOCKED_HOSTNAMES.has(hostname2)) {
2786
- return { valid: false, reason: `Blocked: cloud metadata service (${hostname2})` };
2784
+ const hostname3 = parsed.hostname.toLowerCase();
2785
+ if (BLOCKED_HOSTNAMES.has(hostname3)) {
2786
+ return { valid: false, reason: `Blocked: cloud metadata service (${hostname3})` };
2787
2787
  }
2788
2788
  for (const pattern of BLOCKED_METADATA_IPS) {
2789
- if (pattern.test(hostname2)) {
2790
- return { valid: false, reason: `Blocked: cloud metadata IP (${hostname2})` };
2789
+ if (pattern.test(hostname3)) {
2790
+ return { valid: false, reason: `Blocked: cloud metadata IP (${hostname3})` };
2791
2791
  }
2792
2792
  }
2793
- if (/^169\.254\./.test(hostname2)) {
2794
- return { valid: false, reason: `Blocked: link-local address (${hostname2})` };
2793
+ if (/^169\.254\./.test(hostname3)) {
2794
+ return { valid: false, reason: `Blocked: link-local address (${hostname3})` };
2795
2795
  }
2796
2796
  return { valid: true };
2797
2797
  }
@@ -3377,26 +3377,46 @@ function createIgnoreFilter(projectRoot) {
3377
3377
  }
3378
3378
  return ig;
3379
3379
  }
3380
- function shouldIncludeFile(filePath, projectRoot, includePatterns, excludePatterns, ignoreFilter) {
3381
- const relativePath = path4.relative(projectRoot, filePath);
3382
- if (hasFilteredPathSegment(relativePath, path4.sep)) {
3383
- return false;
3384
- }
3385
- if (ignoreFilter.ignores(relativePath)) {
3386
- return false;
3380
+ function toPosixRelativePath(relativePath) {
3381
+ return relativePath.split(path4.sep).join("/");
3382
+ }
3383
+ function matchesAnyGlob(filePath, patterns) {
3384
+ const normalized = toPosixRelativePath(filePath);
3385
+ return patterns.some((pattern) => matchGlob(normalized, pattern));
3386
+ }
3387
+ function isExcludedByPatterns(relativePath, excludePatterns) {
3388
+ return matchesAnyGlob(relativePath, excludePatterns);
3389
+ }
3390
+ function isExcludedDirectory(relativePath, excludePatterns) {
3391
+ const normalized = toPosixRelativePath(relativePath);
3392
+ if (matchesAnyGlob(normalized, excludePatterns)) {
3393
+ return true;
3387
3394
  }
3388
3395
  for (const pattern of excludePatterns) {
3389
- if (matchGlob(relativePath, pattern)) {
3390
- return false;
3396
+ const posixPattern = toPosixRelativePath(pattern).replace(/\/+$/, "");
3397
+ if (!posixPattern.endsWith("/**")) {
3398
+ continue;
3391
3399
  }
3392
- }
3393
- for (const pattern of includePatterns) {
3394
- if (matchGlob(relativePath, pattern)) {
3400
+ const directoryPattern = posixPattern.slice(0, -3);
3401
+ if (directoryPattern && matchesAnyGlob(normalized, [directoryPattern])) {
3395
3402
  return true;
3396
3403
  }
3397
3404
  }
3398
3405
  return false;
3399
3406
  }
3407
+ function shouldIncludeFile(filePath, projectRoot, includePatterns, excludePatterns, ignoreFilter) {
3408
+ const relativePath = toPosixRelativePath(path4.relative(projectRoot, filePath));
3409
+ if (hasFilteredPathSegment(relativePath, "/")) {
3410
+ return false;
3411
+ }
3412
+ if (ignoreFilter.ignores(relativePath)) {
3413
+ return false;
3414
+ }
3415
+ if (isExcludedByPatterns(relativePath, excludePatterns)) {
3416
+ return false;
3417
+ }
3418
+ return matchesAnyGlob(relativePath, includePatterns);
3419
+ }
3400
3420
  function matchGlob(filePath, pattern) {
3401
3421
  if (pattern.startsWith("**/")) {
3402
3422
  const withoutPrefix = pattern.slice(3);
@@ -3418,7 +3438,7 @@ async function* walkDirectory(dir, projectRoot, includePatterns, excludePatterns
3418
3438
  const subdirs = [];
3419
3439
  for (const entry of entries) {
3420
3440
  const fullPath = path4.join(dir, entry.name);
3421
- const relativePath = path4.relative(projectRoot, fullPath);
3441
+ const relativePath = toPosixRelativePath(path4.relative(projectRoot, fullPath));
3422
3442
  if (isHiddenPathSegment(entry.name)) {
3423
3443
  if (entry.isDirectory()) {
3424
3444
  skipped.push({ path: relativePath, reason: "excluded" });
@@ -3436,6 +3456,10 @@ async function* walkDirectory(dir, projectRoot, includePatterns, excludePatterns
3436
3456
  continue;
3437
3457
  }
3438
3458
  if (entry.isDirectory()) {
3459
+ if (isExcludedDirectory(relativePath, excludePatterns)) {
3460
+ skipped.push({ path: relativePath, reason: "excluded" });
3461
+ continue;
3462
+ }
3439
3463
  subdirs.push({ fullPath, relativePath });
3440
3464
  } else if (entry.isFile()) {
3441
3465
  const stat5 = await fsPromises.stat(fullPath);
@@ -3443,20 +3467,11 @@ async function* walkDirectory(dir, projectRoot, includePatterns, excludePatterns
3443
3467
  skipped.push({ path: relativePath, reason: "too_large" });
3444
3468
  continue;
3445
3469
  }
3446
- for (const pattern of excludePatterns) {
3447
- if (matchGlob(relativePath, pattern)) {
3448
- skipped.push({ path: relativePath, reason: "excluded" });
3449
- continue;
3450
- }
3451
- }
3452
- let matched = false;
3453
- for (const pattern of includePatterns) {
3454
- if (matchGlob(relativePath, pattern)) {
3455
- matched = true;
3456
- break;
3457
- }
3470
+ if (isExcludedByPatterns(relativePath, excludePatterns)) {
3471
+ skipped.push({ path: relativePath, reason: "excluded" });
3472
+ continue;
3458
3473
  }
3459
- if (matched) {
3474
+ if (matchesAnyGlob(relativePath, includePatterns)) {
3460
3475
  filesInDir.push({ path: fullPath, size: stat5.size });
3461
3476
  }
3462
3477
  }
@@ -3467,7 +3482,7 @@ async function* walkDirectory(dir, projectRoot, includePatterns, excludePatterns
3467
3482
  yield f;
3468
3483
  }
3469
3484
  for (let i = options.maxFilesPerDirectory; i < filesInDir.length; i++) {
3470
- skipped.push({ path: path4.relative(projectRoot, filesInDir[i].path), reason: "excluded" });
3485
+ skipped.push({ path: toPosixRelativePath(path4.relative(projectRoot, filesInDir[i].path)), reason: "excluded" });
3471
3486
  }
3472
3487
  const canRecurse = options.maxDepth === -1 || currentDepth < options.maxDepth;
3473
3488
  if (canRecurse) {
@@ -7087,8 +7102,8 @@ function pathSegmentsForAffinityMatch(filePath) {
7087
7102
  if (segments.length === 0) {
7088
7103
  return [];
7089
7104
  }
7090
- const basename8 = segments[segments.length - 1] ?? "";
7091
- const basenameWithoutExt = basename8.replace(/\.[^/.]+$/u, "");
7105
+ const basename9 = segments[segments.length - 1] ?? "";
7106
+ const basenameWithoutExt = basename9.replace(/\.[^/.]+$/u, "");
7092
7107
  const normalizedSegments = segments.map((segment) => segment.toLowerCase());
7093
7108
  return Array.from(/* @__PURE__ */ new Set([
7094
7109
  ...normalizedSegments,
@@ -7511,7 +7526,7 @@ function removeDeadReclaimMarker(lockPath, expectedOwner) {
7511
7526
  return true;
7512
7527
  }
7513
7528
  function reclaimDeadOwner(indexPath, lockPath, expectedOwner) {
7514
- const reclaimPath = path13.join(lockPath, RECLAIM_DIRECTORY_NAME);
7529
+ const reclaimPath2 = path13.join(lockPath, RECLAIM_DIRECTORY_NAME);
7515
7530
  const reclaimOwner = {
7516
7531
  pid: process.pid,
7517
7532
  hostname: os5.hostname(),
@@ -7520,19 +7535,19 @@ function reclaimDeadOwner(indexPath, lockPath, expectedOwner) {
7520
7535
  expectedOwnerToken: expectedOwner.token
7521
7536
  };
7522
7537
  for (let attempt = 0; attempt < 2; attempt += 1) {
7523
- if (publishJsonDirectory(reclaimPath, reclaimOwner)) break;
7538
+ if (publishJsonDirectory(reclaimPath2, reclaimOwner)) break;
7524
7539
  if (attempt === 0 && removeDeadReclaimMarker(lockPath, expectedOwner)) continue;
7525
7540
  return false;
7526
7541
  }
7527
7542
  try {
7528
- const currentReclaimer = readReclaimOwner(reclaimPath);
7543
+ const currentReclaimer = readReclaimOwner(reclaimPath2);
7529
7544
  const currentOwner = readDirectoryOwner(lockPath);
7530
7545
  if (!currentReclaimer || !sameReclaimOwner(currentReclaimer, reclaimOwner) || !currentOwner || !sameOwner(currentOwner, expectedOwner) || getOwnerLiveness(currentOwner) !== "dead") {
7531
7546
  return false;
7532
7547
  }
7533
7548
  publishRecoveryMarker(indexPath, expectedOwner);
7534
7549
  const ownerBeforeQuarantine = readDirectoryOwner(lockPath);
7535
- const reclaimerBeforeQuarantine = readReclaimOwner(reclaimPath);
7550
+ const reclaimerBeforeQuarantine = readReclaimOwner(reclaimPath2);
7536
7551
  if (!ownerBeforeQuarantine || !sameOwner(ownerBeforeQuarantine, expectedOwner) || getOwnerLiveness(ownerBeforeQuarantine) !== "dead" || !reclaimerBeforeQuarantine || !sameReclaimOwner(reclaimerBeforeQuarantine, reclaimOwner)) {
7537
7552
  return false;
7538
7553
  }
@@ -8748,6 +8763,17 @@ var Indexer = class _Indexer {
8748
8763
  }
8749
8764
  return path15.relative(this.projectRoot, canonicalFilePath).split(path15.sep).join("/");
8750
8765
  }
8766
+ isStoredPathExcluded(storedPath) {
8767
+ let matchPath = storedPath.split(path15.sep).join("/");
8768
+ if (path15.isAbsolute(storedPath)) {
8769
+ const relativePath = path15.relative(this.projectRoot, storedPath).split(path15.sep).join("/");
8770
+ if (relativePath.startsWith("..") || path15.isAbsolute(relativePath)) {
8771
+ return false;
8772
+ }
8773
+ matchPath = relativePath;
8774
+ }
8775
+ return isExcludedByPatterns(matchPath, this.config.exclude);
8776
+ }
8751
8777
  resolveStoredFilePath(filePath, rootPath = this.projectRoot) {
8752
8778
  if (path15.isAbsolute(filePath)) {
8753
8779
  return filePath;
@@ -9772,7 +9798,7 @@ var Indexer = class _Indexer {
9772
9798
  await options.queue.onSizeLessThan(Math.max(1, options.providerRateLimits.concurrency));
9773
9799
  const task = options.queue.add(async () => {
9774
9800
  if (options.rateLimitState.backoffMs > 0) {
9775
- await new Promise((resolve20) => setTimeout(resolve20, options.rateLimitState.backoffMs));
9801
+ await new Promise((resolve21) => setTimeout(resolve21, options.rateLimitState.backoffMs));
9776
9802
  }
9777
9803
  try {
9778
9804
  const embeddingResult = await pRetry(
@@ -11149,7 +11175,7 @@ var Indexer = class _Indexer {
11149
11175
  }
11150
11176
  }
11151
11177
  }
11152
- const shouldRetryFailedPath = (filePath) => filePath !== null && currentFileHashes.has(filePath) && unchangedFilePaths.has(filePath);
11178
+ const shouldRetryFailedPath = (filePath) => filePath !== null && !this.isStoredPathExcluded(filePath) && currentFileHashes.has(filePath) && unchangedFilePaths.has(filePath);
11153
11179
  const failedProcessing = this.prepareFailedBatchProcessing(scopedRoots, shouldRetryFailedPath);
11154
11180
  const maxChunkTokens = getSafeEmbeddingChunkTokenLimit(configuredProviderInfo);
11155
11181
  const providerRateLimits = this.getProviderRateLimits(configuredProviderInfo.provider);
@@ -12477,7 +12503,8 @@ var Indexer = class _Indexer {
12477
12503
  const maxChunkTokens = getSafeEmbeddingChunkTokenLimit(configuredProviderInfo);
12478
12504
  const providerRateLimits = this.getProviderRateLimits(configuredProviderInfo.provider);
12479
12505
  const roots = this.config.scope === "global" ? this.getScopedRoots() : null;
12480
- const failedProcessing = this.prepareFailedBatchProcessing(roots, () => true);
12506
+ const shouldProcessFailedPath = (filePath) => filePath === null || !this.isStoredPathExcluded(filePath);
12507
+ const failedProcessing = this.prepareFailedBatchProcessing(roots, shouldProcessFailedPath);
12481
12508
  if (failedProcessing.latestById.size === 0) {
12482
12509
  this.finalizeFailedBatchWriteState(failedProcessing.state);
12483
12510
  return { succeeded: 0, failed: 0, remaining: 0 };
@@ -12490,7 +12517,7 @@ var Indexer = class _Indexer {
12490
12517
  const retryableChunks = this.iterateLatestFailedChunks(
12491
12518
  failedProcessing.latestById,
12492
12519
  roots,
12493
- () => true,
12520
+ shouldProcessFailedPath,
12494
12521
  maxChunkTokens
12495
12522
  );
12496
12523
  for (const retryBatch of iterateOrderedFileBatches(
@@ -12760,9 +12787,9 @@ var Indexer = class _Indexer {
12760
12787
  this.requireReadableComponents(readIssues, "database");
12761
12788
  let shortest = [];
12762
12789
  for (const branchKey of this.getBranchCatalogKeys()) {
12763
- const path33 = database.findShortestPath(fromName, toName, branchKey, maxDepth);
12764
- if (path33.length > 0 && (shortest.length === 0 || path33.length < shortest.length)) {
12765
- shortest = path33;
12790
+ const path34 = database.findShortestPath(fromName, toName, branchKey, maxDepth);
12791
+ if (path34.length > 0 && (shortest.length === 0 || path34.length < shortest.length)) {
12792
+ shortest = path34;
12766
12793
  }
12767
12794
  }
12768
12795
  return shortest.map((hop) => this.resolveFilePathRecord(hop));
@@ -12810,13 +12837,13 @@ var Indexer = class _Indexer {
12810
12837
  }
12811
12838
  }
12812
12839
  if (!found) continue;
12813
- const path33 = [];
12840
+ const path34 = [];
12814
12841
  let currentSymbolId = toSymbolId;
12815
12842
  while (true) {
12816
12843
  const symbol = symbolsById.get(currentSymbolId);
12817
12844
  if (!symbol) break;
12818
12845
  const parent = parentBySymbolId.get(currentSymbolId);
12819
- path33.push({
12846
+ path34.push({
12820
12847
  symbolId: symbol.id,
12821
12848
  symbolName: symbol.name,
12822
12849
  filePath: symbol.filePath,
@@ -12826,9 +12853,9 @@ var Indexer = class _Indexer {
12826
12853
  if (!parent) break;
12827
12854
  currentSymbolId = parent.parentId;
12828
12855
  }
12829
- path33.reverse();
12830
- if (path33.length > 0 && (shortest.length === 0 || path33.length < shortest.length)) {
12831
- shortest = path33;
12856
+ path34.reverse();
12857
+ if (path34.length > 0 && (shortest.length === 0 || path34.length < shortest.length)) {
12858
+ shortest = path34;
12832
12859
  }
12833
12860
  }
12834
12861
  return shortest.map((hop) => this.resolveFilePathRecord(hop));
@@ -13211,8 +13238,8 @@ var CODE_COMMUNITIES_DEFAULT_COUPLING_LIMIT = 20;
13211
13238
  var CODE_COMMUNITIES_MAX_COUPLING_LIMIT = 100;
13212
13239
 
13213
13240
  // src/tools/operations.ts
13214
- import { existsSync as existsSync12, realpathSync as realpathSync5, statSync as statSync5 } from "fs";
13215
- import * as path21 from "path";
13241
+ import { existsSync as existsSync13, realpathSync as realpathSync6, statSync as statSync5 } from "fs";
13242
+ import * as path22 from "path";
13216
13243
 
13217
13244
  // src/tools/knowledge-base-paths.ts
13218
13245
  import * as path16 from "path";
@@ -13507,8 +13534,8 @@ function formatExactSearchHandoff(results) {
13507
13534
  }
13508
13535
  function formatContextEvidence(result, index) {
13509
13536
  const symbol = result.name ? ` ${JSON.stringify(compactEvidenceValue(result.name, 80))}` : "";
13510
- const path33 = compactEvidenceValue(result.filePath, 120);
13511
- return `[${index}] ${result.chunkType}${symbol} in ${path33}:${result.startLine}-${result.endLine} (score ${result.score.toFixed(2)})`;
13537
+ const path34 = compactEvidenceValue(result.filePath, 120);
13538
+ return `[${index}] ${result.chunkType}${symbol} in ${path34}:${result.startLine}-${result.endLine} (score ${result.score.toFixed(2)})`;
13512
13539
  }
13513
13540
  function formatContextPack(heading, selected, candidateCount, duplicateCount, limitOmittedCount, budgetOmittedCount, includeExactSearchHandoff) {
13514
13541
  const lines = selected.map((result, index) => formatContextEvidence(result, index + 1));
@@ -14150,9 +14177,907 @@ function formatEffectivenessMetrics(snapshot) {
14150
14177
  }
14151
14178
 
14152
14179
  // src/utils/auto-index.ts
14153
- import { existsSync as existsSync9, realpathSync as realpathSync4 } from "fs";
14180
+ import { existsSync as existsSync10, realpathSync as realpathSync5 } from "fs";
14181
+ import * as os7 from "os";
14182
+ import * as path18 from "path";
14183
+
14184
+ // src/utils/background-worker.ts
14185
+ import { createHash as createHash2, randomUUID as randomUUID2 } from "crypto";
14186
+ import {
14187
+ existsSync as existsSync9,
14188
+ lstatSync as lstatSync2,
14189
+ mkdirSync as mkdirSync5,
14190
+ readFileSync as readFileSync9,
14191
+ realpathSync as realpathSync4,
14192
+ renameSync as renameSync4,
14193
+ rmSync as rmSync3,
14194
+ writeFileSync as writeFileSync4
14195
+ } from "fs";
14154
14196
  import * as os6 from "os";
14155
14197
  import * as path17 from "path";
14198
+ var OWNER_FILE_NAME2 = "owner.json";
14199
+ var HEARTBEAT_FILE_PREFIX = "heartbeat.";
14200
+ var RECLAIM_DIRECTORY_NAME2 = "reclaim";
14201
+ var REFRESH_REQUEST_FILE_NAME = "refresh-request.json";
14202
+ var HEARTBEAT_INTERVAL_MS = 5e3;
14203
+ var STALE_LEASE_MS = 3e4;
14204
+ var RETRY_DELAY_MS = 5e3;
14205
+ 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;
14206
+ var BackgroundWorkerStopError = class extends Error {
14207
+ constructor(watcherError, autoIndexError) {
14208
+ super("Failed to stop background worker");
14209
+ this.watcherError = watcherError;
14210
+ this.autoIndexError = autoIndexError;
14211
+ this.name = "BackgroundWorkerStopError";
14212
+ }
14213
+ watcherError;
14214
+ autoIndexError;
14215
+ };
14216
+ var workers = /* @__PURE__ */ new Map();
14217
+ var workerKeysByProject = /* @__PURE__ */ new Map();
14218
+ var workerReplacementBarriers = /* @__PURE__ */ new Map();
14219
+ function getErrorCode2(error) {
14220
+ return typeof error === "object" && error !== null && "code" in error ? String(error.code) : void 0;
14221
+ }
14222
+ function canonicalizePath(targetPath) {
14223
+ const resolved = path17.resolve(targetPath);
14224
+ if (existsSync9(resolved)) {
14225
+ try {
14226
+ return realpathSync4.native(resolved);
14227
+ } catch {
14228
+ return resolved;
14229
+ }
14230
+ }
14231
+ const parent = path17.dirname(resolved);
14232
+ if (parent === resolved) return resolved;
14233
+ return path17.join(canonicalizePath(parent), path17.basename(resolved));
14234
+ }
14235
+ function projectLookupKey(projectRoot, host) {
14236
+ return `${host}::${canonicalizePath(projectRoot)}`;
14237
+ }
14238
+ function getBackgroundWorkerProjectKey(projectRoot, host) {
14239
+ return projectLookupKey(projectRoot, host);
14240
+ }
14241
+ function resolveIdentity(projectRoot, config, host) {
14242
+ const canonicalProjectRoot = canonicalizePath(projectRoot);
14243
+ const canonicalIndexPath = canonicalizePath(resolveProjectIndexPath(projectRoot, config.scope, host));
14244
+ return {
14245
+ canonicalIndexPath,
14246
+ canonicalProjectRoot,
14247
+ key: `${canonicalIndexPath}::${canonicalProjectRoot}`
14248
+ };
14249
+ }
14250
+ function controllerKey(identity, host) {
14251
+ return `${identity.key}::${host}`;
14252
+ }
14253
+ function leaseDirectoryName(identity) {
14254
+ const hash = createHash2("sha256").update(identity.key).digest("hex").slice(0, 32);
14255
+ return `background-worker.${hash}.lease`;
14256
+ }
14257
+ function leasePathFor(identity) {
14258
+ return path17.join(identity.canonicalIndexPath, leaseDirectoryName(identity));
14259
+ }
14260
+ function parseOwner2(value) {
14261
+ if (typeof value !== "object" || value === null) return null;
14262
+ const candidate = value;
14263
+ if (candidate.version !== 1) return null;
14264
+ if (!Number.isInteger(candidate.pid) || (candidate.pid ?? 0) <= 0) return null;
14265
+ if (typeof candidate.hostname !== "string" || candidate.hostname.length === 0) return null;
14266
+ if (typeof candidate.startedAt !== "string" || Number.isNaN(Date.parse(candidate.startedAt))) return null;
14267
+ if (typeof candidate.heartbeatAt !== "string" || Number.isNaN(Date.parse(candidate.heartbeatAt))) return null;
14268
+ if (typeof candidate.projectRoot !== "string" || candidate.projectRoot.length === 0) return null;
14269
+ if (typeof candidate.indexPath !== "string" || candidate.indexPath.length === 0) return null;
14270
+ if (typeof candidate.token !== "string" || !UUID_PATTERN2.test(candidate.token)) return null;
14271
+ return candidate;
14272
+ }
14273
+ function parseHeartbeat(value, expectedToken) {
14274
+ if (typeof value !== "object" || value === null) return null;
14275
+ const candidate = value;
14276
+ if (candidate.version !== 1 || candidate.token !== expectedToken) return null;
14277
+ if (typeof candidate.heartbeatAt !== "string" || Number.isNaN(Date.parse(candidate.heartbeatAt))) return null;
14278
+ return candidate;
14279
+ }
14280
+ function parseReclaimOwner2(value) {
14281
+ if (typeof value !== "object" || value === null) return null;
14282
+ const candidate = value;
14283
+ if (candidate.version !== 1) return null;
14284
+ if (!Number.isInteger(candidate.pid) || (candidate.pid ?? 0) <= 0) return null;
14285
+ if (typeof candidate.hostname !== "string" || candidate.hostname.length === 0) return null;
14286
+ if (typeof candidate.startedAt !== "string" || Number.isNaN(Date.parse(candidate.startedAt))) return null;
14287
+ if (typeof candidate.token !== "string" || !UUID_PATTERN2.test(candidate.token)) return null;
14288
+ if (candidate.expectedOwnerToken !== null && (typeof candidate.expectedOwnerToken !== "string" || !UUID_PATTERN2.test(candidate.expectedOwnerToken))) return null;
14289
+ return candidate;
14290
+ }
14291
+ function heartbeatPath(leasePath, token) {
14292
+ return path17.join(leasePath, `${HEARTBEAT_FILE_PREFIX}${token}.json`);
14293
+ }
14294
+ function reclaimPath(leasePath) {
14295
+ return path17.join(leasePath, RECLAIM_DIRECTORY_NAME2);
14296
+ }
14297
+ function refreshRequestPath(leasePath) {
14298
+ return path17.join(leasePath, REFRESH_REQUEST_FILE_NAME);
14299
+ }
14300
+ function readLeaseOwner(leasePath) {
14301
+ try {
14302
+ return parseOwner2(JSON.parse(readFileSync9(path17.join(leasePath, OWNER_FILE_NAME2), "utf-8")));
14303
+ } catch {
14304
+ return null;
14305
+ }
14306
+ }
14307
+ function readOwner(leasePath) {
14308
+ const owner = readLeaseOwner(leasePath);
14309
+ if (!owner) return null;
14310
+ try {
14311
+ const heartbeat = parseHeartbeat(
14312
+ JSON.parse(readFileSync9(heartbeatPath(leasePath, owner.token), "utf-8")),
14313
+ owner.token
14314
+ );
14315
+ return heartbeat ? { ...owner, heartbeatAt: heartbeat.heartbeatAt } : owner;
14316
+ } catch {
14317
+ return owner;
14318
+ }
14319
+ }
14320
+ function readReclaimOwner2(leasePath) {
14321
+ try {
14322
+ return parseReclaimOwner2(JSON.parse(readFileSync9(path17.join(reclaimPath(leasePath), OWNER_FILE_NAME2), "utf-8")));
14323
+ } catch {
14324
+ return null;
14325
+ }
14326
+ }
14327
+ function ownerLiveness(owner) {
14328
+ if (owner.hostname !== os6.hostname()) return "unknown";
14329
+ try {
14330
+ process.kill(owner.pid, 0);
14331
+ return "alive";
14332
+ } catch (error) {
14333
+ const code = getErrorCode2(error);
14334
+ if (code === "ESRCH") return "dead";
14335
+ if (code === "EPERM") return "alive";
14336
+ return "unknown";
14337
+ }
14338
+ }
14339
+ function isHeartbeatExpired(owner) {
14340
+ return Date.now() - Date.parse(owner.heartbeatAt) >= STALE_LEASE_MS;
14341
+ }
14342
+ function sameOwner2(left, right) {
14343
+ return left.pid === right.pid && left.hostname === right.hostname && left.token === right.token;
14344
+ }
14345
+ function writeHeartbeat(leasePath, owner) {
14346
+ const targetPath = heartbeatPath(leasePath, owner.token);
14347
+ const temporaryPath = `${targetPath}.tmp.${process.pid}.${owner.token}.${randomUUID2()}`;
14348
+ const heartbeat = {
14349
+ version: 1,
14350
+ token: owner.token,
14351
+ heartbeatAt: owner.heartbeatAt
14352
+ };
14353
+ try {
14354
+ writeFileSync4(temporaryPath, JSON.stringify(heartbeat), {
14355
+ encoding: "utf-8",
14356
+ flag: "wx",
14357
+ mode: 384
14358
+ });
14359
+ renameSync4(temporaryPath, targetPath);
14360
+ const currentOwner = readLeaseOwner(leasePath);
14361
+ return currentOwner !== null && sameOwner2(currentOwner, owner);
14362
+ } finally {
14363
+ if (existsSync9(temporaryPath)) rmSync3(temporaryPath, { force: true });
14364
+ }
14365
+ }
14366
+ function requestRefreshFromLeader(leasePath, allowDisabledAutoIndex) {
14367
+ const requestPath = refreshRequestPath(leasePath);
14368
+ const temporaryPath = `${requestPath}.tmp.${process.pid}.${randomUUID2()}`;
14369
+ try {
14370
+ const request = {
14371
+ allowDisabledAutoIndex,
14372
+ requestedAt: (/* @__PURE__ */ new Date()).toISOString(),
14373
+ version: 1
14374
+ };
14375
+ writeFileSync4(temporaryPath, JSON.stringify(request), {
14376
+ encoding: "utf-8",
14377
+ flag: "wx",
14378
+ mode: 384
14379
+ });
14380
+ renameSync4(temporaryPath, requestPath);
14381
+ } catch (error) {
14382
+ if (getErrorCode2(error) !== "ENOENT") {
14383
+ console.error("[codebase-index] Failed to request background index refresh from the project worker:", error);
14384
+ }
14385
+ } finally {
14386
+ if (existsSync9(temporaryPath)) rmSync3(temporaryPath, { force: true });
14387
+ }
14388
+ }
14389
+ function consumeRefreshRequest(leasePath) {
14390
+ const requestPath = refreshRequestPath(leasePath);
14391
+ const claimedPath = `${requestPath}.handling.${process.pid}.${randomUUID2()}`;
14392
+ try {
14393
+ renameSync4(requestPath, claimedPath);
14394
+ } catch (error) {
14395
+ if (getErrorCode2(error) === "ENOENT") return null;
14396
+ throw error;
14397
+ }
14398
+ try {
14399
+ const value = JSON.parse(readFileSync9(claimedPath, "utf-8"));
14400
+ return {
14401
+ allowDisabledAutoIndex: value.version === 1 && value.allowDisabledAutoIndex === true,
14402
+ requestedAt: typeof value.requestedAt === "string" ? value.requestedAt : (/* @__PURE__ */ new Date()).toISOString(),
14403
+ version: 1
14404
+ };
14405
+ } catch {
14406
+ return { allowDisabledAutoIndex: false, requestedAt: (/* @__PURE__ */ new Date()).toISOString(), version: 1 };
14407
+ } finally {
14408
+ rmSync3(claimedPath, { force: true });
14409
+ }
14410
+ }
14411
+ function publishLease(leasePath, owner) {
14412
+ const candidatePath = `${leasePath}.candidate.${process.pid}.${owner.token}`;
14413
+ try {
14414
+ mkdirSync5(candidatePath, { mode: 448 });
14415
+ } catch (error) {
14416
+ if (getErrorCode2(error) === "ENOENT") return false;
14417
+ throw error;
14418
+ }
14419
+ try {
14420
+ writeFileSync4(path17.join(candidatePath, OWNER_FILE_NAME2), JSON.stringify(owner), {
14421
+ encoding: "utf-8",
14422
+ flag: "wx",
14423
+ mode: 384
14424
+ });
14425
+ if (existsSync9(leasePath)) return false;
14426
+ try {
14427
+ renameSync4(candidatePath, leasePath);
14428
+ return true;
14429
+ } catch (error) {
14430
+ if (existsSync9(leasePath) || getErrorCode2(error) === "ENOENT") return false;
14431
+ throw error;
14432
+ }
14433
+ } finally {
14434
+ if (existsSync9(candidatePath)) rmSync3(candidatePath, { recursive: true, force: true });
14435
+ }
14436
+ }
14437
+ function sameReclaimOwner2(left, right) {
14438
+ return left.pid === right.pid && left.hostname === right.hostname && left.token === right.token && left.expectedOwnerToken === right.expectedOwnerToken;
14439
+ }
14440
+ function reclaimerLiveness(owner) {
14441
+ return ownerLiveness(owner);
14442
+ }
14443
+ function isReclaimMarkerExpired(leasePath, owner) {
14444
+ const startedAt = owner ? Date.parse(owner.startedAt) : (() => {
14445
+ try {
14446
+ return lstatSync2(reclaimPath(leasePath)).mtimeMs;
14447
+ } catch {
14448
+ return Date.now();
14449
+ }
14450
+ })();
14451
+ return Date.now() - startedAt >= STALE_LEASE_MS;
14452
+ }
14453
+ function hasActiveReclaimMarker(leasePath, owner) {
14454
+ const marker = readReclaimOwner2(leasePath);
14455
+ return marker !== null && marker.expectedOwnerToken === owner.token && (marker.hostname !== os6.hostname() || ownerLiveness(owner) !== "alive");
14456
+ }
14457
+ function publishReclaimMarker(leasePath, expectedOwner) {
14458
+ const markerPath = reclaimPath(leasePath);
14459
+ const owner = {
14460
+ version: 1,
14461
+ pid: process.pid,
14462
+ hostname: os6.hostname(),
14463
+ startedAt: (/* @__PURE__ */ new Date()).toISOString(),
14464
+ token: randomUUID2(),
14465
+ expectedOwnerToken: expectedOwner?.token ?? null
14466
+ };
14467
+ try {
14468
+ mkdirSync5(markerPath, { mode: 448 });
14469
+ } catch (error) {
14470
+ if (getErrorCode2(error) === "EEXIST" || getErrorCode2(error) === "ENOENT") return null;
14471
+ throw error;
14472
+ }
14473
+ try {
14474
+ writeFileSync4(path17.join(markerPath, OWNER_FILE_NAME2), JSON.stringify(owner), {
14475
+ encoding: "utf-8",
14476
+ flag: "wx",
14477
+ mode: 384
14478
+ });
14479
+ return owner;
14480
+ } catch (error) {
14481
+ rmSync3(markerPath, { recursive: true, force: true });
14482
+ throw error;
14483
+ }
14484
+ }
14485
+ function removeExpiredReclaimMarker(leasePath, expectedOwner) {
14486
+ const marker = readReclaimOwner2(leasePath);
14487
+ const markerPath = reclaimPath(leasePath);
14488
+ if (!existsSync9(markerPath)) return false;
14489
+ if (marker && marker.expectedOwnerToken !== (expectedOwner?.token ?? null)) return false;
14490
+ if (marker && (reclaimerLiveness(marker) === "alive" || !isReclaimMarkerExpired(leasePath, marker))) return false;
14491
+ if (!marker && !isReclaimMarkerExpired(leasePath, null)) return false;
14492
+ const staleMarkerPath = `${markerPath}.stale.${marker?.pid ?? process.pid}.${marker?.token ?? randomUUID2()}.${randomUUID2()}`;
14493
+ try {
14494
+ renameSync4(markerPath, staleMarkerPath);
14495
+ } catch (error) {
14496
+ if (getErrorCode2(error) === "ENOENT") return false;
14497
+ throw error;
14498
+ }
14499
+ try {
14500
+ let claimedMarker = null;
14501
+ try {
14502
+ claimedMarker = parseReclaimOwner2(
14503
+ JSON.parse(readFileSync9(path17.join(staleMarkerPath, OWNER_FILE_NAME2), "utf-8"))
14504
+ );
14505
+ } catch {
14506
+ claimedMarker = null;
14507
+ }
14508
+ const markerMatches = marker ? claimedMarker !== null && sameReclaimOwner2(claimedMarker, marker) : claimedMarker === null;
14509
+ if (!markerMatches || !canReclaimLease(leasePath, expectedOwner)) {
14510
+ if (!existsSync9(markerPath) && existsSync9(staleMarkerPath)) renameSync4(staleMarkerPath, markerPath);
14511
+ return false;
14512
+ }
14513
+ rmSync3(staleMarkerPath, { recursive: true, force: true });
14514
+ return true;
14515
+ } catch (error) {
14516
+ if (getErrorCode2(error) === "ENOENT") return false;
14517
+ throw error;
14518
+ }
14519
+ }
14520
+ function canReclaimLease(leasePath, expectedOwner) {
14521
+ if (!existsSync9(leasePath)) return false;
14522
+ if (!expectedOwner) return false;
14523
+ const currentOwner = readOwner(leasePath);
14524
+ if (!currentOwner || !sameOwner2(currentOwner, expectedOwner)) return false;
14525
+ if (currentOwner.hostname === os6.hostname()) {
14526
+ return ownerLiveness(currentOwner) === "dead";
14527
+ }
14528
+ return isHeartbeatExpired(currentOwner);
14529
+ }
14530
+ function reclaimLease(leasePath, expectedOwner) {
14531
+ let marker = null;
14532
+ for (let attempt = 0; attempt < 2; attempt += 1) {
14533
+ marker = publishReclaimMarker(leasePath, expectedOwner);
14534
+ if (marker) break;
14535
+ if (attempt === 0 && removeExpiredReclaimMarker(leasePath, expectedOwner)) continue;
14536
+ return false;
14537
+ }
14538
+ if (!marker) return false;
14539
+ const markerPath = reclaimPath(leasePath);
14540
+ try {
14541
+ const currentMarker = readReclaimOwner2(leasePath);
14542
+ if (!currentMarker || !sameReclaimOwner2(currentMarker, marker) || !canReclaimLease(leasePath, expectedOwner)) {
14543
+ return false;
14544
+ }
14545
+ const stalePath = `${leasePath}.stale.${process.pid}.${marker.token}`;
14546
+ renameSync4(leasePath, stalePath);
14547
+ const quarantinedOwner = readOwner(stalePath);
14548
+ const quarantinedMarker = readReclaimOwner2(stalePath);
14549
+ if (!quarantinedMarker || !sameReclaimOwner2(quarantinedMarker, marker) || expectedOwner !== null && (!quarantinedOwner || !sameOwner2(quarantinedOwner, expectedOwner))) {
14550
+ if (!existsSync9(leasePath) && existsSync9(stalePath)) renameSync4(stalePath, leasePath);
14551
+ return false;
14552
+ }
14553
+ rmSync3(stalePath, { recursive: true, force: true });
14554
+ return true;
14555
+ } catch (error) {
14556
+ if (getErrorCode2(error) === "ENOENT") return false;
14557
+ throw error;
14558
+ } finally {
14559
+ const currentMarker = readReclaimOwner2(leasePath);
14560
+ if (currentMarker && sameReclaimOwner2(currentMarker, marker)) {
14561
+ rmSync3(markerPath, { recursive: true, force: true });
14562
+ }
14563
+ }
14564
+ }
14565
+ function acquireLease(identity) {
14566
+ mkdirSync5(identity.canonicalIndexPath, { recursive: true, mode: 448 });
14567
+ const canonicalIndexPath = realpathSync4.native(identity.canonicalIndexPath);
14568
+ const leasePath = path17.join(canonicalIndexPath, leaseDirectoryName({ ...identity, canonicalIndexPath }));
14569
+ for (let attempt = 0; attempt < 4; attempt += 1) {
14570
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
14571
+ const owner = {
14572
+ version: 1,
14573
+ pid: process.pid,
14574
+ hostname: os6.hostname(),
14575
+ startedAt: timestamp,
14576
+ heartbeatAt: timestamp,
14577
+ projectRoot: identity.canonicalProjectRoot,
14578
+ indexPath: canonicalIndexPath,
14579
+ token: randomUUID2()
14580
+ };
14581
+ if (publishLease(leasePath, owner)) {
14582
+ return { leasePath, owner };
14583
+ }
14584
+ const existingOwner = readOwner(leasePath);
14585
+ if (existingOwner) {
14586
+ if (canReclaimLease(leasePath, existingOwner) && reclaimLease(leasePath, existingOwner)) continue;
14587
+ return null;
14588
+ }
14589
+ return null;
14590
+ }
14591
+ return null;
14592
+ }
14593
+ function releaseLease(lease) {
14594
+ const currentOwner = readOwner(lease.leasePath);
14595
+ if (!currentOwner || !sameOwner2(currentOwner, lease.owner)) return false;
14596
+ const releasePath = `${lease.leasePath}.release.${lease.owner.pid}.${lease.owner.token}`;
14597
+ try {
14598
+ renameSync4(lease.leasePath, releasePath);
14599
+ } catch (error) {
14600
+ if (getErrorCode2(error) === "ENOENT") return false;
14601
+ throw error;
14602
+ }
14603
+ const claimedOwner = readOwner(releasePath);
14604
+ if (!claimedOwner || !sameOwner2(claimedOwner, lease.owner)) {
14605
+ if (!existsSync9(lease.leasePath) && existsSync9(releasePath)) {
14606
+ renameSync4(releasePath, lease.leasePath);
14607
+ }
14608
+ return false;
14609
+ }
14610
+ rmSync3(releasePath, { recursive: true, force: true });
14611
+ return true;
14612
+ }
14613
+ var BackgroundWorkerController = class {
14614
+ constructor(projectRoot, host, config, hooks, identity) {
14615
+ this.projectRoot = projectRoot;
14616
+ this.host = host;
14617
+ this.config = config;
14618
+ this.hooks = hooks;
14619
+ this.identity = identity;
14620
+ }
14621
+ projectRoot;
14622
+ host;
14623
+ config;
14624
+ hooks;
14625
+ identity;
14626
+ lease = null;
14627
+ watcher = null;
14628
+ leaderReady = Promise.resolve();
14629
+ heartbeatTimer = null;
14630
+ retryTimer = null;
14631
+ teardownRetryTimer = null;
14632
+ transition = Promise.resolve();
14633
+ stopPromise = null;
14634
+ stopped = false;
14635
+ stopping = false;
14636
+ losingLeadership = false;
14637
+ restartAfterStop = false;
14638
+ leaderWorkStopped = false;
14639
+ startingLeaderWork = false;
14640
+ stopAutoIndexOnTeardown = true;
14641
+ autoIndexStarted = false;
14642
+ reportedError = null;
14643
+ update(config, hooks, options) {
14644
+ const autoIndexWasEnabled = this.config.indexing.autoIndex;
14645
+ const shouldReplaceWatcher = this.watcher !== null && hooks.watcherFactory !== void 0 && (hooks.watcherFactory === null || hooks.replaceWatcher === true);
14646
+ this.config = config;
14647
+ this.hooks = {
14648
+ ...this.hooks,
14649
+ ...hooks,
14650
+ watcherFactory: hooks.watcherFactory === void 0 ? this.hooks.watcherFactory : hooks.watcherFactory,
14651
+ watcherFactoryForConfig: hooks.watcherFactoryForConfig === void 0 ? this.hooks.watcherFactoryForConfig : hooks.watcherFactoryForConfig
14652
+ };
14653
+ if (autoIndexWasEnabled && !config.indexing.autoIndex || options.restartAutoIndex === true && config.indexing.autoIndex && !this.startingLeaderWork) {
14654
+ this.autoIndexStarted = false;
14655
+ }
14656
+ if (!this.canRun()) {
14657
+ void this.stop().catch((error) => {
14658
+ console.error("[codebase-index] Failed to stop background worker after disabling automatic work:", error);
14659
+ });
14660
+ return;
14661
+ }
14662
+ if (shouldReplaceWatcher) {
14663
+ void this.enqueue(async () => {
14664
+ const watcher = this.watcher;
14665
+ if (watcher) {
14666
+ await watcher.stop();
14667
+ if (this.watcher === watcher) this.watcher = null;
14668
+ }
14669
+ if (this.lease && !this.stopped) this.startLeaderWork();
14670
+ }).catch((error) => {
14671
+ console.error("[codebase-index] Failed to replace background file watcher:", error);
14672
+ });
14673
+ }
14674
+ this.start();
14675
+ }
14676
+ startAfter(activation) {
14677
+ this.transition = activation.catch(() => void 0);
14678
+ this.start();
14679
+ }
14680
+ start() {
14681
+ if (!this.canRun() || this.losingLeadership) return;
14682
+ if (this.stopping) {
14683
+ this.restartAfterStop = true;
14684
+ return;
14685
+ }
14686
+ this.stopped = false;
14687
+ void this.enqueue(async () => {
14688
+ if (this.stopped || this.stopping || this.losingLeadership || !this.canRun()) return;
14689
+ if (!this.lease) {
14690
+ try {
14691
+ this.lease = acquireLease(this.identity);
14692
+ this.reportedError = null;
14693
+ } catch (error) {
14694
+ this.reportAcquireError(error);
14695
+ this.scheduleRetry();
14696
+ return;
14697
+ }
14698
+ }
14699
+ if (!this.lease) {
14700
+ this.scheduleRetry();
14701
+ return;
14702
+ }
14703
+ this.startHeartbeat();
14704
+ this.startLeaderWork();
14705
+ });
14706
+ }
14707
+ waitForStart() {
14708
+ return this.transition.catch(() => void 0).then(() => this.leaderReady);
14709
+ }
14710
+ requestRefresh(allowDisabledAutoIndex = false) {
14711
+ this.start();
14712
+ if (!this.isLeader()) {
14713
+ requestRefreshFromLeader(leasePathFor(this.identity), allowDisabledAutoIndex);
14714
+ return;
14715
+ }
14716
+ void this.enqueue(async () => {
14717
+ if (this.stopped || !this.lease) return;
14718
+ this.hooks.startAutoIndex("retrieval", allowDisabledAutoIndex);
14719
+ });
14720
+ }
14721
+ isLeader() {
14722
+ return this.lease !== null && !this.stopping && !this.losingLeadership;
14723
+ }
14724
+ isStopping() {
14725
+ return this.stopping;
14726
+ }
14727
+ getHooksForConfig(config) {
14728
+ const watcherFactoryForConfig = this.hooks.watcherFactoryForConfig;
14729
+ if (!watcherFactoryForConfig) return this.hooks;
14730
+ return {
14731
+ ...this.hooks,
14732
+ watcherFactory: watcherFactoryForConfig(config),
14733
+ replaceWatcher: true
14734
+ };
14735
+ }
14736
+ attachWatcher(watcherFactory, watcherFactoryForConfig) {
14737
+ if (this.hooks.watcherFactory !== void 0) return;
14738
+ this.hooks = {
14739
+ ...this.hooks,
14740
+ watcherFactory,
14741
+ watcherFactoryForConfig: watcherFactoryForConfig ?? this.hooks.watcherFactoryForConfig
14742
+ };
14743
+ this.start();
14744
+ }
14745
+ async stop(stopAutoIndex = true) {
14746
+ if (this.stopPromise) return this.stopPromise;
14747
+ this.stopped = true;
14748
+ this.stopping = true;
14749
+ this.stopAutoIndexOnTeardown &&= stopAutoIndex;
14750
+ this.clearRetryTimer();
14751
+ const attempt = this.enqueue(async () => {
14752
+ try {
14753
+ const lease = this.lease;
14754
+ if (this.leaderWorkStopped) {
14755
+ if (lease) {
14756
+ this.releaseStoppedLease(lease);
14757
+ } else {
14758
+ this.finishStoppedLease();
14759
+ }
14760
+ return;
14761
+ }
14762
+ const hadLeaderWork = lease !== null || this.watcher !== null || this.autoIndexStarted;
14763
+ const stopped = await this.stopLeaderWork(hadLeaderWork && this.stopAutoIndexOnTeardown);
14764
+ if (!lease) {
14765
+ this.finishStoppedLease();
14766
+ return;
14767
+ }
14768
+ if (!stopped.completed) {
14769
+ this.releaseLeaseWhenAutoIndexStops(lease, stopped.completion);
14770
+ return;
14771
+ }
14772
+ this.leaderWorkStopped = true;
14773
+ this.releaseStoppedLease(lease);
14774
+ } catch (error) {
14775
+ this.scheduleTeardownRetry();
14776
+ throw error;
14777
+ }
14778
+ });
14779
+ const completion = attempt.finally(() => {
14780
+ if (this.stopPromise === completion) this.stopPromise = null;
14781
+ });
14782
+ this.stopPromise = completion;
14783
+ return completion;
14784
+ }
14785
+ canRun() {
14786
+ return this.config.indexing.autoIndex || this.hooks.watcherFactory != null;
14787
+ }
14788
+ enqueue(operation) {
14789
+ const next = this.transition.catch(() => void 0).then(operation);
14790
+ this.transition = next;
14791
+ return next;
14792
+ }
14793
+ startLeaderWork() {
14794
+ if (this.stopped || this.stopping || this.losingLeadership) return;
14795
+ this.startingLeaderWork = true;
14796
+ try {
14797
+ if (this.config.indexing.autoIndex && !this.autoIndexStarted) {
14798
+ this.autoIndexStarted = true;
14799
+ this.hooks.startAutoIndex("startup");
14800
+ }
14801
+ if (!this.watcher && this.hooks.watcherFactory) {
14802
+ try {
14803
+ const watcher = this.hooks.watcherFactory();
14804
+ this.watcher = watcher;
14805
+ this.leaderReady = watcher.whenReady?.().catch((error) => {
14806
+ console.error("[codebase-index] Failed while waiting for background file watcher startup:", error);
14807
+ }) ?? Promise.resolve();
14808
+ } catch (error) {
14809
+ console.error("[codebase-index] Failed to start background file watcher:", error);
14810
+ this.leaderReady = Promise.resolve();
14811
+ }
14812
+ }
14813
+ } finally {
14814
+ this.startingLeaderWork = false;
14815
+ }
14816
+ }
14817
+ async stopLeaderWork(stopAutoIndex) {
14818
+ const watcher = this.watcher;
14819
+ let watcherError;
14820
+ if (watcher) {
14821
+ try {
14822
+ await watcher.stop();
14823
+ if (this.watcher === watcher) this.watcher = null;
14824
+ } catch (error) {
14825
+ watcherError = error;
14826
+ }
14827
+ }
14828
+ let autoIndexError;
14829
+ let autoIndexStop = {
14830
+ completed: true,
14831
+ completion: Promise.resolve()
14832
+ };
14833
+ if (stopAutoIndex) {
14834
+ try {
14835
+ autoIndexStop = await this.hooks.stopAutoIndex();
14836
+ this.autoIndexStarted = false;
14837
+ } catch (error) {
14838
+ autoIndexError = error;
14839
+ }
14840
+ }
14841
+ if (watcherError !== void 0 || autoIndexError !== void 0) {
14842
+ throw new BackgroundWorkerStopError(watcherError, autoIndexError);
14843
+ }
14844
+ return autoIndexStop;
14845
+ }
14846
+ releaseLeaseWhenAutoIndexStops(lease, completion) {
14847
+ void completion.then(
14848
+ () => {
14849
+ void this.enqueue(async () => {
14850
+ if (this.lease !== lease || !this.stopping) return;
14851
+ this.leaderWorkStopped = true;
14852
+ this.releaseStoppedLease(lease);
14853
+ }).catch((error) => {
14854
+ console.error("[codebase-index] Failed to release background worker lease after automatic indexing stopped:", error);
14855
+ this.scheduleTeardownRetry();
14856
+ });
14857
+ },
14858
+ (error) => {
14859
+ console.error("[codebase-index] Failed while waiting for automatic indexing to stop:", error);
14860
+ this.scheduleTeardownRetry();
14861
+ }
14862
+ );
14863
+ }
14864
+ releaseStoppedLease(lease) {
14865
+ if (this.lease !== lease) {
14866
+ this.finishStoppedLease();
14867
+ return;
14868
+ }
14869
+ releaseLease(lease);
14870
+ this.lease = null;
14871
+ this.finishStoppedLease();
14872
+ }
14873
+ finishStoppedLease() {
14874
+ this.leaderWorkStopped = false;
14875
+ this.stopAutoIndexOnTeardown = true;
14876
+ this.stopping = false;
14877
+ this.clearTimers();
14878
+ this.restartAfterTeardown();
14879
+ if (!this.stopped || this.stopping) return;
14880
+ const projectKey = projectLookupKey(this.projectRoot, this.host);
14881
+ const key = controllerKey(this.identity, this.host);
14882
+ if (workers.get(key) === this) workers.delete(key);
14883
+ if (workerKeysByProject.get(projectKey) === key) workerKeysByProject.delete(projectKey);
14884
+ }
14885
+ startHeartbeat() {
14886
+ if (this.heartbeatTimer) return;
14887
+ const heartbeat = () => {
14888
+ void this.heartbeat();
14889
+ };
14890
+ this.heartbeatTimer = setInterval(heartbeat, HEARTBEAT_INTERVAL_MS);
14891
+ this.heartbeatTimer.unref?.();
14892
+ }
14893
+ async heartbeat() {
14894
+ const lease = this.lease;
14895
+ if (!lease || this.losingLeadership || this.stopped && !this.stopping) return;
14896
+ if (hasActiveReclaimMarker(lease.leasePath, lease.owner)) {
14897
+ await this.loseLeadership();
14898
+ return;
14899
+ }
14900
+ const currentOwner = readOwner(lease.leasePath);
14901
+ if (!currentOwner || !sameOwner2(currentOwner, lease.owner)) {
14902
+ await this.loseLeadership();
14903
+ return;
14904
+ }
14905
+ try {
14906
+ const nextOwner = { ...lease.owner, heartbeatAt: (/* @__PURE__ */ new Date()).toISOString() };
14907
+ if (!writeHeartbeat(lease.leasePath, nextOwner)) {
14908
+ await this.loseLeadership();
14909
+ return;
14910
+ }
14911
+ lease.owner = nextOwner;
14912
+ const refreshRequest = !this.stopping ? consumeRefreshRequest(lease.leasePath) : null;
14913
+ if (refreshRequest) {
14914
+ this.hooks.startAutoIndex("retrieval", refreshRequest.allowDisabledAutoIndex);
14915
+ }
14916
+ } catch (error) {
14917
+ const ownerAfterError = readOwner(lease.leasePath);
14918
+ if (hasActiveReclaimMarker(lease.leasePath, lease.owner) || !ownerAfterError || !sameOwner2(ownerAfterError, lease.owner)) {
14919
+ await this.loseLeadership();
14920
+ return;
14921
+ }
14922
+ console.error("[codebase-index] Failed to renew background worker lease:", error);
14923
+ }
14924
+ }
14925
+ async loseLeadership() {
14926
+ if (this.losingLeadership) return;
14927
+ this.losingLeadership = true;
14928
+ this.clearHeartbeat();
14929
+ await this.enqueue(async () => this.stopAfterLeadershipLoss());
14930
+ }
14931
+ async stopAfterLeadershipLoss() {
14932
+ const lease = this.lease;
14933
+ if (!lease) {
14934
+ this.losingLeadership = false;
14935
+ return;
14936
+ }
14937
+ try {
14938
+ const stopped = await this.stopLeaderWork(true);
14939
+ this.lease = null;
14940
+ this.losingLeadership = false;
14941
+ if (stopped.completed) {
14942
+ this.scheduleRetry();
14943
+ } else {
14944
+ void stopped.completion.then(() => this.scheduleRetry());
14945
+ }
14946
+ } catch (error) {
14947
+ console.error("[codebase-index] Failed to stop background work after losing its lease:", error);
14948
+ this.scheduleLostLeadershipTeardownRetry();
14949
+ }
14950
+ }
14951
+ scheduleRetry() {
14952
+ if (this.stopped || !this.canRun() || this.retryTimer) return;
14953
+ this.retryTimer = setTimeout(() => {
14954
+ this.retryTimer = null;
14955
+ this.start();
14956
+ }, RETRY_DELAY_MS);
14957
+ this.retryTimer.unref?.();
14958
+ }
14959
+ scheduleTeardownRetry() {
14960
+ if (!this.stopping || this.teardownRetryTimer) return;
14961
+ this.teardownRetryTimer = setTimeout(() => {
14962
+ this.teardownRetryTimer = null;
14963
+ void this.stop(this.stopAutoIndexOnTeardown).catch((error) => {
14964
+ console.error("[codebase-index] Failed to retry background worker teardown:", error);
14965
+ });
14966
+ }, RETRY_DELAY_MS);
14967
+ this.teardownRetryTimer.unref?.();
14968
+ }
14969
+ restartAfterTeardown() {
14970
+ if (!this.restartAfterStop || !this.canRun() || this.losingLeadership) return;
14971
+ this.restartAfterStop = false;
14972
+ this.stopped = false;
14973
+ this.start();
14974
+ }
14975
+ scheduleLostLeadershipTeardownRetry() {
14976
+ if (this.stopped || !this.losingLeadership || this.retryTimer) return;
14977
+ this.retryTimer = setTimeout(() => {
14978
+ this.retryTimer = null;
14979
+ void this.enqueue(async () => this.stopAfterLeadershipLoss());
14980
+ }, RETRY_DELAY_MS);
14981
+ this.retryTimer.unref?.();
14982
+ }
14983
+ clearHeartbeat() {
14984
+ if (!this.heartbeatTimer) return;
14985
+ clearInterval(this.heartbeatTimer);
14986
+ this.heartbeatTimer = null;
14987
+ }
14988
+ clearTimers() {
14989
+ this.clearHeartbeat();
14990
+ this.clearRetryTimer();
14991
+ if (this.teardownRetryTimer) {
14992
+ clearTimeout(this.teardownRetryTimer);
14993
+ this.teardownRetryTimer = null;
14994
+ }
14995
+ }
14996
+ clearRetryTimer() {
14997
+ if (!this.retryTimer) return;
14998
+ clearTimeout(this.retryTimer);
14999
+ this.retryTimer = null;
15000
+ }
15001
+ reportAcquireError(error) {
15002
+ const message = error instanceof Error ? error.message : String(error);
15003
+ if (this.reportedError === message) return;
15004
+ this.reportedError = message;
15005
+ console.error("[codebase-index] Failed to acquire background worker lease:", error);
15006
+ }
15007
+ };
15008
+ function configureBackgroundWorker(projectRoot, host, config, hooks, options = {}) {
15009
+ const projectKey = projectLookupKey(projectRoot, host);
15010
+ const identity = resolveIdentity(projectRoot, config, host);
15011
+ const key = controllerKey(identity, host);
15012
+ const previousKey = workerKeysByProject.get(projectKey);
15013
+ if (previousKey && previousKey !== key) {
15014
+ const previous = workers.get(previousKey);
15015
+ const previousBarrier = workerReplacementBarriers.get(projectKey) ?? Promise.resolve();
15016
+ const stopPrevious = previous?.stop(options.stopPreviousAutoIndex ?? true) ?? Promise.resolve();
15017
+ const activation = Promise.all([previousBarrier, stopPrevious]).then(() => void 0);
15018
+ workerReplacementBarriers.set(projectKey, activation);
15019
+ workers.delete(previousKey);
15020
+ const worker2 = new BackgroundWorkerController(projectRoot, host, config, hooks, identity);
15021
+ worker2.startAfter(activation);
15022
+ workers.set(key, worker2);
15023
+ workerKeysByProject.set(projectKey, key);
15024
+ return;
15025
+ }
15026
+ let worker = workers.get(key);
15027
+ if (!worker) {
15028
+ worker = new BackgroundWorkerController(projectRoot, host, config, hooks, identity);
15029
+ workers.set(key, worker);
15030
+ } else {
15031
+ worker.update(config, hooks, options);
15032
+ }
15033
+ workerKeysByProject.set(projectKey, key);
15034
+ worker.start();
15035
+ }
15036
+ function attachBackgroundWorkerWatcher(projectRoot, host, watcherFactory, watcherFactoryForConfig) {
15037
+ const key = workerKeysByProject.get(projectLookupKey(projectRoot, host));
15038
+ workers.get(key ?? "")?.attachWatcher(watcherFactory, watcherFactoryForConfig);
15039
+ }
15040
+ function updateBackgroundWorkerConfig(projectRoot, host, config) {
15041
+ const projectKey = projectLookupKey(projectRoot, host);
15042
+ const key = workerKeysByProject.get(projectKey);
15043
+ const worker = key ? workers.get(key) : void 0;
15044
+ if (!worker) return;
15045
+ configureBackgroundWorker(projectRoot, host, config, worker.getHooksForConfig(config), {
15046
+ stopPreviousAutoIndex: false,
15047
+ restartAutoIndex: true
15048
+ });
15049
+ }
15050
+ function requestBackgroundWorker(projectRoot, host) {
15051
+ const key = workerKeysByProject.get(projectLookupKey(projectRoot, host));
15052
+ workers.get(key ?? "")?.start();
15053
+ }
15054
+ function waitForBackgroundWorkerStart(projectRoot, host) {
15055
+ const key = workerKeysByProject.get(projectLookupKey(projectRoot, host));
15056
+ return workers.get(key ?? "")?.waitForStart() ?? Promise.resolve();
15057
+ }
15058
+ function requestBackgroundWorkerRefresh(projectRoot, host, allowDisabledAutoIndex = false) {
15059
+ const key = workerKeysByProject.get(projectLookupKey(projectRoot, host));
15060
+ workers.get(key ?? "")?.requestRefresh(allowDisabledAutoIndex);
15061
+ }
15062
+ function isBackgroundWorkerManaged(projectRoot, host) {
15063
+ const key = workerKeysByProject.get(projectLookupKey(projectRoot, host));
15064
+ return key !== void 0 && workers.has(key);
15065
+ }
15066
+ function isBackgroundWorkerLeader(projectRoot, host) {
15067
+ const key = workerKeysByProject.get(projectLookupKey(projectRoot, host));
15068
+ return key !== void 0 && workers.get(key)?.isLeader() === true;
15069
+ }
15070
+ function isBackgroundWorkerStopping(projectRoot, host) {
15071
+ const key = workerKeysByProject.get(projectLookupKey(projectRoot, host));
15072
+ return key !== void 0 && workers.get(key)?.isStopping() === true;
15073
+ }
15074
+ async function stopBackgroundWorker(projectRoot, host) {
15075
+ const projectKey = projectLookupKey(projectRoot, host);
15076
+ const key = workerKeysByProject.get(projectKey);
15077
+ const worker = key ? workers.get(key) : void 0;
15078
+ if (!worker) return;
15079
+ await worker.stop();
15080
+ }
14156
15081
 
14157
15082
  // src/utils/power-source.ts
14158
15083
  import * as childProcess from "child_process";
@@ -14162,7 +15087,7 @@ function getErrorMessage4(error) {
14162
15087
  return error instanceof Error ? error.message : String(error);
14163
15088
  }
14164
15089
  function runCommand(file, args, options) {
14165
- return new Promise((resolve20, reject) => {
15090
+ return new Promise((resolve21, reject) => {
14166
15091
  childProcess.execFile(
14167
15092
  file,
14168
15093
  args,
@@ -14172,7 +15097,7 @@ function runCommand(file, args, options) {
14172
15097
  reject(error);
14173
15098
  return;
14174
15099
  }
14175
- resolve20(stdout);
15100
+ resolve21(stdout);
14176
15101
  }
14177
15102
  );
14178
15103
  });
@@ -14264,29 +15189,29 @@ var AutoIndexCancelledError = class extends Error {
14264
15189
  function now() {
14265
15190
  return (/* @__PURE__ */ new Date()).toISOString();
14266
15191
  }
14267
- function canonicalizePath(targetPath) {
14268
- const resolved = path17.resolve(targetPath);
14269
- if (existsSync9(resolved)) {
15192
+ function canonicalizePath2(targetPath) {
15193
+ const resolved = path18.resolve(targetPath);
15194
+ if (existsSync10(resolved)) {
14270
15195
  try {
14271
- return realpathSync4.native(resolved);
15196
+ return realpathSync5.native(resolved);
14272
15197
  } catch {
14273
15198
  return resolved;
14274
15199
  }
14275
15200
  }
14276
- const parent = path17.dirname(resolved);
15201
+ const parent = path18.dirname(resolved);
14277
15202
  if (parent === resolved) return resolved;
14278
- return path17.join(canonicalizePath(parent), path17.basename(resolved));
15203
+ return path18.join(canonicalizePath2(parent), path18.basename(resolved));
14279
15204
  }
14280
15205
  function isHomeDirectory(projectRoot) {
14281
- return canonicalizePath(projectRoot) === canonicalizePath(os6.homedir());
15206
+ return canonicalizePath2(projectRoot) === canonicalizePath2(os7.homedir());
14282
15207
  }
14283
- function projectLookupKey(projectRoot, host) {
14284
- return `${host}::${canonicalizePath(projectRoot)}`;
15208
+ function projectLookupKey2(projectRoot, host) {
15209
+ return `${host}::${canonicalizePath2(projectRoot)}`;
14285
15210
  }
14286
15211
  function coordinatorKey(projectRoot, config, host) {
14287
- const canonicalProjectRoot = canonicalizePath(projectRoot);
15212
+ const canonicalProjectRoot = canonicalizePath2(projectRoot);
14288
15213
  const indexPath = resolveProjectIndexPath(projectRoot, config.scope, host);
14289
- return `${canonicalizePath(indexPath)}::${canonicalProjectRoot}`;
15214
+ return `${canonicalizePath2(indexPath)}::${canonicalProjectRoot}`;
14290
15215
  }
14291
15216
  function getProjectSafety(projectRoot, config) {
14292
15217
  if (isHomeDirectory(projectRoot)) {
@@ -14317,10 +15242,10 @@ function safeFailureMessage(error) {
14317
15242
  }
14318
15243
  function cancellableDelay(delayMs, signal) {
14319
15244
  if (signal.aborted) return Promise.reject(new AutoIndexCancelledError());
14320
- return new Promise((resolve20, reject) => {
15245
+ return new Promise((resolve21, reject) => {
14321
15246
  const timer = setTimeout(() => {
14322
15247
  signal.removeEventListener("abort", onAbort);
14323
- resolve20();
15248
+ resolve21();
14324
15249
  }, delayMs);
14325
15250
  timer.unref?.();
14326
15251
  const onAbort = () => {
@@ -14332,18 +15257,44 @@ function cancellableDelay(delayMs, signal) {
14332
15257
  }
14333
15258
  function withTimeout(promise, timeoutMs) {
14334
15259
  if (timeoutMs <= 0) return Promise.resolve(void 0);
14335
- return new Promise((resolve20) => {
14336
- const timer = setTimeout(() => resolve20(void 0), timeoutMs);
15260
+ return new Promise((resolve21) => {
15261
+ const timer = setTimeout(() => resolve21(void 0), timeoutMs);
14337
15262
  timer.unref?.();
14338
15263
  void promise.then((value) => {
14339
15264
  clearTimeout(timer);
14340
- resolve20(value);
15265
+ resolve21(value);
14341
15266
  }, () => {
14342
15267
  clearTimeout(timer);
14343
- resolve20(void 0);
15268
+ resolve21(void 0);
14344
15269
  });
14345
15270
  });
14346
15271
  }
15272
+ function settlesWithin(promise, timeoutMs) {
15273
+ if (timeoutMs <= 0) return Promise.resolve(false);
15274
+ return new Promise((resolve21) => {
15275
+ let settled = false;
15276
+ const timer = setTimeout(() => {
15277
+ if (settled) return;
15278
+ settled = true;
15279
+ resolve21(false);
15280
+ }, timeoutMs);
15281
+ timer.unref?.();
15282
+ void promise.then(
15283
+ () => {
15284
+ if (settled) return;
15285
+ settled = true;
15286
+ clearTimeout(timer);
15287
+ resolve21(true);
15288
+ },
15289
+ () => {
15290
+ if (settled) return;
15291
+ settled = true;
15292
+ clearTimeout(timer);
15293
+ resolve21(true);
15294
+ }
15295
+ );
15296
+ });
15297
+ }
14347
15298
  function requestPriority(request) {
14348
15299
  if (request.force) return 4;
14349
15300
  if (request.source === "manual") return 3;
@@ -14354,6 +15305,7 @@ function mergeRequests(current, next) {
14354
15305
  if (!current) return next;
14355
15306
  const preferred = requestPriority(next) > requestPriority(current) ? next : current;
14356
15307
  return {
15308
+ allowDisabledAutoIndex: current.allowDisabledAutoIndex || next.allowDisabledAutoIndex,
14357
15309
  checkFreshness: current.checkFreshness && next.checkFreshness,
14358
15310
  force: current.force || next.force,
14359
15311
  onProgress: next.onProgress ?? current.onProgress,
@@ -14415,11 +15367,11 @@ var AutoIndexCoordinator = class {
14415
15367
  progress: this.status.progress ? { ...this.status.progress } : void 0
14416
15368
  };
14417
15369
  }
14418
- start(source) {
15370
+ start(source, allowDisabledAutoIndex = false) {
14419
15371
  this.refreshSafety();
14420
- if (!this.registration.config.indexing.autoIndex || !this.registration.safeToRun) return null;
15372
+ if (!this.registration.config.indexing.autoIndex && !allowDisabledAutoIndex || !this.registration.safeToRun) return null;
14421
15373
  if (this.status.state === "failed") return this.inFlight;
14422
- return this.request({ checkFreshness: true, force: false, source });
15374
+ return this.request({ allowDisabledAutoIndex, checkFreshness: true, force: false, source });
14423
15375
  }
14424
15376
  request(request) {
14425
15377
  if (this.stopped) {
@@ -14494,13 +15446,15 @@ var AutoIndexCoordinator = class {
14494
15446
  retryAttempt: void 0
14495
15447
  });
14496
15448
  const inFlight = this.inFlight;
14497
- if (inFlight) {
14498
- if (waitForCompletion) {
14499
- await inFlight;
14500
- } else {
14501
- await withTimeout(inFlight, SHUTDOWN_WAIT_MS);
14502
- }
15449
+ const completion = inFlight ? inFlight.then(() => void 0, () => void 0) : Promise.resolve();
15450
+ if (!inFlight) {
15451
+ return { completed: true, completion };
14503
15452
  }
15453
+ if (waitForCompletion) {
15454
+ await completion;
15455
+ return { completed: true, completion };
15456
+ }
15457
+ return { completed: await settlesWithin(completion, SHUTDOWN_WAIT_MS), completion };
14504
15458
  }
14505
15459
  startRequest(request) {
14506
15460
  if (this.stopped || !this.canRun(request)) {
@@ -14689,7 +15643,7 @@ var AutoIndexCoordinator = class {
14689
15643
  if (request.source === "manual" || request.source === "watcher") {
14690
15644
  return true;
14691
15645
  }
14692
- return this.registration.safeToRun && this.registration.config.indexing.autoIndex;
15646
+ return this.registration.safeToRun && (this.registration.config.indexing.autoIndex || request.allowDisabledAutoIndex === true);
14693
15647
  }
14694
15648
  shouldDeferForBattery(request) {
14695
15649
  return this.registration.backgroundIndexingPolicy !== null && (request.source === "startup" || request.source === "watcher");
@@ -14722,17 +15676,17 @@ var AutoIndexCoordinator = class {
14722
15676
  }
14723
15677
  }
14724
15678
  waitForBatteryRetry(delayMs) {
14725
- return new Promise((resolve20) => {
15679
+ return new Promise((resolve21) => {
14726
15680
  const timer = setTimeout(() => {
14727
15681
  if (this.batteryRetryTimer === timer) {
14728
15682
  this.batteryRetryTimer = null;
14729
15683
  this.resolveBatteryRetry = null;
14730
15684
  }
14731
- resolve20();
15685
+ resolve21();
14732
15686
  }, delayMs);
14733
15687
  timer.unref?.();
14734
15688
  this.batteryRetryTimer = timer;
14735
- this.resolveBatteryRetry = resolve20;
15689
+ this.resolveBatteryRetry = resolve21;
14736
15690
  });
14737
15691
  }
14738
15692
  cancelBatteryRetry() {
@@ -14740,9 +15694,9 @@ var AutoIndexCoordinator = class {
14740
15694
  clearTimeout(this.batteryRetryTimer);
14741
15695
  this.batteryRetryTimer = null;
14742
15696
  }
14743
- const resolve20 = this.resolveBatteryRetry;
15697
+ const resolve21 = this.resolveBatteryRetry;
14744
15698
  this.resolveBatteryRetry = null;
14745
- resolve20?.();
15699
+ resolve21?.();
14746
15700
  }
14747
15701
  finishBatteryCheck(batteryCheck) {
14748
15702
  if (this.batteryCheck !== batteryCheck) return;
@@ -14755,12 +15709,25 @@ var AutoIndexCoordinator = class {
14755
15709
  }
14756
15710
  };
14757
15711
  function getCoordinator(projectRoot, host) {
14758
- const key = coordinatorKeysByProject.get(projectLookupKey(projectRoot, host));
15712
+ const key = coordinatorKeysByProject.get(projectLookupKey2(projectRoot, host));
14759
15713
  return key ? coordinators.get(key) ?? null : null;
14760
15714
  }
14761
- function configureAutoIndex(projectRoot, host, config, getIndexer) {
14762
- const projectKey = projectLookupKey(projectRoot, host);
15715
+ function synchronizeBackgroundWorker(projectRoot, host, config, safeToRun) {
15716
+ if (safeToRun) {
15717
+ updateBackgroundWorkerConfig(projectRoot, host, config);
15718
+ return;
15719
+ }
15720
+ void stopBackgroundWorker(projectRoot, host).catch((error) => {
15721
+ console.error("[codebase-index] Failed to stop background worker after project safety changed:", error);
15722
+ });
15723
+ }
15724
+ function configureAutoIndex(projectRoot, host, config, getIndexer, options = {}) {
15725
+ const projectKey = projectLookupKey2(projectRoot, host);
14763
15726
  const safety = getProjectSafety(projectRoot, config);
15727
+ const synchronizeWorker = options.synchronizeBackgroundWorker ?? true;
15728
+ if (options.preserveManagedWorker === true && isBackgroundWorkerManaged(projectRoot, host)) {
15729
+ return;
15730
+ }
14764
15731
  const registration = {
14765
15732
  backgroundIndexingPolicy: createBackgroundIndexingPolicy(
14766
15733
  config.indexing.pauseBackgroundIndexingOnBattery
@@ -14778,6 +15745,9 @@ function configureAutoIndex(projectRoot, host, config, getIndexer) {
14778
15745
  const stopPrevious = previousCoordinator?.stop(true) ?? Promise.resolve();
14779
15746
  const activation = Promise.all([previousBarrier, stopPrevious]).then(() => void 0);
14780
15747
  coordinatorReplacementBarriers.set(projectKey, activation);
15748
+ if (synchronizeWorker) {
15749
+ synchronizeBackgroundWorker(projectRoot, host, config, safety.safeToRun);
15750
+ }
14781
15751
  coordinators.delete(previousKey);
14782
15752
  const coordinator2 = new AutoIndexCoordinator(registration);
14783
15753
  coordinator2.activateAfter(activation);
@@ -14793,11 +15763,17 @@ function configureAutoIndex(projectRoot, host, config, getIndexer) {
14793
15763
  coordinator.update(registration);
14794
15764
  }
14795
15765
  coordinatorKeysByProject.set(projectKey, key);
15766
+ if (synchronizeWorker) {
15767
+ synchronizeBackgroundWorker(projectRoot, host, config, safety.safeToRun);
15768
+ }
14796
15769
  }
14797
- function startAutoIndex(projectRoot, host, source = "startup") {
14798
- return getCoordinator(projectRoot, host)?.start(source) ?? null;
15770
+ function startAutoIndexForBackgroundWorker(projectRoot, host, source = "startup", allowDisabledAutoIndex = false) {
15771
+ return getCoordinator(projectRoot, host)?.start(source, allowDisabledAutoIndex) ?? null;
14799
15772
  }
14800
15773
  function requestBackgroundIndex(projectRoot, host) {
15774
+ if (isBackgroundWorkerManaged(projectRoot, host) && !isBackgroundWorkerLeader(projectRoot, host)) {
15775
+ return null;
15776
+ }
14801
15777
  return getCoordinator(projectRoot, host)?.request({
14802
15778
  checkFreshness: false,
14803
15779
  force: false,
@@ -14837,15 +15813,23 @@ async function waitForAutoIndexForRetrieval(projectRoot, host) {
14837
15813
  };
14838
15814
  }
14839
15815
  try {
14840
- if (await hasReadableCurrentIndex(coordinator)) return { ready: true };
15816
+ const readiness = await getSearchReadiness(coordinator);
15817
+ if (readiness.searchable) {
15818
+ return { ready: true };
15819
+ }
15820
+ if (readiness.blocked) return unavailableSnapshotResult(readiness.reason);
14841
15821
  } catch {
14842
15822
  }
14843
- const job = coordinator.start("retrieval") ?? coordinator.currentJob();
15823
+ const job = startRetrievalRefresh(projectRoot, host, coordinator);
14844
15824
  if (job) {
14845
15825
  await withTimeout(job, coordinator.getWaitMs());
15826
+ } else if (isBackgroundWorkerManaged(projectRoot, host)) {
15827
+ await waitForPublishedSnapshot(coordinator, coordinator.getWaitMs());
14846
15828
  }
14847
15829
  try {
14848
- if (await hasReadableCurrentIndex(coordinator)) return { ready: true };
15830
+ const readiness = await getSearchReadiness(coordinator);
15831
+ if (readiness.searchable) return { ready: true };
15832
+ if (readiness.blocked) return unavailableSnapshotResult(readiness.reason);
14849
15833
  } catch {
14850
15834
  }
14851
15835
  const status = coordinator.snapshot();
@@ -14866,31 +15850,62 @@ async function waitForAutoIndexForRetrieval(projectRoot, host) {
14866
15850
  text: `Automatic indexing is ${status.state}. Retry shortly or call index_status for progress. You can also run index_codebase explicitly.`
14867
15851
  };
14868
15852
  }
14869
- async function stopAutoIndex(projectRoot, host) {
14870
- await getCoordinator(projectRoot, host)?.stop();
15853
+ async function stopAutoIndexForBackgroundWorker(projectRoot, host, waitForCompletion = false) {
15854
+ const coordinator = getCoordinator(projectRoot, host);
15855
+ if (!coordinator) {
15856
+ return { completed: true, completion: Promise.resolve() };
15857
+ }
15858
+ return coordinator.stop(waitForCompletion);
14871
15859
  }
14872
- async function hasReadableCurrentIndex(coordinator) {
15860
+ async function getSearchReadiness(coordinator) {
14873
15861
  const indexer = coordinator.getIndexer();
14874
15862
  if (indexer.getIndexFreshness) {
14875
15863
  const freshness = await indexer.getIndexFreshness();
14876
- return freshness.readable && freshness.current;
15864
+ const searchable = freshness.readable && freshness.current && freshness.reason === "current";
15865
+ return {
15866
+ blocked: freshness.reason === "unreadable" || freshness.reason === "incompatible" || freshness.reason === "failed-batches" || freshness.reason === "migration-required",
15867
+ reason: freshness.reason,
15868
+ searchable
15869
+ };
15870
+ }
15871
+ const indexed = (await indexer.getStatus()).indexed;
15872
+ return { blocked: false, searchable: indexed };
15873
+ }
15874
+ function unavailableSnapshotResult(reason) {
15875
+ 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.";
15876
+ return {
15877
+ ready: false,
15878
+ text: `${detail} Run index_codebase before retrying retrieval.`
15879
+ };
15880
+ }
15881
+ function startRetrievalRefresh(projectRoot, host, coordinator) {
15882
+ if (isBackgroundWorkerManaged(projectRoot, host)) {
15883
+ requestBackgroundWorkerRefresh(projectRoot, host, true);
15884
+ return isBackgroundWorkerLeader(projectRoot, host) ? coordinator.currentJob() : null;
15885
+ }
15886
+ return coordinator.start("retrieval") ?? coordinator.currentJob();
15887
+ }
15888
+ async function waitForPublishedSnapshot(coordinator, waitMs) {
15889
+ const deadline = Date.now() + waitMs;
15890
+ while (Date.now() < deadline) {
15891
+ if ((await getSearchReadiness(coordinator)).searchable) return;
15892
+ await new Promise((resolve21) => setTimeout(resolve21, Math.min(250, deadline - Date.now())));
14877
15893
  }
14878
- return (await indexer.getStatus()).indexed;
14879
15894
  }
14880
15895
 
14881
15896
  // src/tools/config-state.ts
14882
- import { existsSync as existsSync11, mkdirSync as mkdirSync5, writeFileSync as writeFileSync4 } from "fs";
14883
- import * as path20 from "path";
15897
+ import { existsSync as existsSync12, mkdirSync as mkdirSync6, writeFileSync as writeFileSync5 } from "fs";
15898
+ import * as path21 from "path";
14884
15899
 
14885
15900
  // src/config/merger.ts
14886
- import { existsSync as existsSync10, readFileSync as readFileSync9 } from "fs";
14887
- import * as path19 from "path";
15901
+ import { existsSync as existsSync11, readFileSync as readFileSync10 } from "fs";
15902
+ import * as path20 from "path";
14888
15903
 
14889
15904
  // src/config/rebase.ts
14890
- import * as path18 from "path";
15905
+ import * as path19 from "path";
14891
15906
  function isWithinRoot(rootDir, targetPath) {
14892
- const relativePath = path18.relative(rootDir, targetPath);
14893
- return relativePath === "" || !relativePath.startsWith("..") && !path18.isAbsolute(relativePath);
15907
+ const relativePath = path19.relative(rootDir, targetPath);
15908
+ return relativePath === "" || !relativePath.startsWith("..") && !path19.isAbsolute(relativePath);
14894
15909
  }
14895
15910
  function rebasePathEntries(values, fromDir, toDir) {
14896
15911
  if (!Array.isArray(values)) {
@@ -14898,10 +15913,10 @@ function rebasePathEntries(values, fromDir, toDir) {
14898
15913
  }
14899
15914
  return values.filter((value) => typeof value === "string").map((value) => {
14900
15915
  const trimmed = value.trim();
14901
- if (!trimmed || path18.isAbsolute(trimmed)) {
15916
+ if (!trimmed || path19.isAbsolute(trimmed)) {
14902
15917
  return trimmed;
14903
15918
  }
14904
- return normalizePathSeparators(path18.normalize(path18.relative(toDir, path18.resolve(fromDir, trimmed))));
15919
+ return normalizePathSeparators(path19.normalize(path19.relative(toDir, path19.resolve(fromDir, trimmed))));
14905
15920
  }).filter(Boolean);
14906
15921
  }
14907
15922
  function resolveInheritedKnowledgeBaseEntries(values, sourceRoot, targetRoot) {
@@ -14913,17 +15928,17 @@ function resolveInheritedKnowledgeBaseEntries(values, sourceRoot, targetRoot) {
14913
15928
  if (!trimmed) {
14914
15929
  return trimmed;
14915
15930
  }
14916
- if (path18.isAbsolute(trimmed)) {
15931
+ if (path19.isAbsolute(trimmed)) {
14917
15932
  if (isWithinRoot(sourceRoot, trimmed)) {
14918
- return normalizePathSeparators(path18.normalize(path18.relative(sourceRoot, trimmed) || "."));
15933
+ return normalizePathSeparators(path19.normalize(path19.relative(sourceRoot, trimmed) || "."));
14919
15934
  }
14920
- return path18.normalize(trimmed);
15935
+ return path19.normalize(trimmed);
14921
15936
  }
14922
- const resolvedFromSource = path18.resolve(sourceRoot, trimmed);
15937
+ const resolvedFromSource = path19.resolve(sourceRoot, trimmed);
14923
15938
  if (isWithinRoot(sourceRoot, resolvedFromSource)) {
14924
- return normalizePathSeparators(path18.normalize(trimmed));
15939
+ return normalizePathSeparators(path19.normalize(trimmed));
14925
15940
  }
14926
- return normalizePathSeparators(path18.normalize(path18.relative(targetRoot, resolvedFromSource)));
15941
+ return normalizePathSeparators(path19.normalize(path19.relative(targetRoot, resolvedFromSource)));
14927
15942
  }).filter(Boolean);
14928
15943
  }
14929
15944
 
@@ -14961,8 +15976,8 @@ function mergeUniqueStringArray(values) {
14961
15976
  return [...new Set(values.map((value) => String(value).trim()))];
14962
15977
  }
14963
15978
  function normalizeKnowledgeBasePath2(value) {
14964
- let normalized = path19.normalize(String(value).trim());
14965
- const root = path19.parse(normalized).root;
15979
+ let normalized = path20.normalize(String(value).trim());
15980
+ const root = path20.parse(normalized).root;
14966
15981
  while (normalized.length > root.length && /[\\/]$/.test(normalized)) {
14967
15982
  normalized = normalized.slice(0, -1);
14968
15983
  }
@@ -14996,11 +16011,11 @@ function validateConfigLayerShape(rawConfig, filePath) {
14996
16011
  return rawConfig;
14997
16012
  }
14998
16013
  function loadJsonFile(filePath) {
14999
- if (!existsSync10(filePath)) {
16014
+ if (!existsSync11(filePath)) {
15000
16015
  return null;
15001
16016
  }
15002
16017
  try {
15003
- const content = readFileSync9(filePath, "utf-8");
16018
+ const content = readFileSync10(filePath, "utf-8");
15004
16019
  return validateConfigLayerShape(JSON.parse(content), filePath);
15005
16020
  } catch (error) {
15006
16021
  if (error instanceof Error && error.message.startsWith("Config file ")) {
@@ -15020,7 +16035,7 @@ function loadProjectConfigLayer(projectRoot, host) {
15020
16035
  return {};
15021
16036
  }
15022
16037
  const normalizedConfig = { ...projectConfig };
15023
- const projectConfigBaseDir = path19.dirname(path19.dirname(projectConfigPath));
16038
+ const projectConfigBaseDir = path20.dirname(path20.dirname(projectConfigPath));
15024
16039
  if (Array.isArray(normalizedConfig.knowledgeBases)) {
15025
16040
  normalizedConfig.knowledgeBases = resolveInheritedKnowledgeBaseEntries(
15026
16041
  normalizedConfig.knowledgeBases,
@@ -15110,10 +16125,10 @@ function loadEditableConfig(projectRoot, host) {
15110
16125
  }
15111
16126
  function saveConfig(projectRoot, config, host) {
15112
16127
  const configPath = getConfigPath(projectRoot, host);
15113
- const configDir = path20.dirname(configPath);
15114
- const configBaseDir = path20.dirname(configDir);
15115
- if (!existsSync11(configDir)) {
15116
- mkdirSync5(configDir, { recursive: true });
16128
+ const configDir = path21.dirname(configPath);
16129
+ const configBaseDir = path21.dirname(configDir);
16130
+ if (!existsSync12(configDir)) {
16131
+ mkdirSync6(configDir, { recursive: true });
15117
16132
  }
15118
16133
  const serializableConfig = { ...config };
15119
16134
  if (Array.isArray(serializableConfig.knowledgeBases)) {
@@ -15121,7 +16136,7 @@ function saveConfig(projectRoot, config, host) {
15121
16136
  (kb) => serializeConfigPathValue(kb, configBaseDir)
15122
16137
  );
15123
16138
  }
15124
- writeFileSync4(configPath, JSON.stringify(serializableConfig, null, 2) + "\n", "utf-8");
16139
+ writeFileSync5(configPath, JSON.stringify(serializableConfig, null, 2) + "\n", "utf-8");
15125
16140
  }
15126
16141
 
15127
16142
  // src/tools/operation-runtime.ts
@@ -15200,15 +16215,24 @@ function getOrCreateIndexer(projectRoot, host) {
15200
16215
  }
15201
16216
  const indexer = new Indexer(projectRoot, config, host);
15202
16217
  indexerCache.set(key, indexer);
15203
- configureAutoIndex(projectRoot, host, config, () => getOrCreateIndexer(projectRoot, host));
16218
+ configureAutoIndex(projectRoot, host, config, () => getOrCreateIndexer(projectRoot, host), {
16219
+ preserveManagedWorker: true,
16220
+ synchronizeBackgroundWorker: false
16221
+ });
15204
16222
  return indexer;
15205
16223
  }
15206
- function initializeTools(projectRoot, config, host) {
16224
+ function initializeTools(projectRoot, config, host, options = {}) {
15207
16225
  defaultProjectRoots.set(host, projectRoot);
15208
16226
  const key = getIndexerCacheKey(projectRoot, host);
16227
+ if (options.preserveManagedWorker === true && isBackgroundWorkerManaged(projectRoot, host) && indexerCache.has(key)) {
16228
+ return;
16229
+ }
15209
16230
  configCache.set(key, config);
15210
16231
  indexerCache.set(key, new Indexer(projectRoot, config, host));
15211
- configureAutoIndex(projectRoot, host, config, () => getOrCreateIndexer(projectRoot, host));
16232
+ configureAutoIndex(projectRoot, host, config, () => getOrCreateIndexer(projectRoot, host), {
16233
+ preserveManagedWorker: options.preserveManagedWorker,
16234
+ synchronizeBackgroundWorker: false
16235
+ });
15212
16236
  }
15213
16237
  function getIndexerForProject(projectRoot, host) {
15214
16238
  const root = getProjectRoot(projectRoot, host);
@@ -15222,7 +16246,9 @@ function refreshIndexerForDirectory(projectRoot, host, config = parseConfig(load
15222
16246
  const key = getIndexerCacheKey(projectRoot, host);
15223
16247
  configCache.set(key, config);
15224
16248
  indexerCache.set(key, new Indexer(projectRoot, config, host));
15225
- configureAutoIndex(projectRoot, host, config, () => getOrCreateIndexer(projectRoot, host));
16249
+ configureAutoIndex(projectRoot, host, config, () => getOrCreateIndexer(projectRoot, host), {
16250
+ synchronizeBackgroundWorker: true
16251
+ });
15226
16252
  return config;
15227
16253
  }
15228
16254
  var AutoIndexRetrievalUnavailableError = class extends Error {
@@ -15249,7 +16275,7 @@ function trimOrUndefined(value) {
15249
16275
  return normalized || void 0;
15250
16276
  }
15251
16277
  function normalizeCallGraphPath(value) {
15252
- let normalized = path21.posix.normalize(value.trim().replaceAll("\\", "/"));
16278
+ let normalized = path22.posix.normalize(value.trim().replaceAll("\\", "/"));
15253
16279
  if (normalized.startsWith("./")) {
15254
16280
  normalized = normalized.slice(2);
15255
16281
  }
@@ -15442,12 +16468,12 @@ async function getCallGraphPath(projectRoot, host, from, to, maxDepth, fromFileP
15442
16468
  if (fromResolution.status !== "resolved" || toResolution.status !== "resolved") {
15443
16469
  return { from: fromResolution, to: toResolution, path: [] };
15444
16470
  }
15445
- const path33 = await indexer.findCallPathBySymbolIds(
16471
+ const path34 = await indexer.findCallPathBySymbolIds(
15446
16472
  fromResolution.symbolId,
15447
16473
  toResolution.symbolId,
15448
16474
  maxDepth
15449
16475
  );
15450
- return { from: fromResolution, to: toResolution, path: path33 };
16476
+ return { from: fromResolution, to: toResolution, path: path34 };
15451
16477
  }
15452
16478
  async function runIndexCodebase(projectRoot, host, args, onProgress) {
15453
16479
  const root = getProjectRoot(projectRoot, host);
@@ -15645,15 +16671,15 @@ async function getIndexLogs(projectRoot, host, args) {
15645
16671
  function addKnowledgeBase(projectRoot, host, knowledgeBasePath) {
15646
16672
  const root = getProjectRoot(projectRoot, host);
15647
16673
  const inputPath = knowledgeBasePath.trim();
15648
- const normalizedPath3 = path21.resolve(
15649
- path21.isAbsolute(inputPath) ? inputPath : resolveKnowledgeBasePath(inputPath, root)
16674
+ const normalizedPath3 = path22.resolve(
16675
+ path22.isAbsolute(inputPath) ? inputPath : resolveKnowledgeBasePath(inputPath, root)
15650
16676
  );
15651
- if (!existsSync12(normalizedPath3)) {
16677
+ if (!existsSync13(normalizedPath3)) {
15652
16678
  return `Error: Directory does not exist: ${normalizedPath3}`;
15653
16679
  }
15654
16680
  let realPath;
15655
16681
  try {
15656
- realPath = realpathSync5(normalizedPath3);
16682
+ realPath = realpathSync6(normalizedPath3);
15657
16683
  } catch {
15658
16684
  return `Error: Cannot resolve path: ${normalizedPath3}`;
15659
16685
  }
@@ -15682,7 +16708,7 @@ function addKnowledgeBase(projectRoot, host, knowledgeBasePath) {
15682
16708
  }
15683
16709
  }
15684
16710
  for (const dotDir of sensitiveDotDirs) {
15685
- const sensitiveDir = path21.join(homeDir, dotDir);
16711
+ const sensitiveDir = path22.join(homeDir, dotDir);
15686
16712
  if (sensitiveDir && (realPath === sensitiveDir || realPath.startsWith(`${sensitiveDir}/`))) {
15687
16713
  return `Error: Adding sensitive directory as knowledge base is not allowed: ${normalizedPath3}`;
15688
16714
  }
@@ -15728,7 +16754,7 @@ function listKnowledgeBases(projectRoot, host) {
15728
16754
  for (let i = 0; i < knowledgeBases.length; i++) {
15729
16755
  const kb = knowledgeBases[i];
15730
16756
  const resolvedPath = resolveKnowledgeBasePath(kb, root);
15731
- const exists = existsSync12(resolvedPath);
16757
+ const exists = existsSync13(resolvedPath);
15732
16758
  result += `[${i + 1}] ${kb}
15733
16759
  `;
15734
16760
  result += ` Resolved: ${resolvedPath}
@@ -15745,7 +16771,7 @@ function listKnowledgeBases(projectRoot, host) {
15745
16771
  }
15746
16772
  result += "\n";
15747
16773
  }
15748
- const hasHostConfig = existsSync12(path21.join(root, getHostProjectConfigRelativePath(host)));
16774
+ const hasHostConfig = existsSync13(path22.join(root, getHostProjectConfigRelativePath(host)));
15749
16775
  if (hasHostConfig) {
15750
16776
  result += `
15751
16777
  Config sources: 1 file(s).`;
@@ -16218,7 +17244,7 @@ async function resolveCodebaseContextUnmeasured(projectRoot, host, input) {
16218
17244
  const directory = input.directory ?? void 0;
16219
17245
  const tokenBudget = input.tokenBudget ?? void 0;
16220
17246
  if (from && to) {
16221
- const path33 = await getCallGraphPath(
17247
+ const path34 = await getCallGraphPath(
16222
17248
  projectRoot,
16223
17249
  host,
16224
17250
  from,
@@ -16227,25 +17253,25 @@ async function resolveCodebaseContextUnmeasured(projectRoot, host, input) {
16227
17253
  fromFilePath,
16228
17254
  toFilePath
16229
17255
  );
16230
- const pathText = formatCallGraphPathResult(path33);
16231
- if (path33.path.length > 0) {
17256
+ const pathText = formatCallGraphPathResult(path34);
17257
+ if (path34.path.length > 0) {
16232
17258
  const fitted2 = fitTextToContextBudget(
16233
17259
  pathText,
16234
17260
  tokenBudget
16235
17261
  );
16236
17262
  return {
16237
17263
  text: fitted2.text,
16238
- details: fittedDetails("path", fitted2, path33.path.length)
17264
+ details: fittedDetails("path", fitted2, path34.path.length)
16239
17265
  };
16240
17266
  }
16241
- if (path33.from.status !== "resolved" || path33.to.status !== "resolved") {
17267
+ if (path34.from.status !== "resolved" || path34.to.status !== "resolved") {
16242
17268
  const fitted2 = fitTextToContextBudget(pathText, tokenBudget);
16243
17269
  return {
16244
17270
  text: fitted2.text,
16245
17271
  details: fittedDetails("path", fitted2, 0)
16246
17272
  };
16247
17273
  }
16248
- const resolvedFrom = path33.from;
17274
+ const resolvedFrom = path34.from;
16249
17275
  const { callers } = await getCallGraphData(projectRoot, host, {
16250
17276
  name: to,
16251
17277
  direction: "callers",
@@ -16723,9 +17749,9 @@ function getRelevantEvidence(query) {
16723
17749
  });
16724
17750
  }
16725
17751
  if (query.expected.acceptableFiles) {
16726
- for (const path33 of query.expected.acceptableFiles) {
17752
+ for (const path34 of query.expected.acceptableFiles) {
16727
17753
  legacyEvidence.push({
16728
- path: path33,
17754
+ path: path34,
16729
17755
  ...query.expected.symbol !== void 0 ? { symbol: query.expected.symbol } : {},
16730
17756
  relevance: 1
16731
17757
  });
@@ -17047,9 +18073,9 @@ function computeEvalMetrics(queries, perQuery, embeddingCallCount, embeddingToke
17047
18073
  }
17048
18074
 
17049
18075
  // src/eval/runner-config.ts
17050
- import { existsSync as existsSync13, mkdirSync as mkdirSync6, readFileSync as readFileSync10, rmSync as rmSync3, writeFileSync as writeFileSync5 } from "fs";
17051
- import * as os7 from "os";
17052
- import * as path22 from "path";
18076
+ import { existsSync as existsSync14, mkdirSync as mkdirSync7, readFileSync as readFileSync11, rmSync as rmSync4, writeFileSync as writeFileSync6 } from "fs";
18077
+ import * as os8 from "os";
18078
+ import * as path23 from "path";
17053
18079
  function isRecord2(value) {
17054
18080
  return typeof value === "object" && value !== null && !Array.isArray(value);
17055
18081
  }
@@ -17083,7 +18109,7 @@ function validateEvalConfigShape(rawConfig, filePath) {
17083
18109
  }
17084
18110
  function parseJsonConfigFile(filePath) {
17085
18111
  try {
17086
- return validateEvalConfigShape(JSON.parse(readFileSync10(filePath, "utf-8")), filePath);
18112
+ return validateEvalConfigShape(JSON.parse(readFileSync11(filePath, "utf-8")), filePath);
17087
18113
  } catch (error) {
17088
18114
  if (error instanceof Error && error.message.startsWith("Eval config at ")) {
17089
18115
  throw error;
@@ -17093,20 +18119,20 @@ function parseJsonConfigFile(filePath) {
17093
18119
  }
17094
18120
  }
17095
18121
  function toAbsolute(projectRoot, maybeRelative) {
17096
- return path22.isAbsolute(maybeRelative) ? maybeRelative : path22.join(projectRoot, maybeRelative);
18122
+ return path23.isAbsolute(maybeRelative) ? maybeRelative : path23.join(projectRoot, maybeRelative);
17097
18123
  }
17098
18124
  function isProjectScopedConfigPath(configPath) {
17099
- return path22.basename(configPath) === "codebase-index.json" && path22.basename(path22.dirname(configPath)) === ".opencode";
18125
+ return path23.basename(configPath) === "codebase-index.json" && path23.basename(path23.dirname(configPath)) === ".opencode";
17100
18126
  }
17101
18127
  function normalizeEvalConfigKnowledgeBases(rawConfig, projectRoot, resolvedConfigPath) {
17102
18128
  const config = rawConfig && typeof rawConfig === "object" ? { ...rawConfig } : {};
17103
18129
  const rebaseEntries = (values) => isProjectScopedConfigPath(resolvedConfigPath) ? resolveInheritedKnowledgeBaseEntries(
17104
18130
  values,
17105
- path22.dirname(path22.dirname(resolvedConfigPath)),
18131
+ path23.dirname(path23.dirname(resolvedConfigPath)),
17106
18132
  projectRoot
17107
18133
  ) : rebasePathEntries(
17108
18134
  values,
17109
- path22.dirname(resolvedConfigPath),
18135
+ path23.dirname(resolvedConfigPath),
17110
18136
  projectRoot
17111
18137
  );
17112
18138
  if (Array.isArray(config.knowledgeBases)) {
@@ -17119,7 +18145,7 @@ function normalizeEvalConfigKnowledgeBases(rawConfig, projectRoot, resolvedConfi
17119
18145
  }
17120
18146
  function loadRawConfig(projectRoot, configPath) {
17121
18147
  const fromPath = configPath ? toAbsolute(projectRoot, configPath) : null;
17122
- if (fromPath && existsSync13(fromPath)) {
18148
+ if (fromPath && existsSync14(fromPath)) {
17123
18149
  return normalizeEvalConfigKnowledgeBases(
17124
18150
  parseJsonConfigFile(fromPath),
17125
18151
  projectRoot,
@@ -17127,15 +18153,15 @@ function loadRawConfig(projectRoot, configPath) {
17127
18153
  );
17128
18154
  }
17129
18155
  const projectConfig = resolveProjectConfigPath(projectRoot, "opencode");
17130
- if (existsSync13(projectConfig)) {
18156
+ if (existsSync14(projectConfig)) {
17131
18157
  return normalizeEvalConfigKnowledgeBases(
17132
18158
  parseJsonConfigFile(projectConfig),
17133
18159
  projectRoot,
17134
18160
  projectConfig
17135
18161
  );
17136
18162
  }
17137
- const globalConfig = path22.join(os7.homedir(), ".config", "opencode", "codebase-index.json");
17138
- if (existsSync13(globalConfig)) {
18163
+ const globalConfig = path23.join(os8.homedir(), ".config", "opencode", "codebase-index.json");
18164
+ if (existsSync14(globalConfig)) {
17139
18165
  return parseJsonConfigFile(globalConfig);
17140
18166
  }
17141
18167
  return {};
@@ -17144,24 +18170,24 @@ function getIndexRootPath(projectRoot, scope) {
17144
18170
  return scope === "global" ? getGlobalIndexPath("opencode") : resolveProjectIndexPath(projectRoot, scope, "opencode");
17145
18171
  }
17146
18172
  function getLocalProjectIndexRoot(projectRoot) {
17147
- return path22.join(projectRoot, ".opencode", "index");
18173
+ return path23.join(projectRoot, ".opencode", "index");
17148
18174
  }
17149
18175
  function getLocalProjectConfigPath(projectRoot) {
17150
- return path22.join(projectRoot, ".opencode", "codebase-index.json");
18176
+ return path23.join(projectRoot, ".opencode", "codebase-index.json");
17151
18177
  }
17152
18178
  function clearIndexRoot(projectRoot, scope) {
17153
18179
  const indexRoot = scope === "global" ? getIndexRootPath(projectRoot, scope) : getLocalProjectIndexRoot(projectRoot);
17154
- if (existsSync13(indexRoot)) {
17155
- rmSync3(indexRoot, { recursive: true, force: true });
18180
+ if (existsSync14(indexRoot)) {
18181
+ rmSync4(indexRoot, { recursive: true, force: true });
17156
18182
  }
17157
18183
  }
17158
18184
  function ensureLocalEvalProjectConfig(projectRoot, configPath) {
17159
18185
  const localConfigPath = getLocalProjectConfigPath(projectRoot);
17160
18186
  const resolvedConfigPath = configPath ? toAbsolute(projectRoot, configPath) : resolveProjectConfigPath(projectRoot, "opencode");
17161
- if (!configPath && existsSync13(localConfigPath)) {
18187
+ if (!configPath && existsSync14(localConfigPath)) {
17162
18188
  return localConfigPath;
17163
18189
  }
17164
- if (!existsSync13(resolvedConfigPath) || resolvedConfigPath === localConfigPath) {
18190
+ if (!existsSync14(resolvedConfigPath) || resolvedConfigPath === localConfigPath) {
17165
18191
  return resolvedConfigPath;
17166
18192
  }
17167
18193
  const sourceConfig = normalizeEvalConfigKnowledgeBases(
@@ -17169,8 +18195,8 @@ function ensureLocalEvalProjectConfig(projectRoot, configPath) {
17169
18195
  projectRoot,
17170
18196
  resolvedConfigPath
17171
18197
  );
17172
- mkdirSync6(path22.dirname(localConfigPath), { recursive: true });
17173
- writeFileSync5(localConfigPath, JSON.stringify(sourceConfig, null, 2), "utf-8");
18198
+ mkdirSync7(path23.dirname(localConfigPath), { recursive: true });
18199
+ writeFileSync6(localConfigPath, JSON.stringify(sourceConfig, null, 2), "utf-8");
17174
18200
  return localConfigPath;
17175
18201
  }
17176
18202
  function loadParsedConfig(projectRoot, configPath) {
@@ -17203,9 +18229,9 @@ function getEmbeddingCostPer1MTokens(embeddingProvider) {
17203
18229
  }
17204
18230
 
17205
18231
  // src/eval/schema.ts
17206
- import { readFileSync as readFileSync11 } from "fs";
18232
+ import { readFileSync as readFileSync12 } from "fs";
17207
18233
  function parseJsonFile(filePath) {
17208
- const content = readFileSync11(filePath, "utf-8");
18234
+ const content = readFileSync12(filePath, "utf-8");
17209
18235
  try {
17210
18236
  return JSON.parse(content);
17211
18237
  } catch (error) {
@@ -17222,68 +18248,68 @@ function isStringArray4(value) {
17222
18248
  function isNonEmptyString(value) {
17223
18249
  return typeof value === "string" && value.trim().length > 0;
17224
18250
  }
17225
- function asPositiveNumber(value, path33) {
18251
+ function asPositiveNumber(value, path34) {
17226
18252
  if (typeof value !== "number" || Number.isNaN(value) || value < 0) {
17227
- throw new Error(`${path33} must be a non-negative number`);
18253
+ throw new Error(`${path34} must be a non-negative number`);
17228
18254
  }
17229
18255
  return value;
17230
18256
  }
17231
- function parseQueryType(value, path33) {
18257
+ function parseQueryType(value, path34) {
17232
18258
  if (value === "definition" || value === "implementation-intent" || value === "similarity" || value === "keyword-heavy" || value === "conceptual") {
17233
18259
  return value;
17234
18260
  }
17235
18261
  throw new Error(
17236
- `${path33} must be one of: definition, implementation-intent, similarity, keyword-heavy, conceptual`
18262
+ `${path34} must be one of: definition, implementation-intent, similarity, keyword-heavy, conceptual`
17237
18263
  );
17238
18264
  }
17239
- function parseExpectedRoute(value, path33) {
18265
+ function parseExpectedRoute(value, path34) {
17240
18266
  if (value === void 0) return void 0;
17241
18267
  if (value === "search" || value === "definition") return value;
17242
- throw new Error(`${path33} must be one of: search, definition`);
18268
+ throw new Error(`${path34} must be one of: search, definition`);
17243
18269
  }
17244
- function parseExpectedOutcome(value, path33) {
18270
+ function parseExpectedOutcome(value, path34) {
17245
18271
  if (value === void 0) return void 0;
17246
18272
  if (value === "results" || value === "no-results") {
17247
18273
  return value;
17248
18274
  }
17249
- throw new Error(`${path33} must be one of: results, no-results`);
18275
+ throw new Error(`${path34} must be one of: results, no-results`);
17250
18276
  }
17251
- function parseRecoveryExpectation(value, path33) {
18277
+ function parseRecoveryExpectation(value, path34) {
17252
18278
  if (value === void 0) return void 0;
17253
18279
  if (value === "none" || value === "filter-relaxed") {
17254
18280
  return value;
17255
18281
  }
17256
- throw new Error(`${path33} must be one of: none, filter-relaxed`);
18282
+ throw new Error(`${path34} must be one of: none, filter-relaxed`);
17257
18283
  }
17258
- function parseQueryDifficulty(value, path33) {
18284
+ function parseQueryDifficulty(value, path34) {
17259
18285
  if (value === void 0) return void 0;
17260
18286
  if (value === "easy" || value === "medium" || value === "hard") {
17261
18287
  return value;
17262
18288
  }
17263
- throw new Error(`${path33} must be one of: easy, medium, hard`);
18289
+ throw new Error(`${path34} must be one of: easy, medium, hard`);
17264
18290
  }
17265
- function parseQueryTags(value, path33) {
18291
+ function parseQueryTags(value, path34) {
17266
18292
  if (value === void 0) return void 0;
17267
18293
  if (!isStringArray4(value) || value.some((tag) => tag.trim().length === 0)) {
17268
- throw new Error(`${path33} must be an array of non-empty strings`);
18294
+ throw new Error(`${path34} must be an array of non-empty strings`);
17269
18295
  }
17270
18296
  if (value.length > 16) {
17271
- throw new Error(`${path33} must contain at most 16 tags`);
18297
+ throw new Error(`${path34} must contain at most 16 tags`);
17272
18298
  }
17273
18299
  return value;
17274
18300
  }
17275
- function parseQueryArgs(value, path33) {
18301
+ function parseQueryArgs(value, path34) {
17276
18302
  if (value === void 0) return void 0;
17277
18303
  if (!isRecord3(value)) {
17278
- throw new Error(`${path33} must be an object`);
17279
- }
17280
- const symbol = parseStringOrUndefined(value.symbol, `${path33}.symbol`);
17281
- const filePath = parseStringOrUndefined(value.filePath, `${path33}.filePath`);
17282
- const fileType = parseStringOrUndefined(value.fileType, `${path33}.fileType`);
17283
- const directory = parseStringOrUndefined(value.directory, `${path33}.directory`);
17284
- const callerLimit = parsePositiveIntegerOrUndefined(value.callerLimit, `${path33}.callerLimit`);
17285
- const calleeLimit = parsePositiveIntegerOrUndefined(value.calleeLimit, `${path33}.calleeLimit`);
17286
- const tokenBudget = parsePositiveIntegerOrUndefined(value.tokenBudget, `${path33}.tokenBudget`);
18304
+ throw new Error(`${path34} must be an object`);
18305
+ }
18306
+ const symbol = parseStringOrUndefined(value.symbol, `${path34}.symbol`);
18307
+ const filePath = parseStringOrUndefined(value.filePath, `${path34}.filePath`);
18308
+ const fileType = parseStringOrUndefined(value.fileType, `${path34}.fileType`);
18309
+ const directory = parseStringOrUndefined(value.directory, `${path34}.directory`);
18310
+ const callerLimit = parsePositiveIntegerOrUndefined(value.callerLimit, `${path34}.callerLimit`);
18311
+ const calleeLimit = parsePositiveIntegerOrUndefined(value.calleeLimit, `${path34}.calleeLimit`);
18312
+ const tokenBudget = parsePositiveIntegerOrUndefined(value.tokenBudget, `${path34}.tokenBudget`);
17287
18313
  return {
17288
18314
  ...symbol !== void 0 ? { symbol } : {},
17289
18315
  ...filePath !== void 0 ? { filePath } : {},
@@ -17294,50 +18320,50 @@ function parseQueryArgs(value, path33) {
17294
18320
  ...tokenBudget !== void 0 ? { tokenBudget } : {}
17295
18321
  };
17296
18322
  }
17297
- function parsePositiveIntegerOrUndefined(value, path33) {
18323
+ function parsePositiveIntegerOrUndefined(value, path34) {
17298
18324
  if (value === void 0 || value === null) return void 0;
17299
18325
  if (typeof value !== "number" || !Number.isInteger(value) || value <= 0) {
17300
- throw new Error(`${path33} must be a positive integer`);
18326
+ throw new Error(`${path34} must be a positive integer`);
17301
18327
  }
17302
18328
  return value;
17303
18329
  }
17304
18330
  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-]+)*)?$/;
17305
- function parseSemanticVersion(value, path33) {
18331
+ function parseSemanticVersion(value, path34) {
17306
18332
  if (!isNonEmptyString(value)) {
17307
- throw new Error(`${path33} must be a non-empty string`);
18333
+ throw new Error(`${path34} must be a non-empty string`);
17308
18334
  }
17309
18335
  if (!SEMVER_VERSION_PATTERN.test(value)) {
17310
- throw new Error(`${path33} must be a valid semantic version (MAJOR.MINOR.PATCH)`);
18336
+ throw new Error(`${path34} must be a valid semantic version (MAJOR.MINOR.PATCH)`);
17311
18337
  }
17312
18338
  return value;
17313
18339
  }
17314
- function parseRetrievalMode(value, path33) {
18340
+ function parseRetrievalMode(value, path34) {
17315
18341
  if (value === void 0 || value === "search") return "search";
17316
18342
  if (value === "context" || value === "edit-context") return value;
17317
- throw new Error(`${path33} must be one of: search, context, edit-context`);
18343
+ throw new Error(`${path34} must be one of: search, context, edit-context`);
17318
18344
  }
17319
- function parseStringOrUndefined(value, path33) {
18345
+ function parseStringOrUndefined(value, path34) {
17320
18346
  if (value === void 0 || value === null) return void 0;
17321
18347
  if (!isNonEmptyString(value)) {
17322
- throw new Error(`${path33} must be a non-empty string`);
18348
+ throw new Error(`${path34} must be a non-empty string`);
17323
18349
  }
17324
18350
  return value;
17325
18351
  }
17326
- function parseGradedEvidence(value, path33) {
18352
+ function parseGradedEvidence(value, path34) {
17327
18353
  if (value === void 0) return [];
17328
18354
  if (!Array.isArray(value)) {
17329
- throw new Error(`${path33} must be an array`);
18355
+ throw new Error(`${path34} must be an array`);
17330
18356
  }
17331
18357
  return value.map((entry, index) => {
17332
18358
  if (!isRecord3(entry)) {
17333
- throw new Error(`${path33}[${index}] must be an object`);
18359
+ throw new Error(`${path34}[${index}] must be an object`);
17334
18360
  }
17335
- const evidencePath = parseStringOrUndefined(entry.path, `${path33}[${index}].path`);
18361
+ const evidencePath = parseStringOrUndefined(entry.path, `${path34}[${index}].path`);
17336
18362
  if (evidencePath === void 0) {
17337
- throw new Error(`${path33}[${index}].path is required`);
18363
+ throw new Error(`${path34}[${index}].path is required`);
17338
18364
  }
17339
- const symbol = parseStringOrUndefined(entry.symbol, `${path33}[${index}].symbol`);
17340
- const relevance = parseEvidenceRelevance(entry.relevance, `${path33}[${index}].relevance`);
18365
+ const symbol = parseStringOrUndefined(entry.symbol, `${path34}[${index}].symbol`);
18366
+ const relevance = parseEvidenceRelevance(entry.relevance, `${path34}[${index}].relevance`);
17341
18367
  return {
17342
18368
  path: evidencePath,
17343
18369
  ...symbol !== void 0 ? { symbol } : {},
@@ -17345,27 +18371,27 @@ function parseGradedEvidence(value, path33) {
17345
18371
  };
17346
18372
  });
17347
18373
  }
17348
- function parseEvidenceRelevance(value, path33) {
18374
+ function parseEvidenceRelevance(value, path34) {
17349
18375
  if (value === void 0) {
17350
- throw new Error(`${path33} is required`);
18376
+ throw new Error(`${path34} is required`);
17351
18377
  }
17352
18378
  if (value !== 1 && value !== 2 && value !== 3) {
17353
- throw new Error(`${path33} must be 1, 2, or 3`);
18379
+ throw new Error(`${path34} must be 1, 2, or 3`);
17354
18380
  }
17355
18381
  return value;
17356
18382
  }
17357
- function parseExpectedGraphNeighbor(value, path33) {
18383
+ function parseExpectedGraphNeighbor(value, path34) {
17358
18384
  if (value === void 0) return void 0;
17359
18385
  if (!isRecord3(value)) {
17360
- throw new Error(`${path33} must be an object`);
18386
+ throw new Error(`${path34} must be an object`);
17361
18387
  }
17362
18388
  if (value.direction !== "caller" && value.direction !== "callee") {
17363
- throw new Error(`${path33}.direction must be one of: caller, callee`);
18389
+ throw new Error(`${path34}.direction must be one of: caller, callee`);
17364
18390
  }
17365
- const filePath = parseStringOrUndefined(value.filePath, `${path33}.filePath`);
17366
- const symbol = parseStringOrUndefined(value.symbol, `${path33}.symbol`);
18391
+ const filePath = parseStringOrUndefined(value.filePath, `${path34}.filePath`);
18392
+ const symbol = parseStringOrUndefined(value.symbol, `${path34}.symbol`);
17367
18393
  if (filePath === void 0 && symbol === void 0) {
17368
- throw new Error(`${path33} must include filePath or symbol`);
18394
+ throw new Error(`${path34} must include filePath or symbol`);
17369
18395
  }
17370
18396
  return {
17371
18397
  direction: value.direction,
@@ -17373,9 +18399,9 @@ function parseExpectedGraphNeighbor(value, path33) {
17373
18399
  ...symbol !== void 0 ? { symbol } : {}
17374
18400
  };
17375
18401
  }
17376
- function parseExpected(input, path33) {
18402
+ function parseExpected(input, path34) {
17377
18403
  if (!isRecord3(input)) {
17378
- throw new Error(`${path33} must be an object`);
18404
+ throw new Error(`${path34} must be an object`);
17379
18405
  }
17380
18406
  const filePathRaw = input.filePath;
17381
18407
  const acceptableFilesRaw = input.acceptableFiles;
@@ -17386,29 +18412,29 @@ function parseExpected(input, path33) {
17386
18412
  const recoveryExpectationRaw = input.recoveryExpectation;
17387
18413
  const gradedEvidenceRaw = input.gradedEvidence;
17388
18414
  const graphNeighborRaw = input.graphNeighbor;
17389
- const filePath = parseStringOrUndefined(filePathRaw, `${path33}.filePath`);
18415
+ const filePath = parseStringOrUndefined(filePathRaw, `${path34}.filePath`);
17390
18416
  const acceptableFiles = isStringArray4(acceptableFilesRaw) ? acceptableFilesRaw : void 0;
17391
- const gradedEvidence = parseGradedEvidence(gradedEvidenceRaw, `${path33}.gradedEvidence`);
17392
- const graphNeighbor = parseExpectedGraphNeighbor(graphNeighborRaw, `${path33}.graphNeighbor`);
17393
- const expectedOutcome = parseExpectedOutcome(expectedOutcomeRaw, `${path33}.expectedOutcome`);
18417
+ const gradedEvidence = parseGradedEvidence(gradedEvidenceRaw, `${path34}.gradedEvidence`);
18418
+ const graphNeighbor = parseExpectedGraphNeighbor(graphNeighborRaw, `${path34}.graphNeighbor`);
18419
+ const expectedOutcome = parseExpectedOutcome(expectedOutcomeRaw, `${path34}.expectedOutcome`);
17394
18420
  if (expectedOutcome !== "no-results" && !filePath && (!acceptableFiles || acceptableFiles.length === 0) && gradedEvidence.length === 0) {
17395
18421
  throw new Error(
17396
- `${path33} must include expected.filePath, expected.acceptableFiles, or expected.gradedEvidence`
18422
+ `${path34} must include expected.filePath, expected.acceptableFiles, or expected.gradedEvidence`
17397
18423
  );
17398
18424
  }
17399
18425
  if (acceptableFilesRaw !== void 0 && !isStringArray4(acceptableFilesRaw)) {
17400
- throw new Error(`${path33}.acceptableFiles must be an array of strings`);
18426
+ throw new Error(`${path34}.acceptableFiles must be an array of strings`);
17401
18427
  }
17402
18428
  if (symbolRaw !== void 0 && typeof symbolRaw !== "string") {
17403
- throw new Error(`${path33}.symbol must be a string when provided`);
18429
+ throw new Error(`${path34}.symbol must be a string when provided`);
17404
18430
  }
17405
18431
  if (branchRaw !== void 0 && typeof branchRaw !== "string") {
17406
- throw new Error(`${path33}.branch must be a string when provided`);
18432
+ throw new Error(`${path34}.branch must be a string when provided`);
17407
18433
  }
17408
- const expectedRoute = parseExpectedRoute(expectedRouteRaw, `${path33}.expectedRoute`);
18434
+ const expectedRoute = parseExpectedRoute(expectedRouteRaw, `${path34}.expectedRoute`);
17409
18435
  const recoveryExpectation = parseRecoveryExpectation(
17410
18436
  recoveryExpectationRaw,
17411
- `${path33}.recoveryExpectation`
18437
+ `${path34}.recoveryExpectation`
17412
18438
  );
17413
18439
  return {
17414
18440
  filePath,
@@ -17422,13 +18448,13 @@ function parseExpected(input, path33) {
17422
18448
  ...graphNeighbor !== void 0 ? { graphNeighbor } : {}
17423
18449
  };
17424
18450
  }
17425
- function parseQueryLanguage(value, path33) {
17426
- return parseStringOrUndefined(value, path33);
18451
+ function parseQueryLanguage(value, path34) {
18452
+ return parseStringOrUndefined(value, path34);
17427
18453
  }
17428
18454
  function parseQuery(input, index) {
17429
- const path33 = `queries[${index}]`;
18455
+ const path34 = `queries[${index}]`;
17430
18456
  if (!isRecord3(input)) {
17431
- throw new Error(`${path33} must be an object`);
18457
+ throw new Error(`${path34} must be an object`);
17432
18458
  }
17433
18459
  const id = input.id;
17434
18460
  const query = input.query;
@@ -17440,21 +18466,21 @@ function parseQuery(input, index) {
17440
18466
  const tags = input.tags;
17441
18467
  const args = input.args;
17442
18468
  if (typeof id !== "string" || id.trim().length === 0) {
17443
- throw new Error(`${path33}.id must be a non-empty string`);
18469
+ throw new Error(`${path34}.id must be a non-empty string`);
17444
18470
  }
17445
18471
  if (typeof query !== "string" || query.trim().length === 0) {
17446
- throw new Error(`${path33}.query must be a non-empty string`);
18472
+ throw new Error(`${path34}.query must be a non-empty string`);
17447
18473
  }
17448
18474
  return {
17449
18475
  id,
17450
18476
  query,
17451
- queryType: parseQueryType(queryType, `${path33}.queryType`),
17452
- retrievalMode: parseRetrievalMode(retrievalMode, `${path33}.retrievalMode`),
17453
- language: parseQueryLanguage(language, `${path33}.language`),
17454
- difficulty: parseQueryDifficulty(difficulty, `${path33}.difficulty`),
17455
- args: parseQueryArgs(args, `${path33}.args`),
17456
- tags: parseQueryTags(tags, `${path33}.tags`),
17457
- expected: parseExpected(expected, `${path33}.expected`)
18477
+ queryType: parseQueryType(queryType, `${path34}.queryType`),
18478
+ retrievalMode: parseRetrievalMode(retrievalMode, `${path34}.retrievalMode`),
18479
+ language: parseQueryLanguage(language, `${path34}.language`),
18480
+ difficulty: parseQueryDifficulty(difficulty, `${path34}.difficulty`),
18481
+ args: parseQueryArgs(args, `${path34}.args`),
18482
+ tags: parseQueryTags(tags, `${path34}.tags`),
18483
+ expected: parseExpected(expected, `${path34}.expected`)
17458
18484
  };
17459
18485
  }
17460
18486
  function parseGoldenDataset(raw, sourceLabel) {
@@ -17870,13 +18896,13 @@ async function runEvaluation(options) {
17870
18896
  };
17871
18897
  const outputDir = createRunDirectory(toAbsolute(options.projectRoot, options.outputRoot));
17872
18898
  const perQueryArtifact = buildPerQueryArtifact(perQuery);
17873
- writeJson(path23.join(outputDir, "summary.json"), summary);
17874
- writeJson(path23.join(outputDir, "per-query.json"), perQueryArtifact);
18899
+ writeJson(path24.join(outputDir, "summary.json"), summary);
18900
+ writeJson(path24.join(outputDir, "per-query.json"), perQueryArtifact);
17875
18901
  let comparison;
17876
18902
  if (againstPath) {
17877
18903
  const baseline = loadSummary(againstPath);
17878
18904
  comparison = compareSummaries(summary, baseline, againstPath);
17879
- writeJson(path23.join(outputDir, "compare.json"), comparison);
18905
+ writeJson(path24.join(outputDir, "compare.json"), comparison);
17880
18906
  }
17881
18907
  let gate;
17882
18908
  if (options.ciMode) {
@@ -17886,10 +18912,10 @@ async function runEvaluation(options) {
17886
18912
  const budget = loadBudget(budgetPath);
17887
18913
  if (!comparison && budget.baselinePath) {
17888
18914
  const resolvedBaseline = toAbsolute(options.projectRoot, budget.baselinePath);
17889
- if (existsSync14(resolvedBaseline)) {
18915
+ if (existsSync15(resolvedBaseline)) {
17890
18916
  const baselineSummary = loadSummary(resolvedBaseline);
17891
18917
  comparison = compareSummaries(summary, baselineSummary, resolvedBaseline);
17892
- writeJson(path23.join(outputDir, "compare.json"), comparison);
18918
+ writeJson(path24.join(outputDir, "compare.json"), comparison);
17893
18919
  } else if (budget.failOnMissingBaseline) {
17894
18920
  throw new Error(
17895
18921
  `Budget baseline is missing: ${resolvedBaseline}. Set failOnMissingBaseline=false to allow CI run without baseline.`
@@ -17899,7 +18925,7 @@ async function runEvaluation(options) {
17899
18925
  gate = evaluateBudgetGate(budget, summary, comparison);
17900
18926
  }
17901
18927
  const markdown = createSummaryMarkdown(summary, comparison, gate);
17902
- writeText(path23.join(outputDir, "summary.md"), markdown);
18928
+ writeText(path24.join(outputDir, "summary.md"), markdown);
17903
18929
  return { outputDir, summary, perQuery, comparison, gate };
17904
18930
  } finally {
17905
18931
  await indexer.close();
@@ -17957,23 +18983,23 @@ async function runSweep(options, sweep) {
17957
18983
  bestByMrrAt10,
17958
18984
  bestByP95Latency
17959
18985
  };
17960
- writeJson(path23.join(outputDir, "compare.json"), aggregate);
18986
+ writeJson(path24.join(outputDir, "compare.json"), aggregate);
17961
18987
  const md = createSummaryMarkdown(
17962
18988
  bestByHitAt5?.summary ?? runs[0].summary,
17963
18989
  bestByHitAt5?.comparison,
17964
18990
  void 0,
17965
18991
  aggregate
17966
18992
  );
17967
- writeText(path23.join(outputDir, "summary.md"), md);
17968
- writeJson(path23.join(outputDir, "summary.json"), bestByHitAt5?.summary ?? runs[0].summary);
18993
+ writeText(path24.join(outputDir, "summary.md"), md);
18994
+ writeJson(path24.join(outputDir, "summary.json"), bestByHitAt5?.summary ?? runs[0].summary);
17969
18995
  return { outputDir, aggregate };
17970
18996
  }
17971
18997
 
17972
18998
  // src/eval/cli.ts
17973
- import * as path25 from "path";
18999
+ import * as path26 from "path";
17974
19000
 
17975
19001
  // src/eval/cli-parser.ts
17976
- import * as path24 from "path";
19002
+ import * as path25 from "path";
17977
19003
  function printUsage() {
17978
19004
  console.log(`
17979
19005
  Usage:
@@ -18045,12 +19071,12 @@ function parseEvalArgs(argv, cwd) {
18045
19071
  const arg = argv[i];
18046
19072
  const next = argv[i + 1];
18047
19073
  if (arg === "--project" && next) {
18048
- parsed.projectRoot = path24.resolve(cwd, next);
19074
+ parsed.projectRoot = path25.resolve(cwd, next);
18049
19075
  i += 1;
18050
19076
  continue;
18051
19077
  }
18052
19078
  if (arg === "--config" && next) {
18053
- parsed.configPath = path24.resolve(cwd, next);
19079
+ parsed.configPath = path25.resolve(cwd, next);
18054
19080
  i += 1;
18055
19081
  continue;
18056
19082
  }
@@ -18246,22 +19272,22 @@ async function handleEvalCommand(args, cwd) {
18246
19272
  if (!parsed.againstPath.endsWith(".json")) {
18247
19273
  throw new Error("eval diff --against must point to a summary JSON file");
18248
19274
  }
18249
- const currentSummary = loadSummary(path25.resolve(parsed.projectRoot, currentPath), {
19275
+ const currentSummary = loadSummary(path26.resolve(parsed.projectRoot, currentPath), {
18250
19276
  allowLegacyDiversityMetrics: true
18251
19277
  });
18252
- const baselineSummary = loadSummary(path25.resolve(parsed.projectRoot, parsed.againstPath), {
19278
+ const baselineSummary = loadSummary(path26.resolve(parsed.projectRoot, parsed.againstPath), {
18253
19279
  allowLegacyDiversityMetrics: true
18254
19280
  });
18255
19281
  const comparison = compareSummaries(
18256
19282
  currentSummary,
18257
19283
  baselineSummary,
18258
- path25.resolve(parsed.projectRoot, parsed.againstPath)
19284
+ path26.resolve(parsed.projectRoot, parsed.againstPath)
18259
19285
  );
18260
- const outputDir = createRunDirectory(path25.resolve(parsed.projectRoot, parsed.outputRoot));
19286
+ const outputDir = createRunDirectory(path26.resolve(parsed.projectRoot, parsed.outputRoot));
18261
19287
  const summaryMd = createSummaryMarkdown(currentSummary, comparison);
18262
- writeJson(path25.join(outputDir, "compare.json"), comparison);
18263
- writeText(path25.join(outputDir, "summary.md"), summaryMd);
18264
- writeJson(path25.join(outputDir, "summary.json"), currentSummary);
19288
+ writeJson(path26.join(outputDir, "compare.json"), comparison);
19289
+ writeText(path26.join(outputDir, "summary.md"), summaryMd);
19290
+ writeJson(path26.join(outputDir, "summary.json"), currentSummary);
18265
19291
  console.log(`Eval diff complete. Artifacts: ${outputDir}`);
18266
19292
  return 0;
18267
19293
  }
@@ -18272,9 +19298,9 @@ async function handleEvalCommand(args, cwd) {
18272
19298
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
18273
19299
 
18274
19300
  // src/package-metadata.ts
18275
- import { readFileSync as readFileSync12 } from "fs";
19301
+ import { readFileSync as readFileSync13 } from "fs";
18276
19302
  function getPackageVersion() {
18277
- const raw = JSON.parse(readFileSync12(new URL("../package.json", import.meta.url), "utf-8"));
19303
+ const raw = JSON.parse(readFileSync13(new URL("../package.json", import.meta.url), "utf-8"));
18278
19304
  if (raw && typeof raw === "object" && "version" in raw && typeof raw.version === "string") {
18279
19305
  return raw.version;
18280
19306
  }
@@ -18422,7 +19448,7 @@ async function executeCallGraph(projectRoot, host, args) {
18422
19448
  return { text: formatCallGraphResult(await getCallGraphData(projectRoot, host, args)) };
18423
19449
  }
18424
19450
  async function executeCallGraphPath(projectRoot, host, args) {
18425
- const path33 = await getCallGraphPath(
19451
+ const path34 = await getCallGraphPath(
18426
19452
  projectRoot,
18427
19453
  host,
18428
19454
  args.from,
@@ -18431,7 +19457,7 @@ async function executeCallGraphPath(projectRoot, host, args) {
18431
19457
  args.fromFilePath,
18432
19458
  args.toFilePath
18433
19459
  );
18434
- return { text: formatCallGraphPathResult(path33) };
19460
+ return { text: formatCallGraphPathResult(path34) };
18435
19461
  }
18436
19462
  async function executeCodeCommunities(projectRoot, host, args) {
18437
19463
  const result = await getCodeCommunities(projectRoot, host, args);
@@ -18926,10 +19952,65 @@ ${formatSearchResults(results)}` }] };
18926
19952
  }
18927
19953
 
18928
19954
  // src/adapters/mcp/server.ts
19955
+ var mcpWorkerReferences = /* @__PURE__ */ new Map();
19956
+ var mcpWorkerTeardowns = /* @__PURE__ */ new Map();
19957
+ function retainMcpBackgroundWorker(projectRoot, host) {
19958
+ const key = getBackgroundWorkerProjectKey(projectRoot, host);
19959
+ mcpWorkerReferences.set(key, (mcpWorkerReferences.get(key) ?? 0) + 1);
19960
+ }
19961
+ async function releaseMcpBackgroundWorker(projectRoot, host) {
19962
+ const key = getBackgroundWorkerProjectKey(projectRoot, host);
19963
+ const references = mcpWorkerReferences.get(key) ?? 0;
19964
+ if (references > 1) {
19965
+ mcpWorkerReferences.set(key, references - 1);
19966
+ return;
19967
+ }
19968
+ mcpWorkerReferences.delete(key);
19969
+ const teardown = stopBackgroundWorker(projectRoot, host);
19970
+ mcpWorkerTeardowns.set(key, teardown);
19971
+ try {
19972
+ await teardown;
19973
+ } finally {
19974
+ if (mcpWorkerTeardowns.get(key) === teardown) {
19975
+ mcpWorkerTeardowns.delete(key);
19976
+ }
19977
+ }
19978
+ }
18929
19979
  function getServerInstructions(host) {
18930
19980
  const hostText = `host ${host}`;
18931
19981
  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.`;
18932
19982
  }
19983
+ function configureMcpBackgroundWorker(projectRoot, config, host, watcherFactory, watcherFactoryForConfig) {
19984
+ if (!getProjectSafety(projectRoot, config).safeToRun) {
19985
+ return { managesWorker: false };
19986
+ }
19987
+ if (isBackgroundWorkerManaged(projectRoot, host)) {
19988
+ const key = getBackgroundWorkerProjectKey(projectRoot, host);
19989
+ if ((mcpWorkerReferences.get(key) ?? 0) === 0 && !mcpWorkerTeardowns.has(key)) {
19990
+ return { managesWorker: false };
19991
+ }
19992
+ if (watcherFactory !== void 0) {
19993
+ attachBackgroundWorkerWatcher(projectRoot, host, watcherFactory, watcherFactoryForConfig);
19994
+ }
19995
+ if (mcpWorkerTeardowns.has(key) || isBackgroundWorkerStopping(projectRoot, host)) {
19996
+ requestBackgroundWorker(projectRoot, host);
19997
+ }
19998
+ return { managesWorker: true };
19999
+ }
20000
+ configureBackgroundWorker(projectRoot, host, config, {
20001
+ startAutoIndex: (source, allowDisabledAutoIndex) => {
20002
+ startAutoIndexForBackgroundWorker(projectRoot, host, source, allowDisabledAutoIndex);
20003
+ },
20004
+ stopAutoIndex: () => stopAutoIndexForBackgroundWorker(projectRoot, host),
20005
+ watcherFactory,
20006
+ watcherFactoryForConfig
20007
+ });
20008
+ return { managesWorker: true };
20009
+ }
20010
+ function attachMcpBackgroundWatcher(projectRoot, config, host, watcherFactory, watcherFactoryForConfig) {
20011
+ configureMcpBackgroundWorker(projectRoot, config, host, watcherFactory, watcherFactoryForConfig);
20012
+ return waitForBackgroundWorkerStart(projectRoot, host);
20013
+ }
18933
20014
  function createMcpServer(projectRoot, config, host) {
18934
20015
  const server = new McpServer({
18935
20016
  name: MCP_SERVER_CURRENT_NAME,
@@ -18937,11 +20018,14 @@ function createMcpServer(projectRoot, config, host) {
18937
20018
  }, {
18938
20019
  instructions: getServerInstructions(host)
18939
20020
  });
18940
- initializeTools(projectRoot, config, host);
18941
- startAutoIndex(projectRoot, host, "startup");
20021
+ initializeTools(projectRoot, config, host, { preserveManagedWorker: true });
20022
+ const backgroundWorker = configureMcpBackgroundWorker(projectRoot, config, host);
20023
+ if (backgroundWorker.managesWorker) {
20024
+ retainMcpBackgroundWorker(projectRoot, host);
20025
+ }
18942
20026
  let stopCoordinationPromise = null;
18943
20027
  const stopCoordination = () => {
18944
- stopCoordinationPromise ??= stopAutoIndex(projectRoot, host);
20028
+ stopCoordinationPromise ??= backgroundWorker.managesWorker ? releaseMcpBackgroundWorker(projectRoot, host) : Promise.resolve();
18945
20029
  return stopCoordinationPromise;
18946
20030
  };
18947
20031
  const closeProtocol = server.server.close.bind(server.server);
@@ -18957,7 +20041,9 @@ function createMcpServer(projectRoot, config, host) {
18957
20041
  const onServerClose = server.server.onclose;
18958
20042
  server.server.onclose = () => {
18959
20043
  onServerClose?.();
18960
- void stopCoordination();
20044
+ void stopCoordination().catch((error) => {
20045
+ console.error("[codebase-index] Failed to stop MCP background worker after transport close:", error);
20046
+ });
18961
20047
  };
18962
20048
  registerMcpTools(server, {
18963
20049
  projectRoot,
@@ -18968,7 +20054,7 @@ function createMcpServer(projectRoot, config, host) {
18968
20054
  }
18969
20055
 
18970
20056
  // src/watcher/file-watcher.ts
18971
- import { existsSync as existsSync15, statSync as statSync6 } from "fs";
20057
+ import { existsSync as existsSync16, statSync as statSync6 } from "fs";
18972
20058
 
18973
20059
  // node_modules/chokidar/index.js
18974
20060
  import { EventEmitter as EventEmitter2 } from "events";
@@ -19060,7 +20146,7 @@ var ReaddirpStream = class extends Readable {
19060
20146
  this._directoryFilter = normalizeFilter(opts.directoryFilter);
19061
20147
  const statMethod = opts.lstat ? lstat : stat;
19062
20148
  if (wantBigintFsStats) {
19063
- this._stat = (path33) => statMethod(path33, { bigint: true });
20149
+ this._stat = (path34) => statMethod(path34, { bigint: true });
19064
20150
  } else {
19065
20151
  this._stat = statMethod;
19066
20152
  }
@@ -19085,8 +20171,8 @@ var ReaddirpStream = class extends Readable {
19085
20171
  const par = this.parent;
19086
20172
  const fil = par && par.files;
19087
20173
  if (fil && fil.length > 0) {
19088
- const { path: path33, depth } = par;
19089
- const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path33));
20174
+ const { path: path34, depth } = par;
20175
+ const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path34));
19090
20176
  const awaited = await Promise.all(slice);
19091
20177
  for (const entry of awaited) {
19092
20178
  if (!entry)
@@ -19126,21 +20212,21 @@ var ReaddirpStream = class extends Readable {
19126
20212
  this.reading = false;
19127
20213
  }
19128
20214
  }
19129
- async _exploreDir(path33, depth) {
20215
+ async _exploreDir(path34, depth) {
19130
20216
  let files;
19131
20217
  try {
19132
- files = await readdir(path33, this._rdOptions);
20218
+ files = await readdir(path34, this._rdOptions);
19133
20219
  } catch (error) {
19134
20220
  this._onError(error);
19135
20221
  }
19136
- return { files, depth, path: path33 };
20222
+ return { files, depth, path: path34 };
19137
20223
  }
19138
- async _formatEntry(dirent, path33) {
20224
+ async _formatEntry(dirent, path34) {
19139
20225
  let entry;
19140
- const basename8 = this._isDirent ? dirent.name : dirent;
20226
+ const basename9 = this._isDirent ? dirent.name : dirent;
19141
20227
  try {
19142
- const fullPath = presolve(pjoin(path33, basename8));
19143
- entry = { path: prelative(this._root, fullPath), fullPath, basename: basename8 };
20228
+ const fullPath = presolve(pjoin(path34, basename9));
20229
+ entry = { path: prelative(this._root, fullPath), fullPath, basename: basename9 };
19144
20230
  entry[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath);
19145
20231
  } catch (err) {
19146
20232
  this._onError(err);
@@ -19539,16 +20625,16 @@ var delFromSet = (main, prop, item) => {
19539
20625
  };
19540
20626
  var isEmptySet = (val) => val instanceof Set ? val.size === 0 : !val;
19541
20627
  var FsWatchInstances = /* @__PURE__ */ new Map();
19542
- function createFsWatchInstance(path33, options, listener, errHandler, emitRaw) {
20628
+ function createFsWatchInstance(path34, options, listener, errHandler, emitRaw) {
19543
20629
  const handleEvent = (rawEvent, evPath) => {
19544
- listener(path33);
19545
- emitRaw(rawEvent, evPath, { watchedPath: path33 });
19546
- if (evPath && path33 !== evPath) {
19547
- fsWatchBroadcast(sp.resolve(path33, evPath), KEY_LISTENERS, sp.join(path33, evPath));
20630
+ listener(path34);
20631
+ emitRaw(rawEvent, evPath, { watchedPath: path34 });
20632
+ if (evPath && path34 !== evPath) {
20633
+ fsWatchBroadcast(sp.resolve(path34, evPath), KEY_LISTENERS, sp.join(path34, evPath));
19548
20634
  }
19549
20635
  };
19550
20636
  try {
19551
- return fs_watch(path33, {
20637
+ return fs_watch(path34, {
19552
20638
  persistent: options.persistent
19553
20639
  }, handleEvent);
19554
20640
  } catch (error) {
@@ -19564,12 +20650,12 @@ var fsWatchBroadcast = (fullPath, listenerType, val1, val2, val3) => {
19564
20650
  listener(val1, val2, val3);
19565
20651
  });
19566
20652
  };
19567
- var setFsWatchListener = (path33, fullPath, options, handlers) => {
20653
+ var setFsWatchListener = (path34, fullPath, options, handlers) => {
19568
20654
  const { listener, errHandler, rawEmitter } = handlers;
19569
20655
  let cont = FsWatchInstances.get(fullPath);
19570
20656
  let watcher;
19571
20657
  if (!options.persistent) {
19572
- watcher = createFsWatchInstance(path33, options, listener, errHandler, rawEmitter);
20658
+ watcher = createFsWatchInstance(path34, options, listener, errHandler, rawEmitter);
19573
20659
  if (!watcher)
19574
20660
  return;
19575
20661
  return watcher.close.bind(watcher);
@@ -19580,7 +20666,7 @@ var setFsWatchListener = (path33, fullPath, options, handlers) => {
19580
20666
  addAndConvert(cont, KEY_RAW, rawEmitter);
19581
20667
  } else {
19582
20668
  watcher = createFsWatchInstance(
19583
- path33,
20669
+ path34,
19584
20670
  options,
19585
20671
  fsWatchBroadcast.bind(null, fullPath, KEY_LISTENERS),
19586
20672
  errHandler,
@@ -19595,7 +20681,7 @@ var setFsWatchListener = (path33, fullPath, options, handlers) => {
19595
20681
  cont.watcherUnusable = true;
19596
20682
  if (isWindows && error.code === "EPERM") {
19597
20683
  try {
19598
- const fd = await open(path33, "r");
20684
+ const fd = await open(path34, "r");
19599
20685
  await fd.close();
19600
20686
  broadcastErr(error);
19601
20687
  } catch (err) {
@@ -19626,7 +20712,7 @@ var setFsWatchListener = (path33, fullPath, options, handlers) => {
19626
20712
  };
19627
20713
  };
19628
20714
  var FsWatchFileInstances = /* @__PURE__ */ new Map();
19629
- var setFsWatchFileListener = (path33, fullPath, options, handlers) => {
20715
+ var setFsWatchFileListener = (path34, fullPath, options, handlers) => {
19630
20716
  const { listener, rawEmitter } = handlers;
19631
20717
  let cont = FsWatchFileInstances.get(fullPath);
19632
20718
  const copts = cont && cont.options;
@@ -19648,7 +20734,7 @@ var setFsWatchFileListener = (path33, fullPath, options, handlers) => {
19648
20734
  });
19649
20735
  const currmtime = curr.mtimeMs;
19650
20736
  if (curr.size !== prev.size || currmtime > prev.mtimeMs || currmtime === 0) {
19651
- foreach(cont.listeners, (listener2) => listener2(path33, curr));
20737
+ foreach(cont.listeners, (listener2) => listener2(path34, curr));
19652
20738
  }
19653
20739
  })
19654
20740
  };
@@ -19678,13 +20764,13 @@ var NodeFsHandler = class {
19678
20764
  * @param listener on fs change
19679
20765
  * @returns closer for the watcher instance
19680
20766
  */
19681
- _watchWithNodeFs(path33, listener) {
20767
+ _watchWithNodeFs(path34, listener) {
19682
20768
  const opts = this.fsw.options;
19683
- const directory = sp.dirname(path33);
19684
- const basename8 = sp.basename(path33);
20769
+ const directory = sp.dirname(path34);
20770
+ const basename9 = sp.basename(path34);
19685
20771
  const parent = this.fsw._getWatchedDir(directory);
19686
- parent.add(basename8);
19687
- const absolutePath = sp.resolve(path33);
20772
+ parent.add(basename9);
20773
+ const absolutePath = sp.resolve(path34);
19688
20774
  const options = {
19689
20775
  persistent: opts.persistent
19690
20776
  };
@@ -19693,13 +20779,13 @@ var NodeFsHandler = class {
19693
20779
  let closer;
19694
20780
  if (opts.usePolling) {
19695
20781
  const enableBin = opts.interval !== opts.binaryInterval;
19696
- options.interval = enableBin && isBinaryPath(basename8) ? opts.binaryInterval : opts.interval;
19697
- closer = setFsWatchFileListener(path33, absolutePath, options, {
20782
+ options.interval = enableBin && isBinaryPath(basename9) ? opts.binaryInterval : opts.interval;
20783
+ closer = setFsWatchFileListener(path34, absolutePath, options, {
19698
20784
  listener,
19699
20785
  rawEmitter: this.fsw._emitRaw
19700
20786
  });
19701
20787
  } else {
19702
- closer = setFsWatchListener(path33, absolutePath, options, {
20788
+ closer = setFsWatchListener(path34, absolutePath, options, {
19703
20789
  listener,
19704
20790
  errHandler: this._boundHandleError,
19705
20791
  rawEmitter: this.fsw._emitRaw
@@ -19715,13 +20801,13 @@ var NodeFsHandler = class {
19715
20801
  if (this.fsw.closed) {
19716
20802
  return;
19717
20803
  }
19718
- const dirname15 = sp.dirname(file);
19719
- const basename8 = sp.basename(file);
19720
- const parent = this.fsw._getWatchedDir(dirname15);
20804
+ const dirname16 = sp.dirname(file);
20805
+ const basename9 = sp.basename(file);
20806
+ const parent = this.fsw._getWatchedDir(dirname16);
19721
20807
  let prevStats = stats;
19722
- if (parent.has(basename8))
20808
+ if (parent.has(basename9))
19723
20809
  return;
19724
- const listener = async (path33, newStats) => {
20810
+ const listener = async (path34, newStats) => {
19725
20811
  if (!this.fsw._throttle(THROTTLE_MODE_WATCH, file, 5))
19726
20812
  return;
19727
20813
  if (!newStats || newStats.mtimeMs === 0) {
@@ -19735,18 +20821,18 @@ var NodeFsHandler = class {
19735
20821
  this.fsw._emit(EV.CHANGE, file, newStats2);
19736
20822
  }
19737
20823
  if ((isMacos || isLinux || isFreeBSD) && prevStats.ino !== newStats2.ino) {
19738
- this.fsw._closeFile(path33);
20824
+ this.fsw._closeFile(path34);
19739
20825
  prevStats = newStats2;
19740
20826
  const closer2 = this._watchWithNodeFs(file, listener);
19741
20827
  if (closer2)
19742
- this.fsw._addPathCloser(path33, closer2);
20828
+ this.fsw._addPathCloser(path34, closer2);
19743
20829
  } else {
19744
20830
  prevStats = newStats2;
19745
20831
  }
19746
20832
  } catch (error) {
19747
- this.fsw._remove(dirname15, basename8);
20833
+ this.fsw._remove(dirname16, basename9);
19748
20834
  }
19749
- } else if (parent.has(basename8)) {
20835
+ } else if (parent.has(basename9)) {
19750
20836
  const at = newStats.atimeMs;
19751
20837
  const mt = newStats.mtimeMs;
19752
20838
  if (!at || at <= mt || mt !== prevStats.mtimeMs) {
@@ -19771,7 +20857,7 @@ var NodeFsHandler = class {
19771
20857
  * @param item basename of this item
19772
20858
  * @returns true if no more processing is needed for this entry.
19773
20859
  */
19774
- async _handleSymlink(entry, directory, path33, item) {
20860
+ async _handleSymlink(entry, directory, path34, item) {
19775
20861
  if (this.fsw.closed) {
19776
20862
  return;
19777
20863
  }
@@ -19781,7 +20867,7 @@ var NodeFsHandler = class {
19781
20867
  this.fsw._incrReadyCount();
19782
20868
  let linkPath;
19783
20869
  try {
19784
- linkPath = await fsrealpath(path33);
20870
+ linkPath = await fsrealpath(path34);
19785
20871
  } catch (e) {
19786
20872
  this.fsw._emitReady();
19787
20873
  return true;
@@ -19791,12 +20877,12 @@ var NodeFsHandler = class {
19791
20877
  if (dir.has(item)) {
19792
20878
  if (this.fsw._symlinkPaths.get(full) !== linkPath) {
19793
20879
  this.fsw._symlinkPaths.set(full, linkPath);
19794
- this.fsw._emit(EV.CHANGE, path33, entry.stats);
20880
+ this.fsw._emit(EV.CHANGE, path34, entry.stats);
19795
20881
  }
19796
20882
  } else {
19797
20883
  dir.add(item);
19798
20884
  this.fsw._symlinkPaths.set(full, linkPath);
19799
- this.fsw._emit(EV.ADD, path33, entry.stats);
20885
+ this.fsw._emit(EV.ADD, path34, entry.stats);
19800
20886
  }
19801
20887
  this.fsw._emitReady();
19802
20888
  return true;
@@ -19826,9 +20912,9 @@ var NodeFsHandler = class {
19826
20912
  return;
19827
20913
  }
19828
20914
  const item = entry.path;
19829
- let path33 = sp.join(directory, item);
20915
+ let path34 = sp.join(directory, item);
19830
20916
  current.add(item);
19831
- if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path33, item)) {
20917
+ if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path34, item)) {
19832
20918
  return;
19833
20919
  }
19834
20920
  if (this.fsw.closed) {
@@ -19837,11 +20923,11 @@ var NodeFsHandler = class {
19837
20923
  }
19838
20924
  if (item === target || !target && !previous.has(item)) {
19839
20925
  this.fsw._incrReadyCount();
19840
- path33 = sp.join(dir, sp.relative(dir, path33));
19841
- this._addToNodeFs(path33, initialAdd, wh, depth + 1);
20926
+ path34 = sp.join(dir, sp.relative(dir, path34));
20927
+ this._addToNodeFs(path34, initialAdd, wh, depth + 1);
19842
20928
  }
19843
20929
  }).on(EV.ERROR, this._boundHandleError);
19844
- return new Promise((resolve20, reject) => {
20930
+ return new Promise((resolve21, reject) => {
19845
20931
  if (!stream)
19846
20932
  return reject();
19847
20933
  stream.once(STR_END, () => {
@@ -19850,7 +20936,7 @@ var NodeFsHandler = class {
19850
20936
  return;
19851
20937
  }
19852
20938
  const wasThrottled = throttler ? throttler.clear() : false;
19853
- resolve20(void 0);
20939
+ resolve21(void 0);
19854
20940
  previous.getChildren().filter((item) => {
19855
20941
  return item !== directory && !current.has(item);
19856
20942
  }).forEach((item) => {
@@ -19907,13 +20993,13 @@ var NodeFsHandler = class {
19907
20993
  * @param depth Child path actually targeted for watch
19908
20994
  * @param target Child path actually targeted for watch
19909
20995
  */
19910
- async _addToNodeFs(path33, initialAdd, priorWh, depth, target) {
20996
+ async _addToNodeFs(path34, initialAdd, priorWh, depth, target) {
19911
20997
  const ready = this.fsw._emitReady;
19912
- if (this.fsw._isIgnored(path33) || this.fsw.closed) {
20998
+ if (this.fsw._isIgnored(path34) || this.fsw.closed) {
19913
20999
  ready();
19914
21000
  return false;
19915
21001
  }
19916
- const wh = this.fsw._getWatchHelpers(path33);
21002
+ const wh = this.fsw._getWatchHelpers(path34);
19917
21003
  if (priorWh) {
19918
21004
  wh.filterPath = (entry) => priorWh.filterPath(entry);
19919
21005
  wh.filterDir = (entry) => priorWh.filterDir(entry);
@@ -19929,8 +21015,8 @@ var NodeFsHandler = class {
19929
21015
  const follow = this.fsw.options.followSymlinks;
19930
21016
  let closer;
19931
21017
  if (stats.isDirectory()) {
19932
- const absPath = sp.resolve(path33);
19933
- const targetPath = follow ? await fsrealpath(path33) : path33;
21018
+ const absPath = sp.resolve(path34);
21019
+ const targetPath = follow ? await fsrealpath(path34) : path34;
19934
21020
  if (this.fsw.closed)
19935
21021
  return;
19936
21022
  closer = await this._handleDir(wh.watchPath, stats, initialAdd, depth, target, wh, targetPath);
@@ -19940,29 +21026,29 @@ var NodeFsHandler = class {
19940
21026
  this.fsw._symlinkPaths.set(absPath, targetPath);
19941
21027
  }
19942
21028
  } else if (stats.isSymbolicLink()) {
19943
- const targetPath = follow ? await fsrealpath(path33) : path33;
21029
+ const targetPath = follow ? await fsrealpath(path34) : path34;
19944
21030
  if (this.fsw.closed)
19945
21031
  return;
19946
21032
  const parent = sp.dirname(wh.watchPath);
19947
21033
  this.fsw._getWatchedDir(parent).add(wh.watchPath);
19948
21034
  this.fsw._emit(EV.ADD, wh.watchPath, stats);
19949
- closer = await this._handleDir(parent, stats, initialAdd, depth, path33, wh, targetPath);
21035
+ closer = await this._handleDir(parent, stats, initialAdd, depth, path34, wh, targetPath);
19950
21036
  if (this.fsw.closed)
19951
21037
  return;
19952
21038
  if (targetPath !== void 0) {
19953
- this.fsw._symlinkPaths.set(sp.resolve(path33), targetPath);
21039
+ this.fsw._symlinkPaths.set(sp.resolve(path34), targetPath);
19954
21040
  }
19955
21041
  } else {
19956
21042
  closer = this._handleFile(wh.watchPath, stats, initialAdd);
19957
21043
  }
19958
21044
  ready();
19959
21045
  if (closer)
19960
- this.fsw._addPathCloser(path33, closer);
21046
+ this.fsw._addPathCloser(path34, closer);
19961
21047
  return false;
19962
21048
  } catch (error) {
19963
21049
  if (this.fsw._handleError(error)) {
19964
21050
  ready();
19965
- return path33;
21051
+ return path34;
19966
21052
  }
19967
21053
  }
19968
21054
  }
@@ -20005,24 +21091,24 @@ function createPattern(matcher) {
20005
21091
  }
20006
21092
  return () => false;
20007
21093
  }
20008
- function normalizePath3(path33) {
20009
- if (typeof path33 !== "string")
21094
+ function normalizePath3(path34) {
21095
+ if (typeof path34 !== "string")
20010
21096
  throw new Error("string expected");
20011
- path33 = sp2.normalize(path33);
20012
- path33 = path33.replace(/\\/g, "/");
21097
+ path34 = sp2.normalize(path34);
21098
+ path34 = path34.replace(/\\/g, "/");
20013
21099
  let prepend = false;
20014
- if (path33.startsWith("//"))
21100
+ if (path34.startsWith("//"))
20015
21101
  prepend = true;
20016
- path33 = path33.replace(DOUBLE_SLASH_RE, "/");
21102
+ path34 = path34.replace(DOUBLE_SLASH_RE, "/");
20017
21103
  if (prepend)
20018
- path33 = "/" + path33;
20019
- return path33;
21104
+ path34 = "/" + path34;
21105
+ return path34;
20020
21106
  }
20021
21107
  function matchPatterns(patterns, testString, stats) {
20022
- const path33 = normalizePath3(testString);
21108
+ const path34 = normalizePath3(testString);
20023
21109
  for (let index = 0; index < patterns.length; index++) {
20024
21110
  const pattern = patterns[index];
20025
- if (pattern(path33, stats)) {
21111
+ if (pattern(path34, stats)) {
20026
21112
  return true;
20027
21113
  }
20028
21114
  }
@@ -20060,19 +21146,19 @@ var toUnix = (string) => {
20060
21146
  }
20061
21147
  return str;
20062
21148
  };
20063
- var normalizePathToUnix = (path33) => toUnix(sp2.normalize(toUnix(path33)));
20064
- var normalizeIgnored = (cwd = "") => (path33) => {
20065
- if (typeof path33 === "string") {
20066
- return normalizePathToUnix(sp2.isAbsolute(path33) ? path33 : sp2.join(cwd, path33));
21149
+ var normalizePathToUnix = (path34) => toUnix(sp2.normalize(toUnix(path34)));
21150
+ var normalizeIgnored = (cwd = "") => (path34) => {
21151
+ if (typeof path34 === "string") {
21152
+ return normalizePathToUnix(sp2.isAbsolute(path34) ? path34 : sp2.join(cwd, path34));
20067
21153
  } else {
20068
- return path33;
21154
+ return path34;
20069
21155
  }
20070
21156
  };
20071
- var getAbsolutePath = (path33, cwd) => {
20072
- if (sp2.isAbsolute(path33)) {
20073
- return path33;
21157
+ var getAbsolutePath = (path34, cwd) => {
21158
+ if (sp2.isAbsolute(path34)) {
21159
+ return path34;
20074
21160
  }
20075
- return sp2.join(cwd, path33);
21161
+ return sp2.join(cwd, path34);
20076
21162
  };
20077
21163
  var EMPTY_SET = Object.freeze(/* @__PURE__ */ new Set());
20078
21164
  var DirEntry = class {
@@ -20137,10 +21223,10 @@ var WatchHelper = class {
20137
21223
  dirParts;
20138
21224
  followSymlinks;
20139
21225
  statMethod;
20140
- constructor(path33, follow, fsw) {
21226
+ constructor(path34, follow, fsw) {
20141
21227
  this.fsw = fsw;
20142
- const watchPath = path33;
20143
- this.path = path33 = path33.replace(REPLACER_RE, "");
21228
+ const watchPath = path34;
21229
+ this.path = path34 = path34.replace(REPLACER_RE, "");
20144
21230
  this.watchPath = watchPath;
20145
21231
  this.fullWatchPath = sp2.resolve(watchPath);
20146
21232
  this.dirParts = [];
@@ -20280,20 +21366,20 @@ var FSWatcher = class extends EventEmitter2 {
20280
21366
  this._closePromise = void 0;
20281
21367
  let paths = unifyPaths(paths_);
20282
21368
  if (cwd) {
20283
- paths = paths.map((path33) => {
20284
- const absPath = getAbsolutePath(path33, cwd);
21369
+ paths = paths.map((path34) => {
21370
+ const absPath = getAbsolutePath(path34, cwd);
20285
21371
  return absPath;
20286
21372
  });
20287
21373
  }
20288
- paths.forEach((path33) => {
20289
- this._removeIgnoredPath(path33);
21374
+ paths.forEach((path34) => {
21375
+ this._removeIgnoredPath(path34);
20290
21376
  });
20291
21377
  this._userIgnored = void 0;
20292
21378
  if (!this._readyCount)
20293
21379
  this._readyCount = 0;
20294
21380
  this._readyCount += paths.length;
20295
- Promise.all(paths.map(async (path33) => {
20296
- const res = await this._nodeFsHandler._addToNodeFs(path33, !_internal, void 0, 0, _origAdd);
21381
+ Promise.all(paths.map(async (path34) => {
21382
+ const res = await this._nodeFsHandler._addToNodeFs(path34, !_internal, void 0, 0, _origAdd);
20297
21383
  if (res)
20298
21384
  this._emitReady();
20299
21385
  return res;
@@ -20315,17 +21401,17 @@ var FSWatcher = class extends EventEmitter2 {
20315
21401
  return this;
20316
21402
  const paths = unifyPaths(paths_);
20317
21403
  const { cwd } = this.options;
20318
- paths.forEach((path33) => {
20319
- if (!sp2.isAbsolute(path33) && !this._closers.has(path33)) {
21404
+ paths.forEach((path34) => {
21405
+ if (!sp2.isAbsolute(path34) && !this._closers.has(path34)) {
20320
21406
  if (cwd)
20321
- path33 = sp2.join(cwd, path33);
20322
- path33 = sp2.resolve(path33);
21407
+ path34 = sp2.join(cwd, path34);
21408
+ path34 = sp2.resolve(path34);
20323
21409
  }
20324
- this._closePath(path33);
20325
- this._addIgnoredPath(path33);
20326
- if (this._watched.has(path33)) {
21410
+ this._closePath(path34);
21411
+ this._addIgnoredPath(path34);
21412
+ if (this._watched.has(path34)) {
20327
21413
  this._addIgnoredPath({
20328
- path: path33,
21414
+ path: path34,
20329
21415
  recursive: true
20330
21416
  });
20331
21417
  }
@@ -20389,38 +21475,38 @@ var FSWatcher = class extends EventEmitter2 {
20389
21475
  * @param stats arguments to be passed with event
20390
21476
  * @returns the error if defined, otherwise the value of the FSWatcher instance's `closed` flag
20391
21477
  */
20392
- async _emit(event, path33, stats) {
21478
+ async _emit(event, path34, stats) {
20393
21479
  if (this.closed)
20394
21480
  return;
20395
21481
  const opts = this.options;
20396
21482
  if (isWindows)
20397
- path33 = sp2.normalize(path33);
21483
+ path34 = sp2.normalize(path34);
20398
21484
  if (opts.cwd)
20399
- path33 = sp2.relative(opts.cwd, path33);
20400
- const args = [path33];
21485
+ path34 = sp2.relative(opts.cwd, path34);
21486
+ const args = [path34];
20401
21487
  if (stats != null)
20402
21488
  args.push(stats);
20403
21489
  const awf = opts.awaitWriteFinish;
20404
21490
  let pw;
20405
- if (awf && (pw = this._pendingWrites.get(path33))) {
21491
+ if (awf && (pw = this._pendingWrites.get(path34))) {
20406
21492
  pw.lastChange = /* @__PURE__ */ new Date();
20407
21493
  return this;
20408
21494
  }
20409
21495
  if (opts.atomic) {
20410
21496
  if (event === EVENTS.UNLINK) {
20411
- this._pendingUnlinks.set(path33, [event, ...args]);
21497
+ this._pendingUnlinks.set(path34, [event, ...args]);
20412
21498
  setTimeout(() => {
20413
- this._pendingUnlinks.forEach((entry, path34) => {
21499
+ this._pendingUnlinks.forEach((entry, path35) => {
20414
21500
  this.emit(...entry);
20415
21501
  this.emit(EVENTS.ALL, ...entry);
20416
- this._pendingUnlinks.delete(path34);
21502
+ this._pendingUnlinks.delete(path35);
20417
21503
  });
20418
21504
  }, typeof opts.atomic === "number" ? opts.atomic : 100);
20419
21505
  return this;
20420
21506
  }
20421
- if (event === EVENTS.ADD && this._pendingUnlinks.has(path33)) {
21507
+ if (event === EVENTS.ADD && this._pendingUnlinks.has(path34)) {
20422
21508
  event = EVENTS.CHANGE;
20423
- this._pendingUnlinks.delete(path33);
21509
+ this._pendingUnlinks.delete(path34);
20424
21510
  }
20425
21511
  }
20426
21512
  if (awf && (event === EVENTS.ADD || event === EVENTS.CHANGE) && this._readyEmitted) {
@@ -20438,16 +21524,16 @@ var FSWatcher = class extends EventEmitter2 {
20438
21524
  this.emitWithAll(event, args);
20439
21525
  }
20440
21526
  };
20441
- this._awaitWriteFinish(path33, awf.stabilityThreshold, event, awfEmit);
21527
+ this._awaitWriteFinish(path34, awf.stabilityThreshold, event, awfEmit);
20442
21528
  return this;
20443
21529
  }
20444
21530
  if (event === EVENTS.CHANGE) {
20445
- const isThrottled = !this._throttle(EVENTS.CHANGE, path33, 50);
21531
+ const isThrottled = !this._throttle(EVENTS.CHANGE, path34, 50);
20446
21532
  if (isThrottled)
20447
21533
  return this;
20448
21534
  }
20449
21535
  if (opts.alwaysStat && stats === void 0 && (event === EVENTS.ADD || event === EVENTS.ADD_DIR || event === EVENTS.CHANGE)) {
20450
- const fullPath = opts.cwd ? sp2.join(opts.cwd, path33) : path33;
21536
+ const fullPath = opts.cwd ? sp2.join(opts.cwd, path34) : path34;
20451
21537
  let stats2;
20452
21538
  try {
20453
21539
  stats2 = await stat3(fullPath);
@@ -20478,23 +21564,23 @@ var FSWatcher = class extends EventEmitter2 {
20478
21564
  * @param timeout duration of time to suppress duplicate actions
20479
21565
  * @returns tracking object or false if action should be suppressed
20480
21566
  */
20481
- _throttle(actionType, path33, timeout) {
21567
+ _throttle(actionType, path34, timeout) {
20482
21568
  if (!this._throttled.has(actionType)) {
20483
21569
  this._throttled.set(actionType, /* @__PURE__ */ new Map());
20484
21570
  }
20485
21571
  const action = this._throttled.get(actionType);
20486
21572
  if (!action)
20487
21573
  throw new Error("invalid throttle");
20488
- const actionPath = action.get(path33);
21574
+ const actionPath = action.get(path34);
20489
21575
  if (actionPath) {
20490
21576
  actionPath.count++;
20491
21577
  return false;
20492
21578
  }
20493
21579
  let timeoutObject;
20494
21580
  const clear = () => {
20495
- const item = action.get(path33);
21581
+ const item = action.get(path34);
20496
21582
  const count = item ? item.count : 0;
20497
- action.delete(path33);
21583
+ action.delete(path34);
20498
21584
  clearTimeout(timeoutObject);
20499
21585
  if (item)
20500
21586
  clearTimeout(item.timeoutObject);
@@ -20502,7 +21588,7 @@ var FSWatcher = class extends EventEmitter2 {
20502
21588
  };
20503
21589
  timeoutObject = setTimeout(clear, timeout);
20504
21590
  const thr = { timeoutObject, clear, count: 0 };
20505
- action.set(path33, thr);
21591
+ action.set(path34, thr);
20506
21592
  return thr;
20507
21593
  }
20508
21594
  _incrReadyCount() {
@@ -20516,44 +21602,44 @@ var FSWatcher = class extends EventEmitter2 {
20516
21602
  * @param event
20517
21603
  * @param awfEmit Callback to be called when ready for event to be emitted.
20518
21604
  */
20519
- _awaitWriteFinish(path33, threshold, event, awfEmit) {
21605
+ _awaitWriteFinish(path34, threshold, event, awfEmit) {
20520
21606
  const awf = this.options.awaitWriteFinish;
20521
21607
  if (typeof awf !== "object")
20522
21608
  return;
20523
21609
  const pollInterval = awf.pollInterval;
20524
21610
  let timeoutHandler;
20525
- let fullPath = path33;
20526
- if (this.options.cwd && !sp2.isAbsolute(path33)) {
20527
- fullPath = sp2.join(this.options.cwd, path33);
21611
+ let fullPath = path34;
21612
+ if (this.options.cwd && !sp2.isAbsolute(path34)) {
21613
+ fullPath = sp2.join(this.options.cwd, path34);
20528
21614
  }
20529
21615
  const now2 = /* @__PURE__ */ new Date();
20530
21616
  const writes = this._pendingWrites;
20531
21617
  function awaitWriteFinishFn(prevStat) {
20532
21618
  statcb(fullPath, (err, curStat) => {
20533
- if (err || !writes.has(path33)) {
21619
+ if (err || !writes.has(path34)) {
20534
21620
  if (err && err.code !== "ENOENT")
20535
21621
  awfEmit(err);
20536
21622
  return;
20537
21623
  }
20538
21624
  const now3 = Number(/* @__PURE__ */ new Date());
20539
21625
  if (prevStat && curStat.size !== prevStat.size) {
20540
- writes.get(path33).lastChange = now3;
21626
+ writes.get(path34).lastChange = now3;
20541
21627
  }
20542
- const pw = writes.get(path33);
21628
+ const pw = writes.get(path34);
20543
21629
  const df = now3 - pw.lastChange;
20544
21630
  if (df >= threshold) {
20545
- writes.delete(path33);
21631
+ writes.delete(path34);
20546
21632
  awfEmit(void 0, curStat);
20547
21633
  } else {
20548
21634
  timeoutHandler = setTimeout(awaitWriteFinishFn, pollInterval, curStat);
20549
21635
  }
20550
21636
  });
20551
21637
  }
20552
- if (!writes.has(path33)) {
20553
- writes.set(path33, {
21638
+ if (!writes.has(path34)) {
21639
+ writes.set(path34, {
20554
21640
  lastChange: now2,
20555
21641
  cancelWait: () => {
20556
- writes.delete(path33);
21642
+ writes.delete(path34);
20557
21643
  clearTimeout(timeoutHandler);
20558
21644
  return event;
20559
21645
  }
@@ -20564,8 +21650,8 @@ var FSWatcher = class extends EventEmitter2 {
20564
21650
  /**
20565
21651
  * Determines whether user has asked to ignore this path.
20566
21652
  */
20567
- _isIgnored(path33, stats) {
20568
- if (this.options.atomic && DOT_RE.test(path33))
21653
+ _isIgnored(path34, stats) {
21654
+ if (this.options.atomic && DOT_RE.test(path34))
20569
21655
  return true;
20570
21656
  if (!this._userIgnored) {
20571
21657
  const { cwd } = this.options;
@@ -20575,17 +21661,17 @@ var FSWatcher = class extends EventEmitter2 {
20575
21661
  const list = [...ignoredPaths.map(normalizeIgnored(cwd)), ...ignored];
20576
21662
  this._userIgnored = anymatch(list, void 0);
20577
21663
  }
20578
- return this._userIgnored(path33, stats);
21664
+ return this._userIgnored(path34, stats);
20579
21665
  }
20580
- _isntIgnored(path33, stat5) {
20581
- return !this._isIgnored(path33, stat5);
21666
+ _isntIgnored(path34, stat5) {
21667
+ return !this._isIgnored(path34, stat5);
20582
21668
  }
20583
21669
  /**
20584
21670
  * Provides a set of common helpers and properties relating to symlink handling.
20585
21671
  * @param path file or directory pattern being watched
20586
21672
  */
20587
- _getWatchHelpers(path33) {
20588
- return new WatchHelper(path33, this.options.followSymlinks, this);
21673
+ _getWatchHelpers(path34) {
21674
+ return new WatchHelper(path34, this.options.followSymlinks, this);
20589
21675
  }
20590
21676
  // Directory helpers
20591
21677
  // -----------------
@@ -20617,63 +21703,63 @@ var FSWatcher = class extends EventEmitter2 {
20617
21703
  * @param item base path of item/directory
20618
21704
  */
20619
21705
  _remove(directory, item, isDirectory) {
20620
- const path33 = sp2.join(directory, item);
20621
- const fullPath = sp2.resolve(path33);
20622
- isDirectory = isDirectory != null ? isDirectory : this._watched.has(path33) || this._watched.has(fullPath);
20623
- if (!this._throttle("remove", path33, 100))
21706
+ const path34 = sp2.join(directory, item);
21707
+ const fullPath = sp2.resolve(path34);
21708
+ isDirectory = isDirectory != null ? isDirectory : this._watched.has(path34) || this._watched.has(fullPath);
21709
+ if (!this._throttle("remove", path34, 100))
20624
21710
  return;
20625
21711
  if (!isDirectory && this._watched.size === 1) {
20626
21712
  this.add(directory, item, true);
20627
21713
  }
20628
- const wp = this._getWatchedDir(path33);
21714
+ const wp = this._getWatchedDir(path34);
20629
21715
  const nestedDirectoryChildren = wp.getChildren();
20630
- nestedDirectoryChildren.forEach((nested) => this._remove(path33, nested));
21716
+ nestedDirectoryChildren.forEach((nested) => this._remove(path34, nested));
20631
21717
  const parent = this._getWatchedDir(directory);
20632
21718
  const wasTracked = parent.has(item);
20633
21719
  parent.remove(item);
20634
21720
  if (this._symlinkPaths.has(fullPath)) {
20635
21721
  this._symlinkPaths.delete(fullPath);
20636
21722
  }
20637
- let relPath = path33;
21723
+ let relPath = path34;
20638
21724
  if (this.options.cwd)
20639
- relPath = sp2.relative(this.options.cwd, path33);
21725
+ relPath = sp2.relative(this.options.cwd, path34);
20640
21726
  if (this.options.awaitWriteFinish && this._pendingWrites.has(relPath)) {
20641
21727
  const event = this._pendingWrites.get(relPath).cancelWait();
20642
21728
  if (event === EVENTS.ADD)
20643
21729
  return;
20644
21730
  }
20645
- this._watched.delete(path33);
21731
+ this._watched.delete(path34);
20646
21732
  this._watched.delete(fullPath);
20647
21733
  const eventName = isDirectory ? EVENTS.UNLINK_DIR : EVENTS.UNLINK;
20648
- if (wasTracked && !this._isIgnored(path33))
20649
- this._emit(eventName, path33);
20650
- this._closePath(path33);
21734
+ if (wasTracked && !this._isIgnored(path34))
21735
+ this._emit(eventName, path34);
21736
+ this._closePath(path34);
20651
21737
  }
20652
21738
  /**
20653
21739
  * Closes all watchers for a path
20654
21740
  */
20655
- _closePath(path33) {
20656
- this._closeFile(path33);
20657
- const dir = sp2.dirname(path33);
20658
- this._getWatchedDir(dir).remove(sp2.basename(path33));
21741
+ _closePath(path34) {
21742
+ this._closeFile(path34);
21743
+ const dir = sp2.dirname(path34);
21744
+ this._getWatchedDir(dir).remove(sp2.basename(path34));
20659
21745
  }
20660
21746
  /**
20661
21747
  * Closes only file-specific watchers
20662
21748
  */
20663
- _closeFile(path33) {
20664
- const closers = this._closers.get(path33);
21749
+ _closeFile(path34) {
21750
+ const closers = this._closers.get(path34);
20665
21751
  if (!closers)
20666
21752
  return;
20667
21753
  closers.forEach((closer) => closer());
20668
- this._closers.delete(path33);
21754
+ this._closers.delete(path34);
20669
21755
  }
20670
- _addPathCloser(path33, closer) {
21756
+ _addPathCloser(path34, closer) {
20671
21757
  if (!closer)
20672
21758
  return;
20673
- let list = this._closers.get(path33);
21759
+ let list = this._closers.get(path34);
20674
21760
  if (!list) {
20675
21761
  list = [];
20676
- this._closers.set(path33, list);
21762
+ this._closers.set(path34, list);
20677
21763
  }
20678
21764
  list.push(closer);
20679
21765
  }
@@ -20703,11 +21789,11 @@ function watch(paths, options = {}) {
20703
21789
  var chokidar_default = { watch, FSWatcher };
20704
21790
 
20705
21791
  // src/watcher/file-watcher.ts
20706
- import * as path28 from "path";
21792
+ import * as path29 from "path";
20707
21793
 
20708
21794
  // src/watcher/native-recursive-watcher.ts
20709
21795
  import { watch as watch2 } from "fs";
20710
- import * as path26 from "path";
21796
+ import * as path27 from "path";
20711
21797
  var NativeRecursiveWatcher = class {
20712
21798
  constructor(root, onChange, options = {}) {
20713
21799
  this.root = root;
@@ -20755,9 +21841,9 @@ var NativeRecursiveWatcher = class {
20755
21841
  toAbsolutePath(filename) {
20756
21842
  if (filename == null) return null;
20757
21843
  const normalizedFilename = typeof filename === "string" ? filename : filename.toString();
20758
- const absolutePath = path26.resolve(this.root, normalizedFilename);
20759
- const relativePath = path26.relative(this.root, absolutePath);
20760
- const outsideRoot = relativePath === ".." || relativePath.startsWith(`..${path26.sep}`) || path26.isAbsolute(relativePath);
21844
+ const absolutePath = path27.resolve(this.root, normalizedFilename);
21845
+ const relativePath = path27.relative(this.root, absolutePath);
21846
+ const outsideRoot = relativePath === ".." || relativePath.startsWith(`..${path27.sep}`) || path27.isAbsolute(relativePath);
20761
21847
  return outsideRoot ? null : absolutePath;
20762
21848
  }
20763
21849
  defaultWatchFactory = (root, listener, options) => watch2(root, options, listener);
@@ -20765,16 +21851,16 @@ var NativeRecursiveWatcher = class {
20765
21851
 
20766
21852
  // src/watcher/snapshot.ts
20767
21853
  import * as fsPromises4 from "fs/promises";
20768
- import * as path27 from "path";
21854
+ import * as path28 from "path";
20769
21855
  async function buildFileSnapshotScan(projectRoot, config, configPaths = []) {
20770
- const normalizedProjectRoot = path27.resolve(projectRoot);
21856
+ const normalizedProjectRoot = path28.resolve(projectRoot);
20771
21857
  const ignoreFilter = createIgnoreFilter(normalizedProjectRoot);
20772
21858
  const includePatterns = [...config.include, ...config.additionalInclude ?? []];
20773
21859
  const maxDepth = config.indexing?.maxDepth ?? -1;
20774
21860
  const snapshot = /* @__PURE__ */ new Map();
20775
21861
  const unreadablePrefixes = /* @__PURE__ */ new Set();
20776
21862
  const includeFile = async (filePath) => {
20777
- const normalizedPath3 = path27.resolve(filePath);
21863
+ const normalizedPath3 = path28.resolve(filePath);
20778
21864
  if (!shouldIncludeFile(normalizedPath3, normalizedProjectRoot, includePatterns, config.exclude, ignoreFilter)) return;
20779
21865
  const stat5 = await readStatIfFile(normalizedPath3, unreadablePrefixes);
20780
21866
  if (stat5) snapshot.set(normalizedPath3, { size: stat5.size, mtimeMs: stat5.mtimeMs });
@@ -20786,16 +21872,16 @@ async function buildFileSnapshotScan(projectRoot, config, configPaths = []) {
20786
21872
  } catch (error) {
20787
21873
  if (isMissingFsError(error)) return;
20788
21874
  if (isPermissionFsError(error)) {
20789
- unreadablePrefixes.add(path27.resolve(directoryPath));
21875
+ unreadablePrefixes.add(path28.resolve(directoryPath));
20790
21876
  return;
20791
21877
  }
20792
21878
  throw error;
20793
21879
  }
20794
21880
  for (const entry of entries) {
20795
- const fullPath = path27.join(directoryPath, entry.name);
20796
- const relativePath = path27.relative(normalizedProjectRoot, fullPath);
21881
+ const fullPath = path28.join(directoryPath, entry.name);
21882
+ const relativePath = path28.relative(normalizedProjectRoot, fullPath);
20797
21883
  if (entry.isDirectory()) {
20798
- if (hasFilteredPathSegment(relativePath, path27.sep) || isRestrictedDirectory(relativePath, path27.sep)) continue;
21884
+ if (hasFilteredPathSegment(relativePath, path28.sep) || isRestrictedDirectory(relativePath, path28.sep)) continue;
20799
21885
  if (ignoreFilter.ignores(relativePath)) continue;
20800
21886
  if (maxDepth === -1 || depth < maxDepth) await walk(fullPath, depth + 1);
20801
21887
  } else if (entry.isFile()) {
@@ -20808,19 +21894,19 @@ async function buildFileSnapshotScan(projectRoot, config, configPaths = []) {
20808
21894
  return { entries: snapshot, unreadablePrefixes };
20809
21895
  }
20810
21896
  async function buildFileSnapshotForPathScan(projectRoot, config, configPaths, targetPath) {
20811
- const normalizedProjectRoot = path27.resolve(projectRoot);
20812
- const normalizedTargetPath = path27.resolve(targetPath);
21897
+ const normalizedProjectRoot = path28.resolve(projectRoot);
21898
+ const normalizedTargetPath = path28.resolve(targetPath);
20813
21899
  if (!isWithinPath(normalizedProjectRoot, normalizedTargetPath)) {
20814
21900
  return { entries: /* @__PURE__ */ new Map(), unreadablePrefixes: /* @__PURE__ */ new Set() };
20815
21901
  }
20816
21902
  const ignoreFilter = createIgnoreFilter(normalizedProjectRoot);
20817
21903
  const includePatterns = [...config.include, ...config.additionalInclude ?? []];
20818
21904
  const maxDepth = config.indexing?.maxDepth ?? -1;
20819
- const explicitConfigPaths = new Set(configPaths.map((configPath) => path27.resolve(configPath)));
21905
+ const explicitConfigPaths = new Set(configPaths.map((configPath) => path28.resolve(configPath)));
20820
21906
  const snapshot = /* @__PURE__ */ new Map();
20821
21907
  const unreadablePrefixes = /* @__PURE__ */ new Set();
20822
21908
  const includeFile = async (filePath) => {
20823
- const normalizedPath3 = path27.resolve(filePath);
21909
+ const normalizedPath3 = path28.resolve(filePath);
20824
21910
  if (!explicitConfigPaths.has(normalizedPath3) && !shouldIncludeFile(
20825
21911
  normalizedPath3,
20826
21912
  normalizedProjectRoot,
@@ -20838,16 +21924,16 @@ async function buildFileSnapshotForPathScan(projectRoot, config, configPaths, ta
20838
21924
  } catch (error) {
20839
21925
  if (isMissingFsError(error)) return;
20840
21926
  if (isPermissionFsError(error)) {
20841
- unreadablePrefixes.add(path27.resolve(directoryPath));
21927
+ unreadablePrefixes.add(path28.resolve(directoryPath));
20842
21928
  return;
20843
21929
  }
20844
21930
  throw error;
20845
21931
  }
20846
21932
  for (const entry of entries) {
20847
- const fullPath = path27.join(directoryPath, entry.name);
20848
- const relativePath = path27.relative(normalizedProjectRoot, fullPath);
21933
+ const fullPath = path28.join(directoryPath, entry.name);
21934
+ const relativePath = path28.relative(normalizedProjectRoot, fullPath);
20849
21935
  if (entry.isDirectory()) {
20850
- if (hasFilteredPathSegment(relativePath, path27.sep) || isRestrictedDirectory(relativePath, path27.sep)) continue;
21936
+ if (hasFilteredPathSegment(relativePath, path28.sep) || isRestrictedDirectory(relativePath, path28.sep)) continue;
20851
21937
  if (ignoreFilter.ignores(relativePath)) continue;
20852
21938
  if (maxDepth === -1 || depth < maxDepth) await walk(fullPath, depth + 1);
20853
21939
  } else if (entry.isFile()) {
@@ -20871,7 +21957,7 @@ function completeFileSnapshot(previous, scan) {
20871
21957
  return completed;
20872
21958
  }
20873
21959
  async function includeExplicitConfigPaths(snapshot, unreadablePrefixes, configPaths) {
20874
- for (const configPath of [...new Set(configPaths.map((value) => path27.resolve(value)))]) {
21960
+ for (const configPath of [...new Set(configPaths.map((value) => path28.resolve(value)))]) {
20875
21961
  if (snapshot.has(configPath)) continue;
20876
21962
  const stat5 = await readStatIfFile(configPath, unreadablePrefixes);
20877
21963
  if (stat5) snapshot.set(configPath, { size: stat5.size, mtimeMs: stat5.mtimeMs });
@@ -20881,12 +21967,12 @@ async function includeExplicitConfigPathsInPath(snapshot, unreadablePrefixes, co
20881
21967
  await includeExplicitConfigPaths(
20882
21968
  snapshot,
20883
21969
  unreadablePrefixes,
20884
- configPaths.filter((configPath) => isWithinPath(targetPath, path27.resolve(configPath)))
21970
+ configPaths.filter((configPath) => isWithinPath(targetPath, path28.resolve(configPath)))
20885
21971
  );
20886
21972
  }
20887
21973
  function isWithinPath(parentPath, childPath) {
20888
- const relativePath = path27.relative(parentPath, childPath);
20889
- return relativePath === "" || !relativePath.startsWith(`..${path27.sep}`) && relativePath !== ".." && !path27.isAbsolute(relativePath);
21974
+ const relativePath = path28.relative(parentPath, childPath);
21975
+ return relativePath === "" || !relativePath.startsWith(`..${path28.sep}`) && relativePath !== ".." && !path28.isAbsolute(relativePath);
20890
21976
  }
20891
21977
  async function readStatIfFile(filePath, unreadablePrefixes) {
20892
21978
  try {
@@ -20895,7 +21981,7 @@ async function readStatIfFile(filePath, unreadablePrefixes) {
20895
21981
  } catch (error) {
20896
21982
  if (isMissingFsError(error)) return null;
20897
21983
  if (isPermissionFsError(error)) {
20898
- unreadablePrefixes.add(path27.resolve(filePath));
21984
+ unreadablePrefixes.add(path28.resolve(filePath));
20899
21985
  return null;
20900
21986
  }
20901
21987
  throw error;
@@ -21032,8 +22118,8 @@ var FileWatcher = class {
21032
22118
  this.createWatcher();
21033
22119
  }
21034
22120
  resetReady() {
21035
- this.readyPromise = new Promise((resolve20) => {
21036
- this.resolveReady = resolve20;
22121
+ this.readyPromise = new Promise((resolve21) => {
22122
+ this.resolveReady = resolve21;
21037
22123
  });
21038
22124
  this.startupReadySignals = 1;
21039
22125
  }
@@ -21064,7 +22150,7 @@ var FileWatcher = class {
21064
22150
  const resolvedWatchTargets = watchTargets ?? this.getFullChokidarWatchTargets();
21065
22151
  const watcherOptions = {
21066
22152
  ignored: (filePath) => {
21067
- const relativePath = path28.relative(this.projectRoot, filePath);
22153
+ const relativePath = path29.relative(this.projectRoot, filePath);
21068
22154
  if (!relativePath) return false;
21069
22155
  if (this.isProjectConfigPathOrAncestor(relativePath)) {
21070
22156
  return false;
@@ -21072,10 +22158,10 @@ var FileWatcher = class {
21072
22158
  if (this.isOutsideProjectPath(relativePath)) {
21073
22159
  return true;
21074
22160
  }
21075
- if (hasFilteredPathSegment(relativePath, path28.sep)) {
22161
+ if (hasFilteredPathSegment(relativePath, path29.sep)) {
21076
22162
  return true;
21077
22163
  }
21078
- if (isRestrictedDirectory(relativePath, path28.sep)) {
22164
+ if (isRestrictedDirectory(relativePath, path29.sep)) {
21079
22165
  return true;
21080
22166
  }
21081
22167
  if (ignoreFilter.ignores(relativePath)) {
@@ -21166,13 +22252,13 @@ var FileWatcher = class {
21166
22252
  getExternalConfigWatchTargets() {
21167
22253
  return [...new Set(
21168
22254
  this.projectConfigPaths.filter((projectConfigPath) => {
21169
- const relativeConfigPath = path28.relative(this.projectRoot, projectConfigPath);
22255
+ const relativeConfigPath = path29.relative(this.projectRoot, projectConfigPath);
21170
22256
  return this.isOutsideProjectPath(relativeConfigPath);
21171
22257
  }).map((projectConfigPath) => {
21172
- if (existsSync15(projectConfigPath)) {
22258
+ if (existsSync16(projectConfigPath)) {
21173
22259
  return projectConfigPath;
21174
22260
  }
21175
- return this.getNearestExistingDirectory(path28.dirname(projectConfigPath));
22261
+ return this.getNearestExistingDirectory(path29.dirname(projectConfigPath));
21176
22262
  })
21177
22263
  )];
21178
22264
  }
@@ -21234,7 +22320,7 @@ var FileWatcher = class {
21234
22320
  }
21235
22321
  scheduleNativeReconciliation(generation, filePath) {
21236
22322
  if (!this.isCurrentNativeSetup(generation)) return;
21237
- const requiresFullReconciliation = filePath === path28.join(this.projectRoot, ".gitignore");
22323
+ const requiresFullReconciliation = filePath === path29.join(this.projectRoot, ".gitignore");
21238
22324
  const invalidatedPath = requiresFullReconciliation ? null : filePath;
21239
22325
  this.nativeInvalidatedPaths.set(invalidatedPath, invalidatedPath !== null);
21240
22326
  if (this.nativeReconcileTimer) {
@@ -21329,23 +22415,23 @@ var FileWatcher = class {
21329
22415
  this.scheduleFlush();
21330
22416
  }
21331
22417
  isProjectConfigPath(filePath) {
21332
- const relativePath = path28.relative(this.projectRoot, filePath);
21333
- const normalizedRelativePath = path28.normalize(relativePath);
22418
+ const relativePath = path29.relative(this.projectRoot, filePath);
22419
+ const normalizedRelativePath = path29.normalize(relativePath);
21334
22420
  return this.getProjectConfigRelativePaths().some((configPath) => configPath === normalizedRelativePath);
21335
22421
  }
21336
22422
  isProjectConfigPathOrAncestor(relativePath) {
21337
- const normalizedRelativePath = path28.normalize(relativePath);
22423
+ const normalizedRelativePath = path29.normalize(relativePath);
21338
22424
  return this.getProjectConfigRelativePaths().some(
21339
- (configPath) => configPath === normalizedRelativePath || configPath.startsWith(`${normalizedRelativePath}${path28.sep}`)
22425
+ (configPath) => configPath === normalizedRelativePath || configPath.startsWith(`${normalizedRelativePath}${path29.sep}`)
21340
22426
  );
21341
22427
  }
21342
22428
  isOutsideProjectPath(relativePath) {
21343
- return relativePath === ".." || relativePath.startsWith(`..${path28.sep}`) || path28.isAbsolute(relativePath);
22429
+ return relativePath === ".." || relativePath.startsWith(`..${path29.sep}`) || path29.isAbsolute(relativePath);
21344
22430
  }
21345
22431
  getNearestExistingDirectory(directoryPath) {
21346
22432
  let candidate = directoryPath;
21347
- while (!existsSync15(candidate)) {
21348
- const parent = path28.dirname(candidate);
22433
+ while (!existsSync16(candidate)) {
22434
+ const parent = path29.dirname(candidate);
21349
22435
  if (parent === candidate) break;
21350
22436
  candidate = parent;
21351
22437
  }
@@ -21353,7 +22439,7 @@ var FileWatcher = class {
21353
22439
  }
21354
22440
  getProjectConfigRelativePaths() {
21355
22441
  return this.projectConfigPaths.map(
21356
- (configPath) => path28.normalize(path28.relative(this.projectRoot, configPath))
22442
+ (configPath) => path29.normalize(path29.relative(this.projectRoot, configPath))
21357
22443
  );
21358
22444
  }
21359
22445
  getConfigPathStates() {
@@ -21411,7 +22497,7 @@ var FileWatcher = class {
21411
22497
  return;
21412
22498
  }
21413
22499
  const changes = Array.from(this.pendingChanges.entries()).map(
21414
- ([path33, type]) => ({ path: path33, type })
22500
+ ([path34, type]) => ({ path: path34, type })
21415
22501
  );
21416
22502
  this.pendingChanges.clear();
21417
22503
  try {
@@ -21457,7 +22543,7 @@ var FileWatcher = class {
21457
22543
  };
21458
22544
 
21459
22545
  // src/watcher/git-head-watcher.ts
21460
- import * as path29 from "path";
22546
+ import * as path30 from "path";
21461
22547
  var GitHeadWatcher = class {
21462
22548
  watcher = null;
21463
22549
  projectRoot;
@@ -21479,13 +22565,13 @@ var GitHeadWatcher = class {
21479
22565
  this.readyPromise = Promise.resolve();
21480
22566
  return;
21481
22567
  }
21482
- this.readyPromise = new Promise((resolve20) => {
21483
- this.resolveReady = resolve20;
22568
+ this.readyPromise = new Promise((resolve21) => {
22569
+ this.resolveReady = resolve21;
21484
22570
  });
21485
22571
  this.onBranchChange = handler;
21486
22572
  this.currentBranch = getCurrentBranch(this.projectRoot);
21487
22573
  const headPath = getHeadPath(this.projectRoot);
21488
- const refsPath = path29.join(this.projectRoot, ".git", "refs", "heads");
22574
+ const refsPath = path30.join(this.projectRoot, ".git", "refs", "heads");
21489
22575
  this.watcher = chokidar_default.watch([headPath, refsPath], {
21490
22576
  persistent: true,
21491
22577
  ignoreInitial: true,
@@ -21553,7 +22639,9 @@ var GitHeadWatcher = class {
21553
22639
  function createWatcherWithIndexer(getIndexer, projectRoot, config, host, options = {}) {
21554
22640
  const fileWatcher = new FileWatcher(projectRoot, config, host, options);
21555
22641
  const configPaths = getConfigPaths(projectRoot, host, options);
21556
- configureAutoIndex(projectRoot, host, parseConfig(config), getIndexer);
22642
+ configureAutoIndex(projectRoot, host, parseConfig(config), getIndexer, {
22643
+ synchronizeBackgroundWorker: false
22644
+ });
21557
22645
  let stopped = false;
21558
22646
  const requestReindex = () => {
21559
22647
  if (stopped) return;
@@ -21573,7 +22661,9 @@ function createWatcherWithIndexer(getIndexer, projectRoot, config, host, options
21573
22661
  const parsedConfig = options.configPath ? parseConfig(loadConfigFile(options.configPath)) : void 0;
21574
22662
  const refreshedConfig = refreshIndexerForDirectory(projectRoot, host, parsedConfig);
21575
22663
  if (refreshedConfig) {
21576
- configureAutoIndex(projectRoot, host, refreshedConfig, getIndexer);
22664
+ configureAutoIndex(projectRoot, host, refreshedConfig, getIndexer, {
22665
+ synchronizeBackgroundWorker: false
22666
+ });
21577
22667
  }
21578
22668
  }
21579
22669
  requestReindex();
@@ -21621,7 +22711,7 @@ function getConfigPaths(projectRoot, host, options) {
21621
22711
 
21622
22712
  // src/tools/visualize/activity.ts
21623
22713
  import { execFileSync } from "child_process";
21624
- import * as path30 from "path";
22714
+ import * as path31 from "path";
21625
22715
  function attachRecentActivity(data, projectRoot) {
21626
22716
  const activity = readGitActivity(projectRoot);
21627
22717
  const changes = activity.size > 0 ? buildGitChanges(data, activity, projectRoot) : buildGraphChanges(data);
@@ -21783,7 +22873,7 @@ function normalizePath4(filePath) {
21783
22873
  return filePath.replace(/\\/g, "/");
21784
22874
  }
21785
22875
  function toGitRelativePath(projectRoot, filePath) {
21786
- const relativePath = path30.isAbsolute(filePath) ? path30.relative(projectRoot, filePath) : filePath;
22876
+ const relativePath = path31.isAbsolute(filePath) ? path31.relative(projectRoot, filePath) : filePath;
21787
22877
  return normalizePath4(relativePath);
21788
22878
  }
21789
22879
 
@@ -22041,7 +23131,7 @@ render();
22041
23131
  }
22042
23132
 
22043
23133
  // src/tools/visualize/transform.ts
22044
- import * as path31 from "path";
23134
+ import * as path32 from "path";
22045
23135
 
22046
23136
  // src/tools/visualize/modules.ts
22047
23137
  var MAX_MODULES = 18;
@@ -22301,7 +23391,7 @@ function transformForVisualization(symbols, edges, options = {}) {
22301
23391
  filePath: s.filePath,
22302
23392
  kind: s.kind,
22303
23393
  line: s.startLine,
22304
- directory: path31.dirname(s.filePath),
23394
+ directory: path32.dirname(s.filePath),
22305
23395
  moduleId: "",
22306
23396
  moduleLabel: ""
22307
23397
  }));
@@ -22329,9 +23419,9 @@ function parseArgs(argv) {
22329
23419
  let host = "opencode";
22330
23420
  for (let i = 2; i < argv.length; i++) {
22331
23421
  if (argv[i] === "--project" && argv[i + 1]) {
22332
- project = path32.resolve(argv[++i]);
23422
+ project = path33.resolve(argv[++i]);
22333
23423
  } else if (argv[i] === "--config" && argv[i + 1]) {
22334
- config = path32.resolve(argv[++i]);
23424
+ config = path33.resolve(argv[++i]);
22335
23425
  } else if (argv[i] === "--host" && argv[i + 1]) {
22336
23426
  host = parseHostMode(argv[++i]);
22337
23427
  } else if (argv[i] === "--host") {
@@ -22359,7 +23449,7 @@ function parseIndexArgs(argv, cwd) {
22359
23449
  if (!arg.startsWith("--project=")) {
22360
23450
  i += 1;
22361
23451
  }
22362
- project = path32.resolve(cwd, value);
23452
+ project = path33.resolve(cwd, value);
22363
23453
  continue;
22364
23454
  }
22365
23455
  if (arg === "--config" || arg.startsWith("--config=")) {
@@ -22370,7 +23460,7 @@ function parseIndexArgs(argv, cwd) {
22370
23460
  if (!arg.startsWith("--config=")) {
22371
23461
  i += 1;
22372
23462
  }
22373
- config = path32.resolve(cwd, value);
23463
+ config = path33.resolve(cwd, value);
22374
23464
  continue;
22375
23465
  }
22376
23466
  if (arg === "--host" || arg.startsWith("--host=")) {
@@ -22430,7 +23520,7 @@ Progress and diagnostics are written to stderr. Final index output is written to
22430
23520
  );
22431
23521
  }
22432
23522
  function isCliEntrypoint(moduleUrl, argvPath) {
22433
- return argvPath !== void 0 && realpathSync6(fileURLToPath2(moduleUrl)) === realpathSync6(argvPath);
23523
+ return argvPath !== void 0 && realpathSync7(fileURLToPath2(moduleUrl)) === realpathSync7(argvPath);
22434
23524
  }
22435
23525
  function parseVisualizeArgs(argv, cwd) {
22436
23526
  let project = cwd;
@@ -22440,7 +23530,7 @@ function parseVisualizeArgs(argv, cwd) {
22440
23530
  for (let i = 0; i < argv.length; i++) {
22441
23531
  const arg = argv[i];
22442
23532
  if (arg === "--project" && argv[i + 1]) {
22443
- project = path32.resolve(argv[++i]);
23533
+ project = path33.resolve(argv[++i]);
22444
23534
  } else if (arg === "--max" && argv[i + 1]) {
22445
23535
  maxNodes = Number(argv[++i]);
22446
23536
  } else if (arg.startsWith("--max=") || arg.startsWith("max=")) {
@@ -22477,8 +23567,8 @@ async function handleVisualizeCommand(argv, cwd) {
22477
23567
  console.error("No connected symbols found. Retry with: npm run visualize -- orphans");
22478
23568
  return 1;
22479
23569
  }
22480
- const outputPath = path32.join(os8.tmpdir(), `call-graph-${Date.now()}.html`);
22481
- writeFileSync6(outputPath, generateVisualizationHtml(vizData), "utf-8");
23570
+ const outputPath = path33.join(os9.tmpdir(), `call-graph-${Date.now()}.html`);
23571
+ writeFileSync7(outputPath, generateVisualizationHtml(vizData), "utf-8");
22482
23572
  console.log(`Temporal call graph visualization generated: ${outputPath}`);
22483
23573
  console.log(`Nodes: ${vizData.nodes.length} | Edges: ${vizData.edges.length}`);
22484
23574
  console.log(`Recent change lenses: ${vizData.changes?.length ?? 0}`);
@@ -22509,7 +23599,6 @@ async function runMcpCli(argv) {
22509
23599
  const config = parseConfig(rawConfig);
22510
23600
  const server = createMcpServer(args.project, config, args.host);
22511
23601
  const transport = new StdioServerTransport();
22512
- let watcher = null;
22513
23602
  let shutdownPromise;
22514
23603
  const onServerClose = server.server.onclose;
22515
23604
  const shutdown = () => {
@@ -22523,16 +23612,19 @@ async function runMcpCli(argv) {
22523
23612
  shutdownPromise = (async () => {
22524
23613
  let exitCode = 0;
22525
23614
  try {
22526
- await watcher?.stop();
22527
- } catch (error) {
22528
- exitCode = 1;
22529
- console.error("Failed to stop MCP file watcher cleanly:", error);
22530
- }
22531
- try {
22532
- await stopAutoIndex(args.project, args.host);
23615
+ await stopBackgroundWorker(args.project, args.host);
22533
23616
  } catch (error) {
22534
23617
  exitCode = 1;
22535
- console.error("Failed to stop automatic indexing cleanly:", error);
23618
+ if (error instanceof BackgroundWorkerStopError) {
23619
+ if (error.watcherError !== void 0) {
23620
+ console.error("Failed to stop MCP file watcher cleanly:", error.watcherError);
23621
+ }
23622
+ if (error.autoIndexError !== void 0) {
23623
+ console.error("Failed to stop automatic indexing cleanly:", error.autoIndexError);
23624
+ }
23625
+ } else {
23626
+ console.error("Failed to stop automatic indexing cleanly:", error);
23627
+ }
22536
23628
  }
22537
23629
  try {
22538
23630
  await server.close();
@@ -22561,19 +23653,25 @@ async function runMcpCli(argv) {
22561
23653
  process.once("SIGHUP", requestShutdown);
22562
23654
  process.once("SIGTERM", requestShutdown);
22563
23655
  }
22564
- await server.connect(transport);
22565
- if (shutdownPromise) return;
22566
23656
  const isHomeDir = isHomeDirectory(args.project);
22567
23657
  const isValidProject = !isHomeDir && (!config.indexing.requireProjectMarker || hasProjectMarker(args.project));
22568
- if (config.indexing.watchFiles && isValidProject) {
22569
- watcher = createWatcherWithIndexer(
22570
- () => getIndexerForProject(args.project, args.host),
22571
- args.project,
22572
- config,
22573
- args.host,
22574
- args.config ? { configPath: args.config } : {}
22575
- );
22576
- }
23658
+ const watcherFactoryForConfig = (refreshedConfig) => refreshedConfig.indexing.watchFiles && !isHomeDirectory(args.project) && (!refreshedConfig.indexing.requireProjectMarker || hasProjectMarker(args.project)) ? () => createWatcherWithIndexer(
23659
+ () => getIndexerForProject(args.project, args.host),
23660
+ args.project,
23661
+ refreshedConfig,
23662
+ args.host,
23663
+ args.config ? { configPath: args.config } : {}
23664
+ ) : null;
23665
+ await server.connect(transport);
23666
+ if (shutdownPromise) return;
23667
+ await attachMcpBackgroundWatcher(
23668
+ args.project,
23669
+ config,
23670
+ args.host,
23671
+ config.indexing.watchFiles && isValidProject ? watcherFactoryForConfig(config) : null,
23672
+ watcherFactoryForConfig
23673
+ );
23674
+ if (shutdownPromise) return;
22577
23675
  }
22578
23676
  function printIndexProgress(onProgress, title, metadata) {
22579
23677
  const details = Object.entries(metadata).filter(([, value]) => value !== void 0 && value !== null).map(([key, value]) => `${key}=${isSensitiveKey(key) ? "[REDACTED]" : String(value)}`).join(" ");