opencode-codebase-index 0.24.0 → 0.25.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.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) {
@@ -3596,6 +3611,26 @@ function formatCostEstimate(estimate) {
3596
3611
  \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518
3597
3612
  `;
3598
3613
  }
3614
+ function formatDryRunEstimate(estimate) {
3615
+ return `Dry run: parsed the file set to measure the embedding workload. No embedding requests were made and the index was not changed.
3616
+
3617
+ Files to embed: ${estimate.filesCount.toLocaleString()}
3618
+ Chunks to embed: ${estimate.chunksCount.toLocaleString()}
3619
+ Tokens to embed: ${estimate.tokensToEmbed.toLocaleString()}
3620
+
3621
+ The "Tokens to embed" value uses the local estimateTokens(text) = ceil(len/4). It
3622
+ matches the live "Tokens used" counter only for providers that report usage on the
3623
+ same basis (ollama); for providers that report a server tokenizer count (OpenAI,
3624
+ Gemini, custom) it is only an estimate.
3625
+
3626
+ For a matching provider and a project-scoped force index, the force pass clears its
3627
+ own cached embeddings, so the live counter climbs to this number. A force index on a
3628
+ shared global index can reuse cached embeddings from other projects, and an
3629
+ incremental index counts cached chunks that are not re-embedded; in both cases this
3630
+ number is an upper bound on the live counter, so a progress percent against this
3631
+ total tops out below 100%.
3632
+ `;
3633
+ }
3599
3634
  function formatBytes(bytes) {
3600
3635
  if (bytes === 0) return "0 B";
3601
3636
  const k = 1024;
@@ -6820,6 +6855,9 @@ var CALL_GRAPH_SYMBOL_CHUNK_TYPES = /* @__PURE__ */ new Set([
6820
6855
  "enum_declaration",
6821
6856
  "function_definition",
6822
6857
  "class_definition",
6858
+ // Ruby module/class symbols that are declaration-bearing and navigable.
6859
+ "class",
6860
+ "module",
6823
6861
  "class_specifier",
6824
6862
  "struct_specifier",
6825
6863
  "namespace_definition",
@@ -7064,8 +7102,8 @@ function pathSegmentsForAffinityMatch(filePath) {
7064
7102
  if (segments.length === 0) {
7065
7103
  return [];
7066
7104
  }
7067
- const basename8 = segments[segments.length - 1] ?? "";
7068
- const basenameWithoutExt = basename8.replace(/\.[^/.]+$/u, "");
7105
+ const basename9 = segments[segments.length - 1] ?? "";
7106
+ const basenameWithoutExt = basename9.replace(/\.[^/.]+$/u, "");
7069
7107
  const normalizedSegments = segments.map((segment) => segment.toLowerCase());
7070
7108
  return Array.from(/* @__PURE__ */ new Set([
7071
7109
  ...normalizedSegments,
@@ -7488,7 +7526,7 @@ function removeDeadReclaimMarker(lockPath, expectedOwner) {
7488
7526
  return true;
7489
7527
  }
7490
7528
  function reclaimDeadOwner(indexPath, lockPath, expectedOwner) {
7491
- const reclaimPath = path13.join(lockPath, RECLAIM_DIRECTORY_NAME);
7529
+ const reclaimPath2 = path13.join(lockPath, RECLAIM_DIRECTORY_NAME);
7492
7530
  const reclaimOwner = {
7493
7531
  pid: process.pid,
7494
7532
  hostname: os5.hostname(),
@@ -7497,19 +7535,19 @@ function reclaimDeadOwner(indexPath, lockPath, expectedOwner) {
7497
7535
  expectedOwnerToken: expectedOwner.token
7498
7536
  };
7499
7537
  for (let attempt = 0; attempt < 2; attempt += 1) {
7500
- if (publishJsonDirectory(reclaimPath, reclaimOwner)) break;
7538
+ if (publishJsonDirectory(reclaimPath2, reclaimOwner)) break;
7501
7539
  if (attempt === 0 && removeDeadReclaimMarker(lockPath, expectedOwner)) continue;
7502
7540
  return false;
7503
7541
  }
7504
7542
  try {
7505
- const currentReclaimer = readReclaimOwner(reclaimPath);
7543
+ const currentReclaimer = readReclaimOwner(reclaimPath2);
7506
7544
  const currentOwner = readDirectoryOwner(lockPath);
7507
7545
  if (!currentReclaimer || !sameReclaimOwner(currentReclaimer, reclaimOwner) || !currentOwner || !sameOwner(currentOwner, expectedOwner) || getOwnerLiveness(currentOwner) !== "dead") {
7508
7546
  return false;
7509
7547
  }
7510
7548
  publishRecoveryMarker(indexPath, expectedOwner);
7511
7549
  const ownerBeforeQuarantine = readDirectoryOwner(lockPath);
7512
- const reclaimerBeforeQuarantine = readReclaimOwner(reclaimPath);
7550
+ const reclaimerBeforeQuarantine = readReclaimOwner(reclaimPath2);
7513
7551
  if (!ownerBeforeQuarantine || !sameOwner(ownerBeforeQuarantine, expectedOwner) || getOwnerLiveness(ownerBeforeQuarantine) !== "dead" || !reclaimerBeforeQuarantine || !sameReclaimOwner(reclaimerBeforeQuarantine, reclaimOwner)) {
7514
7552
  return false;
7515
7553
  }
@@ -8725,6 +8763,17 @@ var Indexer = class _Indexer {
8725
8763
  }
8726
8764
  return path15.relative(this.projectRoot, canonicalFilePath).split(path15.sep).join("/");
8727
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
+ }
8728
8777
  resolveStoredFilePath(filePath, rootPath = this.projectRoot) {
8729
8778
  if (path15.isAbsolute(filePath)) {
8730
8779
  return filePath;
@@ -9749,7 +9798,7 @@ var Indexer = class _Indexer {
9749
9798
  await options.queue.onSizeLessThan(Math.max(1, options.providerRateLimits.concurrency));
9750
9799
  const task = options.queue.add(async () => {
9751
9800
  if (options.rateLimitState.backoffMs > 0) {
9752
- await new Promise((resolve20) => setTimeout(resolve20, options.rateLimitState.backoffMs));
9801
+ await new Promise((resolve21) => setTimeout(resolve21, options.rateLimitState.backoffMs));
9753
9802
  }
9754
9803
  try {
9755
9804
  const embeddingResult = await pRetry(
@@ -10866,6 +10915,70 @@ var Indexer = class _Indexer {
10866
10915
  );
10867
10916
  return createCostEstimate(files, configuredProviderInfo);
10868
10917
  }
10918
+ // Dry-run counterpart to index()/forceIndex(): parse the real file set and sum
10919
+ // estimateTokens over the embedding text of every indexable chunk, without
10920
+ // calling the embedding provider or writing to the index. Read-only and
10921
+ // lock-free (mirrors estimateCost). The token sum is the exact value "Tokens
10922
+ // used" climbs to for a force index (cache bypassed); for an incremental it is
10923
+ // an upper bound because cached chunks are counted here but not re-embedded.
10924
+ // Used by index_codebase(dryRun:true) to give a stable, monotonic progress
10925
+ // denominator that matches the live "Tokens used" basis.
10926
+ async dryRunCost() {
10927
+ const { configuredProviderInfo } = await this.ensureInitialized();
10928
+ const maxChunkTokens = getSafeEmbeddingChunkTokenLimit(configuredProviderInfo);
10929
+ const includePatterns = [...this.config.include, ...this.config.additionalInclude];
10930
+ const { files } = await collectFiles(
10931
+ this.materializedProjectRoot,
10932
+ includePatterns,
10933
+ this.config.exclude,
10934
+ this.config.indexing.maxFileSize,
10935
+ this.getMaterializedKnowledgeBases(),
10936
+ { maxDepth: this.config.indexing.maxDepth, maxFilesPerDirectory: this.config.indexing.maxFilesPerDirectory }
10937
+ );
10938
+ let filesCount = 0;
10939
+ let chunksCount = 0;
10940
+ let tokensToEmbed = 0;
10941
+ for (const batch of iterateOrderedFileBatches(files, (f) => f.size, this.fileBatchLimits)) {
10942
+ const loadedFiles = await Promise.all(batch.map(async (f) => {
10943
+ try {
10944
+ return {
10945
+ path: this.toStoredFilePath(f.path),
10946
+ content: await fsPromises3.readFile(f.path, "utf-8")
10947
+ };
10948
+ } catch {
10949
+ return null;
10950
+ }
10951
+ }));
10952
+ const readable = loadedFiles.filter(
10953
+ (f) => f !== null
10954
+ );
10955
+ filesCount += readable.length;
10956
+ const contentByPath = new Map(readable.map((f) => [f.path, f.content]));
10957
+ const parsedFiles = parseFiles(readable, this.config.indexing.linesPerChunk);
10958
+ for (const parsed of parsedFiles) {
10959
+ let chunksToProcess = parsed.chunks;
10960
+ if (this.config.indexing.fallbackToTextOnMaxChunks && chunksToProcess.length > this.config.indexing.maxChunksPerFile) {
10961
+ const content = contentByPath.get(parsed.path);
10962
+ if (content !== void 0) {
10963
+ chunksToProcess = parseFileAsText(parsed.path, content, this.config.indexing.linesPerChunk);
10964
+ }
10965
+ }
10966
+ chunksToProcess = selectIndexableChunks(
10967
+ chunksToProcess,
10968
+ this.config.indexing.maxChunksPerFile,
10969
+ this.config.indexing.semanticOnly
10970
+ );
10971
+ for (const chunk of chunksToProcess) {
10972
+ const texts = createEmbeddingTexts(chunk, parsed.path, maxChunkTokens);
10973
+ chunksCount += 1;
10974
+ for (const text of texts) {
10975
+ tokensToEmbed += estimateTokens2(text);
10976
+ }
10977
+ }
10978
+ }
10979
+ }
10980
+ return { filesCount, chunksCount, tokensToEmbed };
10981
+ }
10869
10982
  async index(onProgress) {
10870
10983
  return this.withIndexMutationLease("index", async (recoveredOwners) => {
10871
10984
  return this.indexUnlocked(onProgress, recoveredOwners);
@@ -11062,7 +11175,7 @@ var Indexer = class _Indexer {
11062
11175
  }
11063
11176
  }
11064
11177
  }
11065
- 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);
11066
11179
  const failedProcessing = this.prepareFailedBatchProcessing(scopedRoots, shouldRetryFailedPath);
11067
11180
  const maxChunkTokens = getSafeEmbeddingChunkTokenLimit(configuredProviderInfo);
11068
11181
  const providerRateLimits = this.getProviderRateLimits(configuredProviderInfo.provider);
@@ -12390,7 +12503,8 @@ var Indexer = class _Indexer {
12390
12503
  const maxChunkTokens = getSafeEmbeddingChunkTokenLimit(configuredProviderInfo);
12391
12504
  const providerRateLimits = this.getProviderRateLimits(configuredProviderInfo.provider);
12392
12505
  const roots = this.config.scope === "global" ? this.getScopedRoots() : null;
12393
- const failedProcessing = this.prepareFailedBatchProcessing(roots, () => true);
12506
+ const shouldProcessFailedPath = (filePath) => filePath === null || !this.isStoredPathExcluded(filePath);
12507
+ const failedProcessing = this.prepareFailedBatchProcessing(roots, shouldProcessFailedPath);
12394
12508
  if (failedProcessing.latestById.size === 0) {
12395
12509
  this.finalizeFailedBatchWriteState(failedProcessing.state);
12396
12510
  return { succeeded: 0, failed: 0, remaining: 0 };
@@ -12403,7 +12517,7 @@ var Indexer = class _Indexer {
12403
12517
  const retryableChunks = this.iterateLatestFailedChunks(
12404
12518
  failedProcessing.latestById,
12405
12519
  roots,
12406
- () => true,
12520
+ shouldProcessFailedPath,
12407
12521
  maxChunkTokens
12408
12522
  );
12409
12523
  for (const retryBatch of iterateOrderedFileBatches(
@@ -12673,9 +12787,9 @@ var Indexer = class _Indexer {
12673
12787
  this.requireReadableComponents(readIssues, "database");
12674
12788
  let shortest = [];
12675
12789
  for (const branchKey of this.getBranchCatalogKeys()) {
12676
- const path33 = database.findShortestPath(fromName, toName, branchKey, maxDepth);
12677
- if (path33.length > 0 && (shortest.length === 0 || path33.length < shortest.length)) {
12678
- 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;
12679
12793
  }
12680
12794
  }
12681
12795
  return shortest.map((hop) => this.resolveFilePathRecord(hop));
@@ -12723,13 +12837,13 @@ var Indexer = class _Indexer {
12723
12837
  }
12724
12838
  }
12725
12839
  if (!found) continue;
12726
- const path33 = [];
12840
+ const path34 = [];
12727
12841
  let currentSymbolId = toSymbolId;
12728
12842
  while (true) {
12729
12843
  const symbol = symbolsById.get(currentSymbolId);
12730
12844
  if (!symbol) break;
12731
12845
  const parent = parentBySymbolId.get(currentSymbolId);
12732
- path33.push({
12846
+ path34.push({
12733
12847
  symbolId: symbol.id,
12734
12848
  symbolName: symbol.name,
12735
12849
  filePath: symbol.filePath,
@@ -12739,9 +12853,9 @@ var Indexer = class _Indexer {
12739
12853
  if (!parent) break;
12740
12854
  currentSymbolId = parent.parentId;
12741
12855
  }
12742
- path33.reverse();
12743
- if (path33.length > 0 && (shortest.length === 0 || path33.length < shortest.length)) {
12744
- shortest = path33;
12856
+ path34.reverse();
12857
+ if (path34.length > 0 && (shortest.length === 0 || path34.length < shortest.length)) {
12858
+ shortest = path34;
12745
12859
  }
12746
12860
  }
12747
12861
  return shortest.map((hop) => this.resolveFilePathRecord(hop));
@@ -13124,8 +13238,8 @@ var CODE_COMMUNITIES_DEFAULT_COUPLING_LIMIT = 20;
13124
13238
  var CODE_COMMUNITIES_MAX_COUPLING_LIMIT = 100;
13125
13239
 
13126
13240
  // src/tools/operations.ts
13127
- import { existsSync as existsSync12, realpathSync as realpathSync5, statSync as statSync5 } from "fs";
13128
- import * as path21 from "path";
13241
+ import { existsSync as existsSync13, realpathSync as realpathSync6, statSync as statSync5 } from "fs";
13242
+ import * as path22 from "path";
13129
13243
 
13130
13244
  // src/tools/knowledge-base-paths.ts
13131
13245
  import * as path16 from "path";
@@ -13420,8 +13534,8 @@ function formatExactSearchHandoff(results) {
13420
13534
  }
13421
13535
  function formatContextEvidence(result, index) {
13422
13536
  const symbol = result.name ? ` ${JSON.stringify(compactEvidenceValue(result.name, 80))}` : "";
13423
- const path33 = compactEvidenceValue(result.filePath, 120);
13424
- 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)})`;
13425
13539
  }
13426
13540
  function formatContextPack(heading, selected, candidateCount, duplicateCount, limitOmittedCount, budgetOmittedCount, includeExactSearchHandoff) {
13427
13541
  const lines = selected.map((result, index) => formatContextEvidence(result, index + 1));
@@ -14063,119 +14177,47 @@ function formatEffectivenessMetrics(snapshot) {
14063
14177
  }
14064
14178
 
14065
14179
  // src/utils/auto-index.ts
14066
- 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";
14067
14196
  import * as os6 from "os";
14068
14197
  import * as path17 from "path";
14069
-
14070
- // src/utils/power-source.ts
14071
- import * as childProcess from "child_process";
14072
- var POWER_SOURCE_RECHECK_DELAY_MS = 6e4;
14073
- var PMSET_TIMEOUT_MS = 5e3;
14074
- function getErrorMessage4(error) {
14075
- return error instanceof Error ? error.message : String(error);
14076
- }
14077
- function runCommand(file, args, options) {
14078
- return new Promise((resolve20, reject) => {
14079
- childProcess.execFile(
14080
- file,
14081
- args,
14082
- { encoding: "utf8", timeout: options.timeoutMs },
14083
- (error, stdout) => {
14084
- if (error) {
14085
- reject(error);
14086
- return;
14087
- }
14088
- resolve20(stdout);
14089
- }
14090
- );
14091
- });
14092
- }
14093
- function parseMacOsPowerSource(output) {
14094
- const match = output.match(/Now drawing from '([^']+)'/i);
14095
- if (!match) {
14096
- return "unknown";
14097
- }
14098
- const source = match[1].toLowerCase();
14099
- if (source === "battery power") {
14100
- return "battery";
14101
- }
14102
- if (source === "ac power") {
14103
- return "ac";
14104
- }
14105
- return "unknown";
14106
- }
14107
- async function readMacOsPowerSource(commandRunner = runCommand) {
14108
- const output = await commandRunner(
14109
- "/usr/bin/pmset",
14110
- ["-g", "batt"],
14111
- { timeoutMs: PMSET_TIMEOUT_MS }
14112
- );
14113
- return parseMacOsPowerSource(output);
14114
- }
14115
- var MacOsBackgroundIndexingPolicy = class {
14116
- constructor(readPowerSource, recheckDelayMs) {
14117
- this.readPowerSource = readPowerSource;
14118
- this.recheckDelayMs = recheckDelayMs;
14119
- }
14120
- readPowerSource;
14121
- recheckDelayMs;
14122
- lastPaused = null;
14123
- reportedFailure = false;
14124
- isPaused() {
14125
- return this.checkPowerSource();
14126
- }
14127
- async checkPowerSource() {
14128
- try {
14129
- const source = await this.readPowerSource();
14130
- if (source === "unknown") {
14131
- throw new Error("pmset returned an unrecognized power source");
14132
- }
14133
- this.reportedFailure = false;
14134
- const paused = source === "battery";
14135
- if (paused && this.lastPaused !== true) {
14136
- console.warn("[codebase-index] Background indexing paused while macOS is using battery power.");
14137
- } else if (!paused && this.lastPaused === true) {
14138
- console.warn("[codebase-index] AC power detected; resuming pending background indexing.");
14139
- }
14140
- this.lastPaused = paused;
14141
- return paused;
14142
- } catch (error) {
14143
- if (!this.reportedFailure) {
14144
- console.error(
14145
- `[codebase-index] Failed to determine the macOS power source; background indexing will continue: ${getErrorMessage4(error)}`
14146
- );
14147
- this.reportedFailure = true;
14148
- }
14149
- this.lastPaused = false;
14150
- return false;
14151
- }
14152
- }
14153
- };
14154
- function createBackgroundIndexingPolicy(pauseOnBattery, options = {}) {
14155
- const platform2 = options.platform ?? process.platform;
14156
- if (!pauseOnBattery || platform2 !== "darwin") {
14157
- return null;
14158
- }
14159
- return new MacOsBackgroundIndexingPolicy(
14160
- options.readPowerSource ?? readMacOsPowerSource,
14161
- options.recheckDelayMs ?? POWER_SOURCE_RECHECK_DELAY_MS
14162
- );
14163
- }
14164
-
14165
- // src/utils/auto-index.ts
14166
- var MAX_RETRY_DELAY_MS = 1e4;
14167
- var SHUTDOWN_WAIT_MS = 2e3;
14168
- var coordinators = /* @__PURE__ */ new Map();
14169
- var coordinatorKeysByProject = /* @__PURE__ */ new Map();
14170
- var coordinatorReplacementBarriers = /* @__PURE__ */ new Map();
14171
- var AutoIndexCancelledError = class extends Error {
14172
- constructor() {
14173
- super("Auto-index coordination was cancelled");
14174
- this.name = "AutoIndexCancelledError";
14175
- }
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;
14176
14215
  };
14177
- function now() {
14178
- return (/* @__PURE__ */ new Date()).toISOString();
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;
14179
14221
  }
14180
14222
  function canonicalizePath(targetPath) {
14181
14223
  const resolved = path17.resolve(targetPath);
@@ -14190,50 +14232,1020 @@ function canonicalizePath(targetPath) {
14190
14232
  if (parent === resolved) return resolved;
14191
14233
  return path17.join(canonicalizePath(parent), path17.basename(resolved));
14192
14234
  }
14193
- function isHomeDirectory(projectRoot) {
14194
- return canonicalizePath(projectRoot) === canonicalizePath(os6.homedir());
14195
- }
14196
14235
  function projectLookupKey(projectRoot, host) {
14197
14236
  return `${host}::${canonicalizePath(projectRoot)}`;
14198
14237
  }
14199
- function coordinatorKey(projectRoot, config, host) {
14238
+ function getBackgroundWorkerProjectKey(projectRoot, host) {
14239
+ return projectLookupKey(projectRoot, host);
14240
+ }
14241
+ function resolveIdentity(projectRoot, config, host) {
14200
14242
  const canonicalProjectRoot = canonicalizePath(projectRoot);
14201
- const indexPath = resolveProjectIndexPath(projectRoot, config.scope, host);
14202
- return `${canonicalizePath(indexPath)}::${canonicalProjectRoot}`;
14243
+ const canonicalIndexPath = canonicalizePath(resolveProjectIndexPath(projectRoot, config.scope, host));
14244
+ return {
14245
+ canonicalIndexPath,
14246
+ canonicalProjectRoot,
14247
+ key: `${canonicalIndexPath}::${canonicalProjectRoot}`
14248
+ };
14203
14249
  }
14204
- function getProjectSafety(projectRoot, config) {
14205
- if (isHomeDirectory(projectRoot)) {
14206
- return { safeToRun: false, blockedReason: "home-directory" };
14207
- }
14208
- if (config.indexing.requireProjectMarker && !hasProjectMarker(projectRoot)) {
14209
- return { safeToRun: false, blockedReason: "project-marker-missing" };
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;
14210
14305
  }
14211
- return { safeToRun: true };
14212
14306
  }
14213
- function calculatePercentage2(progress) {
14214
- if (progress.phase === "scanning") return 0;
14215
- if (progress.phase === "complete") return 100;
14216
- if (progress.phase === "parsing") {
14217
- return progress.totalFiles === 0 ? 5 : Math.round(5 + progress.filesProcessed / progress.totalFiles * 15);
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;
14218
14318
  }
14219
- if (progress.phase === "embedding") {
14220
- return progress.totalChunks === 0 ? 20 : Math.round(20 + progress.chunksProcessed / progress.totalChunks * 70);
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;
14221
14325
  }
14222
- if (progress.phase === "storing") return 95;
14223
- return 0;
14224
14326
  }
14225
- function safeFailureMessage(error) {
14226
- if (isTransientIndexLockContention(error)) {
14227
- return "Another index process remained busy after the configured retries.";
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";
14228
14337
  }
14229
- return "Automatic indexing failed. Check the embedding provider configuration, then run index_codebase.";
14230
14338
  }
14231
- function cancellableDelay(delayMs, signal) {
14232
- if (signal.aborted) return Promise.reject(new AutoIndexCancelledError());
14233
- return new Promise((resolve20, reject) => {
14234
- const timer = setTimeout(() => {
14235
- signal.removeEventListener("abort", onAbort);
14236
- resolve20();
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
+ }
15081
+
15082
+ // src/utils/power-source.ts
15083
+ import * as childProcess from "child_process";
15084
+ var POWER_SOURCE_RECHECK_DELAY_MS = 6e4;
15085
+ var PMSET_TIMEOUT_MS = 5e3;
15086
+ function getErrorMessage4(error) {
15087
+ return error instanceof Error ? error.message : String(error);
15088
+ }
15089
+ function runCommand(file, args, options) {
15090
+ return new Promise((resolve21, reject) => {
15091
+ childProcess.execFile(
15092
+ file,
15093
+ args,
15094
+ { encoding: "utf8", timeout: options.timeoutMs },
15095
+ (error, stdout) => {
15096
+ if (error) {
15097
+ reject(error);
15098
+ return;
15099
+ }
15100
+ resolve21(stdout);
15101
+ }
15102
+ );
15103
+ });
15104
+ }
15105
+ function parseMacOsPowerSource(output) {
15106
+ const match = output.match(/Now drawing from '([^']+)'/i);
15107
+ if (!match) {
15108
+ return "unknown";
15109
+ }
15110
+ const source = match[1].toLowerCase();
15111
+ if (source === "battery power") {
15112
+ return "battery";
15113
+ }
15114
+ if (source === "ac power") {
15115
+ return "ac";
15116
+ }
15117
+ return "unknown";
15118
+ }
15119
+ async function readMacOsPowerSource(commandRunner = runCommand) {
15120
+ const output = await commandRunner(
15121
+ "/usr/bin/pmset",
15122
+ ["-g", "batt"],
15123
+ { timeoutMs: PMSET_TIMEOUT_MS }
15124
+ );
15125
+ return parseMacOsPowerSource(output);
15126
+ }
15127
+ var MacOsBackgroundIndexingPolicy = class {
15128
+ constructor(readPowerSource, recheckDelayMs) {
15129
+ this.readPowerSource = readPowerSource;
15130
+ this.recheckDelayMs = recheckDelayMs;
15131
+ }
15132
+ readPowerSource;
15133
+ recheckDelayMs;
15134
+ lastPaused = null;
15135
+ reportedFailure = false;
15136
+ isPaused() {
15137
+ return this.checkPowerSource();
15138
+ }
15139
+ async checkPowerSource() {
15140
+ try {
15141
+ const source = await this.readPowerSource();
15142
+ if (source === "unknown") {
15143
+ throw new Error("pmset returned an unrecognized power source");
15144
+ }
15145
+ this.reportedFailure = false;
15146
+ const paused = source === "battery";
15147
+ if (paused && this.lastPaused !== true) {
15148
+ console.warn("[codebase-index] Background indexing paused while macOS is using battery power.");
15149
+ } else if (!paused && this.lastPaused === true) {
15150
+ console.warn("[codebase-index] AC power detected; resuming pending background indexing.");
15151
+ }
15152
+ this.lastPaused = paused;
15153
+ return paused;
15154
+ } catch (error) {
15155
+ if (!this.reportedFailure) {
15156
+ console.error(
15157
+ `[codebase-index] Failed to determine the macOS power source; background indexing will continue: ${getErrorMessage4(error)}`
15158
+ );
15159
+ this.reportedFailure = true;
15160
+ }
15161
+ this.lastPaused = false;
15162
+ return false;
15163
+ }
15164
+ }
15165
+ };
15166
+ function createBackgroundIndexingPolicy(pauseOnBattery, options = {}) {
15167
+ const platform2 = options.platform ?? process.platform;
15168
+ if (!pauseOnBattery || platform2 !== "darwin") {
15169
+ return null;
15170
+ }
15171
+ return new MacOsBackgroundIndexingPolicy(
15172
+ options.readPowerSource ?? readMacOsPowerSource,
15173
+ options.recheckDelayMs ?? POWER_SOURCE_RECHECK_DELAY_MS
15174
+ );
15175
+ }
15176
+
15177
+ // src/utils/auto-index.ts
15178
+ var MAX_RETRY_DELAY_MS = 1e4;
15179
+ var SHUTDOWN_WAIT_MS = 2e3;
15180
+ var coordinators = /* @__PURE__ */ new Map();
15181
+ var coordinatorKeysByProject = /* @__PURE__ */ new Map();
15182
+ var coordinatorReplacementBarriers = /* @__PURE__ */ new Map();
15183
+ var AutoIndexCancelledError = class extends Error {
15184
+ constructor() {
15185
+ super("Auto-index coordination was cancelled");
15186
+ this.name = "AutoIndexCancelledError";
15187
+ }
15188
+ };
15189
+ function now() {
15190
+ return (/* @__PURE__ */ new Date()).toISOString();
15191
+ }
15192
+ function canonicalizePath2(targetPath) {
15193
+ const resolved = path18.resolve(targetPath);
15194
+ if (existsSync10(resolved)) {
15195
+ try {
15196
+ return realpathSync5.native(resolved);
15197
+ } catch {
15198
+ return resolved;
15199
+ }
15200
+ }
15201
+ const parent = path18.dirname(resolved);
15202
+ if (parent === resolved) return resolved;
15203
+ return path18.join(canonicalizePath2(parent), path18.basename(resolved));
15204
+ }
15205
+ function isHomeDirectory(projectRoot) {
15206
+ return canonicalizePath2(projectRoot) === canonicalizePath2(os7.homedir());
15207
+ }
15208
+ function projectLookupKey2(projectRoot, host) {
15209
+ return `${host}::${canonicalizePath2(projectRoot)}`;
15210
+ }
15211
+ function coordinatorKey(projectRoot, config, host) {
15212
+ const canonicalProjectRoot = canonicalizePath2(projectRoot);
15213
+ const indexPath = resolveProjectIndexPath(projectRoot, config.scope, host);
15214
+ return `${canonicalizePath2(indexPath)}::${canonicalProjectRoot}`;
15215
+ }
15216
+ function getProjectSafety(projectRoot, config) {
15217
+ if (isHomeDirectory(projectRoot)) {
15218
+ return { safeToRun: false, blockedReason: "home-directory" };
15219
+ }
15220
+ if (config.indexing.requireProjectMarker && !hasProjectMarker(projectRoot)) {
15221
+ return { safeToRun: false, blockedReason: "project-marker-missing" };
15222
+ }
15223
+ return { safeToRun: true };
15224
+ }
15225
+ function calculatePercentage2(progress) {
15226
+ if (progress.phase === "scanning") return 0;
15227
+ if (progress.phase === "complete") return 100;
15228
+ if (progress.phase === "parsing") {
15229
+ return progress.totalFiles === 0 ? 5 : Math.round(5 + progress.filesProcessed / progress.totalFiles * 15);
15230
+ }
15231
+ if (progress.phase === "embedding") {
15232
+ return progress.totalChunks === 0 ? 20 : Math.round(20 + progress.chunksProcessed / progress.totalChunks * 70);
15233
+ }
15234
+ if (progress.phase === "storing") return 95;
15235
+ return 0;
15236
+ }
15237
+ function safeFailureMessage(error) {
15238
+ if (isTransientIndexLockContention(error)) {
15239
+ return "Another index process remained busy after the configured retries.";
15240
+ }
15241
+ return "Automatic indexing failed. Check the embedding provider configuration, then run index_codebase.";
15242
+ }
15243
+ function cancellableDelay(delayMs, signal) {
15244
+ if (signal.aborted) return Promise.reject(new AutoIndexCancelledError());
15245
+ return new Promise((resolve21, reject) => {
15246
+ const timer = setTimeout(() => {
15247
+ signal.removeEventListener("abort", onAbort);
15248
+ resolve21();
14237
15249
  }, delayMs);
14238
15250
  timer.unref?.();
14239
15251
  const onAbort = () => {
@@ -14245,18 +15257,44 @@ function cancellableDelay(delayMs, signal) {
14245
15257
  }
14246
15258
  function withTimeout(promise, timeoutMs) {
14247
15259
  if (timeoutMs <= 0) return Promise.resolve(void 0);
14248
- return new Promise((resolve20) => {
14249
- const timer = setTimeout(() => resolve20(void 0), timeoutMs);
15260
+ return new Promise((resolve21) => {
15261
+ const timer = setTimeout(() => resolve21(void 0), timeoutMs);
14250
15262
  timer.unref?.();
14251
15263
  void promise.then((value) => {
14252
15264
  clearTimeout(timer);
14253
- resolve20(value);
15265
+ resolve21(value);
14254
15266
  }, () => {
14255
15267
  clearTimeout(timer);
14256
- resolve20(void 0);
15268
+ resolve21(void 0);
14257
15269
  });
14258
15270
  });
14259
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
+ }
14260
15298
  function requestPriority(request) {
14261
15299
  if (request.force) return 4;
14262
15300
  if (request.source === "manual") return 3;
@@ -14267,6 +15305,7 @@ function mergeRequests(current, next) {
14267
15305
  if (!current) return next;
14268
15306
  const preferred = requestPriority(next) > requestPriority(current) ? next : current;
14269
15307
  return {
15308
+ allowDisabledAutoIndex: current.allowDisabledAutoIndex || next.allowDisabledAutoIndex,
14270
15309
  checkFreshness: current.checkFreshness && next.checkFreshness,
14271
15310
  force: current.force || next.force,
14272
15311
  onProgress: next.onProgress ?? current.onProgress,
@@ -14328,11 +15367,11 @@ var AutoIndexCoordinator = class {
14328
15367
  progress: this.status.progress ? { ...this.status.progress } : void 0
14329
15368
  };
14330
15369
  }
14331
- start(source) {
15370
+ start(source, allowDisabledAutoIndex = false) {
14332
15371
  this.refreshSafety();
14333
- if (!this.registration.config.indexing.autoIndex || !this.registration.safeToRun) return null;
15372
+ if (!this.registration.config.indexing.autoIndex && !allowDisabledAutoIndex || !this.registration.safeToRun) return null;
14334
15373
  if (this.status.state === "failed") return this.inFlight;
14335
- return this.request({ checkFreshness: true, force: false, source });
15374
+ return this.request({ allowDisabledAutoIndex, checkFreshness: true, force: false, source });
14336
15375
  }
14337
15376
  request(request) {
14338
15377
  if (this.stopped) {
@@ -14407,13 +15446,15 @@ var AutoIndexCoordinator = class {
14407
15446
  retryAttempt: void 0
14408
15447
  });
14409
15448
  const inFlight = this.inFlight;
14410
- if (inFlight) {
14411
- if (waitForCompletion) {
14412
- await inFlight;
14413
- } else {
14414
- await withTimeout(inFlight, SHUTDOWN_WAIT_MS);
14415
- }
15449
+ const completion = inFlight ? inFlight.then(() => void 0, () => void 0) : Promise.resolve();
15450
+ if (!inFlight) {
15451
+ return { completed: true, completion };
14416
15452
  }
15453
+ if (waitForCompletion) {
15454
+ await completion;
15455
+ return { completed: true, completion };
15456
+ }
15457
+ return { completed: await settlesWithin(completion, SHUTDOWN_WAIT_MS), completion };
14417
15458
  }
14418
15459
  startRequest(request) {
14419
15460
  if (this.stopped || !this.canRun(request)) {
@@ -14602,7 +15643,7 @@ var AutoIndexCoordinator = class {
14602
15643
  if (request.source === "manual" || request.source === "watcher") {
14603
15644
  return true;
14604
15645
  }
14605
- return this.registration.safeToRun && this.registration.config.indexing.autoIndex;
15646
+ return this.registration.safeToRun && (this.registration.config.indexing.autoIndex || request.allowDisabledAutoIndex === true);
14606
15647
  }
14607
15648
  shouldDeferForBattery(request) {
14608
15649
  return this.registration.backgroundIndexingPolicy !== null && (request.source === "startup" || request.source === "watcher");
@@ -14635,17 +15676,17 @@ var AutoIndexCoordinator = class {
14635
15676
  }
14636
15677
  }
14637
15678
  waitForBatteryRetry(delayMs) {
14638
- return new Promise((resolve20) => {
15679
+ return new Promise((resolve21) => {
14639
15680
  const timer = setTimeout(() => {
14640
15681
  if (this.batteryRetryTimer === timer) {
14641
15682
  this.batteryRetryTimer = null;
14642
15683
  this.resolveBatteryRetry = null;
14643
15684
  }
14644
- resolve20();
15685
+ resolve21();
14645
15686
  }, delayMs);
14646
15687
  timer.unref?.();
14647
15688
  this.batteryRetryTimer = timer;
14648
- this.resolveBatteryRetry = resolve20;
15689
+ this.resolveBatteryRetry = resolve21;
14649
15690
  });
14650
15691
  }
14651
15692
  cancelBatteryRetry() {
@@ -14653,9 +15694,9 @@ var AutoIndexCoordinator = class {
14653
15694
  clearTimeout(this.batteryRetryTimer);
14654
15695
  this.batteryRetryTimer = null;
14655
15696
  }
14656
- const resolve20 = this.resolveBatteryRetry;
15697
+ const resolve21 = this.resolveBatteryRetry;
14657
15698
  this.resolveBatteryRetry = null;
14658
- resolve20?.();
15699
+ resolve21?.();
14659
15700
  }
14660
15701
  finishBatteryCheck(batteryCheck) {
14661
15702
  if (this.batteryCheck !== batteryCheck) return;
@@ -14668,12 +15709,25 @@ var AutoIndexCoordinator = class {
14668
15709
  }
14669
15710
  };
14670
15711
  function getCoordinator(projectRoot, host) {
14671
- const key = coordinatorKeysByProject.get(projectLookupKey(projectRoot, host));
15712
+ const key = coordinatorKeysByProject.get(projectLookupKey2(projectRoot, host));
14672
15713
  return key ? coordinators.get(key) ?? null : null;
14673
15714
  }
14674
- function configureAutoIndex(projectRoot, host, config, getIndexer) {
14675
- 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);
14676
15726
  const safety = getProjectSafety(projectRoot, config);
15727
+ const synchronizeWorker = options.synchronizeBackgroundWorker ?? true;
15728
+ if (options.preserveManagedWorker === true && isBackgroundWorkerManaged(projectRoot, host)) {
15729
+ return;
15730
+ }
14677
15731
  const registration = {
14678
15732
  backgroundIndexingPolicy: createBackgroundIndexingPolicy(
14679
15733
  config.indexing.pauseBackgroundIndexingOnBattery
@@ -14691,6 +15745,9 @@ function configureAutoIndex(projectRoot, host, config, getIndexer) {
14691
15745
  const stopPrevious = previousCoordinator?.stop(true) ?? Promise.resolve();
14692
15746
  const activation = Promise.all([previousBarrier, stopPrevious]).then(() => void 0);
14693
15747
  coordinatorReplacementBarriers.set(projectKey, activation);
15748
+ if (synchronizeWorker) {
15749
+ synchronizeBackgroundWorker(projectRoot, host, config, safety.safeToRun);
15750
+ }
14694
15751
  coordinators.delete(previousKey);
14695
15752
  const coordinator2 = new AutoIndexCoordinator(registration);
14696
15753
  coordinator2.activateAfter(activation);
@@ -14706,11 +15763,17 @@ function configureAutoIndex(projectRoot, host, config, getIndexer) {
14706
15763
  coordinator.update(registration);
14707
15764
  }
14708
15765
  coordinatorKeysByProject.set(projectKey, key);
15766
+ if (synchronizeWorker) {
15767
+ synchronizeBackgroundWorker(projectRoot, host, config, safety.safeToRun);
15768
+ }
14709
15769
  }
14710
- function startAutoIndex(projectRoot, host, source = "startup") {
14711
- 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;
14712
15772
  }
14713
15773
  function requestBackgroundIndex(projectRoot, host) {
15774
+ if (isBackgroundWorkerManaged(projectRoot, host) && !isBackgroundWorkerLeader(projectRoot, host)) {
15775
+ return null;
15776
+ }
14714
15777
  return getCoordinator(projectRoot, host)?.request({
14715
15778
  checkFreshness: false,
14716
15779
  force: false,
@@ -14750,15 +15813,23 @@ async function waitForAutoIndexForRetrieval(projectRoot, host) {
14750
15813
  };
14751
15814
  }
14752
15815
  try {
14753
- 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);
14754
15821
  } catch {
14755
15822
  }
14756
- const job = coordinator.start("retrieval") ?? coordinator.currentJob();
15823
+ const job = startRetrievalRefresh(projectRoot, host, coordinator);
14757
15824
  if (job) {
14758
15825
  await withTimeout(job, coordinator.getWaitMs());
15826
+ } else if (isBackgroundWorkerManaged(projectRoot, host)) {
15827
+ await waitForPublishedSnapshot(coordinator, coordinator.getWaitMs());
14759
15828
  }
14760
15829
  try {
14761
- 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);
14762
15833
  } catch {
14763
15834
  }
14764
15835
  const status = coordinator.snapshot();
@@ -14779,31 +15850,62 @@ async function waitForAutoIndexForRetrieval(projectRoot, host) {
14779
15850
  text: `Automatic indexing is ${status.state}. Retry shortly or call index_status for progress. You can also run index_codebase explicitly.`
14780
15851
  };
14781
15852
  }
14782
- async function stopAutoIndex(projectRoot, host) {
14783
- 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);
14784
15859
  }
14785
- async function hasReadableCurrentIndex(coordinator) {
15860
+ async function getSearchReadiness(coordinator) {
14786
15861
  const indexer = coordinator.getIndexer();
14787
15862
  if (indexer.getIndexFreshness) {
14788
15863
  const freshness = await indexer.getIndexFreshness();
14789
- 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())));
14790
15893
  }
14791
- return (await indexer.getStatus()).indexed;
14792
15894
  }
14793
15895
 
14794
15896
  // src/tools/config-state.ts
14795
- import { existsSync as existsSync11, mkdirSync as mkdirSync5, writeFileSync as writeFileSync4 } from "fs";
14796
- import * as path20 from "path";
15897
+ import { existsSync as existsSync12, mkdirSync as mkdirSync6, writeFileSync as writeFileSync5 } from "fs";
15898
+ import * as path21 from "path";
14797
15899
 
14798
15900
  // src/config/merger.ts
14799
- import { existsSync as existsSync10, readFileSync as readFileSync9 } from "fs";
14800
- import * as path19 from "path";
15901
+ import { existsSync as existsSync11, readFileSync as readFileSync10 } from "fs";
15902
+ import * as path20 from "path";
14801
15903
 
14802
15904
  // src/config/rebase.ts
14803
- import * as path18 from "path";
15905
+ import * as path19 from "path";
14804
15906
  function isWithinRoot(rootDir, targetPath) {
14805
- const relativePath = path18.relative(rootDir, targetPath);
14806
- return relativePath === "" || !relativePath.startsWith("..") && !path18.isAbsolute(relativePath);
15907
+ const relativePath = path19.relative(rootDir, targetPath);
15908
+ return relativePath === "" || !relativePath.startsWith("..") && !path19.isAbsolute(relativePath);
14807
15909
  }
14808
15910
  function rebasePathEntries(values, fromDir, toDir) {
14809
15911
  if (!Array.isArray(values)) {
@@ -14811,10 +15913,10 @@ function rebasePathEntries(values, fromDir, toDir) {
14811
15913
  }
14812
15914
  return values.filter((value) => typeof value === "string").map((value) => {
14813
15915
  const trimmed = value.trim();
14814
- if (!trimmed || path18.isAbsolute(trimmed)) {
15916
+ if (!trimmed || path19.isAbsolute(trimmed)) {
14815
15917
  return trimmed;
14816
15918
  }
14817
- return normalizePathSeparators(path18.normalize(path18.relative(toDir, path18.resolve(fromDir, trimmed))));
15919
+ return normalizePathSeparators(path19.normalize(path19.relative(toDir, path19.resolve(fromDir, trimmed))));
14818
15920
  }).filter(Boolean);
14819
15921
  }
14820
15922
  function resolveInheritedKnowledgeBaseEntries(values, sourceRoot, targetRoot) {
@@ -14826,17 +15928,17 @@ function resolveInheritedKnowledgeBaseEntries(values, sourceRoot, targetRoot) {
14826
15928
  if (!trimmed) {
14827
15929
  return trimmed;
14828
15930
  }
14829
- if (path18.isAbsolute(trimmed)) {
15931
+ if (path19.isAbsolute(trimmed)) {
14830
15932
  if (isWithinRoot(sourceRoot, trimmed)) {
14831
- return normalizePathSeparators(path18.normalize(path18.relative(sourceRoot, trimmed) || "."));
15933
+ return normalizePathSeparators(path19.normalize(path19.relative(sourceRoot, trimmed) || "."));
14832
15934
  }
14833
- return path18.normalize(trimmed);
15935
+ return path19.normalize(trimmed);
14834
15936
  }
14835
- const resolvedFromSource = path18.resolve(sourceRoot, trimmed);
15937
+ const resolvedFromSource = path19.resolve(sourceRoot, trimmed);
14836
15938
  if (isWithinRoot(sourceRoot, resolvedFromSource)) {
14837
- return normalizePathSeparators(path18.normalize(trimmed));
15939
+ return normalizePathSeparators(path19.normalize(trimmed));
14838
15940
  }
14839
- return normalizePathSeparators(path18.normalize(path18.relative(targetRoot, resolvedFromSource)));
15941
+ return normalizePathSeparators(path19.normalize(path19.relative(targetRoot, resolvedFromSource)));
14840
15942
  }).filter(Boolean);
14841
15943
  }
14842
15944
 
@@ -14874,8 +15976,8 @@ function mergeUniqueStringArray(values) {
14874
15976
  return [...new Set(values.map((value) => String(value).trim()))];
14875
15977
  }
14876
15978
  function normalizeKnowledgeBasePath2(value) {
14877
- let normalized = path19.normalize(String(value).trim());
14878
- const root = path19.parse(normalized).root;
15979
+ let normalized = path20.normalize(String(value).trim());
15980
+ const root = path20.parse(normalized).root;
14879
15981
  while (normalized.length > root.length && /[\\/]$/.test(normalized)) {
14880
15982
  normalized = normalized.slice(0, -1);
14881
15983
  }
@@ -14909,11 +16011,11 @@ function validateConfigLayerShape(rawConfig, filePath) {
14909
16011
  return rawConfig;
14910
16012
  }
14911
16013
  function loadJsonFile(filePath) {
14912
- if (!existsSync10(filePath)) {
16014
+ if (!existsSync11(filePath)) {
14913
16015
  return null;
14914
16016
  }
14915
16017
  try {
14916
- const content = readFileSync9(filePath, "utf-8");
16018
+ const content = readFileSync10(filePath, "utf-8");
14917
16019
  return validateConfigLayerShape(JSON.parse(content), filePath);
14918
16020
  } catch (error) {
14919
16021
  if (error instanceof Error && error.message.startsWith("Config file ")) {
@@ -14933,7 +16035,7 @@ function loadProjectConfigLayer(projectRoot, host) {
14933
16035
  return {};
14934
16036
  }
14935
16037
  const normalizedConfig = { ...projectConfig };
14936
- const projectConfigBaseDir = path19.dirname(path19.dirname(projectConfigPath));
16038
+ const projectConfigBaseDir = path20.dirname(path20.dirname(projectConfigPath));
14937
16039
  if (Array.isArray(normalizedConfig.knowledgeBases)) {
14938
16040
  normalizedConfig.knowledgeBases = resolveInheritedKnowledgeBaseEntries(
14939
16041
  normalizedConfig.knowledgeBases,
@@ -15023,10 +16125,10 @@ function loadEditableConfig(projectRoot, host) {
15023
16125
  }
15024
16126
  function saveConfig(projectRoot, config, host) {
15025
16127
  const configPath = getConfigPath(projectRoot, host);
15026
- const configDir = path20.dirname(configPath);
15027
- const configBaseDir = path20.dirname(configDir);
15028
- if (!existsSync11(configDir)) {
15029
- 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 });
15030
16132
  }
15031
16133
  const serializableConfig = { ...config };
15032
16134
  if (Array.isArray(serializableConfig.knowledgeBases)) {
@@ -15034,7 +16136,7 @@ function saveConfig(projectRoot, config, host) {
15034
16136
  (kb) => serializeConfigPathValue(kb, configBaseDir)
15035
16137
  );
15036
16138
  }
15037
- writeFileSync4(configPath, JSON.stringify(serializableConfig, null, 2) + "\n", "utf-8");
16139
+ writeFileSync5(configPath, JSON.stringify(serializableConfig, null, 2) + "\n", "utf-8");
15038
16140
  }
15039
16141
 
15040
16142
  // src/tools/operation-runtime.ts
@@ -15113,15 +16215,24 @@ function getOrCreateIndexer(projectRoot, host) {
15113
16215
  }
15114
16216
  const indexer = new Indexer(projectRoot, config, host);
15115
16217
  indexerCache.set(key, indexer);
15116
- configureAutoIndex(projectRoot, host, config, () => getOrCreateIndexer(projectRoot, host));
16218
+ configureAutoIndex(projectRoot, host, config, () => getOrCreateIndexer(projectRoot, host), {
16219
+ preserveManagedWorker: true,
16220
+ synchronizeBackgroundWorker: false
16221
+ });
15117
16222
  return indexer;
15118
16223
  }
15119
- function initializeTools(projectRoot, config, host) {
16224
+ function initializeTools(projectRoot, config, host, options = {}) {
15120
16225
  defaultProjectRoots.set(host, projectRoot);
15121
16226
  const key = getIndexerCacheKey(projectRoot, host);
16227
+ if (options.preserveManagedWorker === true && isBackgroundWorkerManaged(projectRoot, host) && indexerCache.has(key)) {
16228
+ return;
16229
+ }
15122
16230
  configCache.set(key, config);
15123
16231
  indexerCache.set(key, new Indexer(projectRoot, config, host));
15124
- configureAutoIndex(projectRoot, host, config, () => getOrCreateIndexer(projectRoot, host));
16232
+ configureAutoIndex(projectRoot, host, config, () => getOrCreateIndexer(projectRoot, host), {
16233
+ preserveManagedWorker: options.preserveManagedWorker,
16234
+ synchronizeBackgroundWorker: false
16235
+ });
15125
16236
  }
15126
16237
  function getIndexerForProject(projectRoot, host) {
15127
16238
  const root = getProjectRoot(projectRoot, host);
@@ -15135,7 +16246,9 @@ function refreshIndexerForDirectory(projectRoot, host, config = parseConfig(load
15135
16246
  const key = getIndexerCacheKey(projectRoot, host);
15136
16247
  configCache.set(key, config);
15137
16248
  indexerCache.set(key, new Indexer(projectRoot, config, host));
15138
- configureAutoIndex(projectRoot, host, config, () => getOrCreateIndexer(projectRoot, host));
16249
+ configureAutoIndex(projectRoot, host, config, () => getOrCreateIndexer(projectRoot, host), {
16250
+ synchronizeBackgroundWorker: true
16251
+ });
15139
16252
  return config;
15140
16253
  }
15141
16254
  var AutoIndexRetrievalUnavailableError = class extends Error {
@@ -15162,7 +16275,7 @@ function trimOrUndefined(value) {
15162
16275
  return normalized || void 0;
15163
16276
  }
15164
16277
  function normalizeCallGraphPath(value) {
15165
- let normalized = path21.posix.normalize(value.trim().replaceAll("\\", "/"));
16278
+ let normalized = path22.posix.normalize(value.trim().replaceAll("\\", "/"));
15166
16279
  if (normalized.startsWith("./")) {
15167
16280
  normalized = normalized.slice(2);
15168
16281
  }
@@ -15355,12 +16468,12 @@ async function getCallGraphPath(projectRoot, host, from, to, maxDepth, fromFileP
15355
16468
  if (fromResolution.status !== "resolved" || toResolution.status !== "resolved") {
15356
16469
  return { from: fromResolution, to: toResolution, path: [] };
15357
16470
  }
15358
- const path33 = await indexer.findCallPathBySymbolIds(
16471
+ const path34 = await indexer.findCallPathBySymbolIds(
15359
16472
  fromResolution.symbolId,
15360
16473
  toResolution.symbolId,
15361
16474
  maxDepth
15362
16475
  );
15363
- return { from: fromResolution, to: toResolution, path: path33 };
16476
+ return { from: fromResolution, to: toResolution, path: path34 };
15364
16477
  }
15365
16478
  async function runIndexCodebase(projectRoot, host, args, onProgress) {
15366
16479
  const root = getProjectRoot(projectRoot, host);
@@ -15369,6 +16482,9 @@ async function runIndexCodebase(projectRoot, host, args, onProgress) {
15369
16482
  if (args.estimateOnly) {
15370
16483
  return { kind: "estimate", estimate: await indexer.estimateCost() };
15371
16484
  }
16485
+ if (args.dryRun) {
16486
+ return { kind: "dryrun", dryrun: await indexer.dryRunCost() };
16487
+ }
15372
16488
  const coordinated = runCoordinatedIndex(root, host, args.force ?? false, (progress) => {
15373
16489
  if (onProgress) {
15374
16490
  void onProgress(formatProgressTitle(progress), {
@@ -15555,15 +16671,15 @@ async function getIndexLogs(projectRoot, host, args) {
15555
16671
  function addKnowledgeBase(projectRoot, host, knowledgeBasePath) {
15556
16672
  const root = getProjectRoot(projectRoot, host);
15557
16673
  const inputPath = knowledgeBasePath.trim();
15558
- const normalizedPath3 = path21.resolve(
15559
- path21.isAbsolute(inputPath) ? inputPath : resolveKnowledgeBasePath(inputPath, root)
16674
+ const normalizedPath3 = path22.resolve(
16675
+ path22.isAbsolute(inputPath) ? inputPath : resolveKnowledgeBasePath(inputPath, root)
15560
16676
  );
15561
- if (!existsSync12(normalizedPath3)) {
16677
+ if (!existsSync13(normalizedPath3)) {
15562
16678
  return `Error: Directory does not exist: ${normalizedPath3}`;
15563
16679
  }
15564
16680
  let realPath;
15565
16681
  try {
15566
- realPath = realpathSync5(normalizedPath3);
16682
+ realPath = realpathSync6(normalizedPath3);
15567
16683
  } catch {
15568
16684
  return `Error: Cannot resolve path: ${normalizedPath3}`;
15569
16685
  }
@@ -15592,7 +16708,7 @@ function addKnowledgeBase(projectRoot, host, knowledgeBasePath) {
15592
16708
  }
15593
16709
  }
15594
16710
  for (const dotDir of sensitiveDotDirs) {
15595
- const sensitiveDir = path21.join(homeDir, dotDir);
16711
+ const sensitiveDir = path22.join(homeDir, dotDir);
15596
16712
  if (sensitiveDir && (realPath === sensitiveDir || realPath.startsWith(`${sensitiveDir}/`))) {
15597
16713
  return `Error: Adding sensitive directory as knowledge base is not allowed: ${normalizedPath3}`;
15598
16714
  }
@@ -15638,7 +16754,7 @@ function listKnowledgeBases(projectRoot, host) {
15638
16754
  for (let i = 0; i < knowledgeBases.length; i++) {
15639
16755
  const kb = knowledgeBases[i];
15640
16756
  const resolvedPath = resolveKnowledgeBasePath(kb, root);
15641
- const exists = existsSync12(resolvedPath);
16757
+ const exists = existsSync13(resolvedPath);
15642
16758
  result += `[${i + 1}] ${kb}
15643
16759
  `;
15644
16760
  result += ` Resolved: ${resolvedPath}
@@ -15655,7 +16771,7 @@ function listKnowledgeBases(projectRoot, host) {
15655
16771
  }
15656
16772
  result += "\n";
15657
16773
  }
15658
- const hasHostConfig = existsSync12(path21.join(root, getHostProjectConfigRelativePath(host)));
16774
+ const hasHostConfig = existsSync13(path22.join(root, getHostProjectConfigRelativePath(host)));
15659
16775
  if (hasHostConfig) {
15660
16776
  result += `
15661
16777
  Config sources: 1 file(s).`;
@@ -16128,7 +17244,7 @@ async function resolveCodebaseContextUnmeasured(projectRoot, host, input) {
16128
17244
  const directory = input.directory ?? void 0;
16129
17245
  const tokenBudget = input.tokenBudget ?? void 0;
16130
17246
  if (from && to) {
16131
- const path33 = await getCallGraphPath(
17247
+ const path34 = await getCallGraphPath(
16132
17248
  projectRoot,
16133
17249
  host,
16134
17250
  from,
@@ -16137,25 +17253,25 @@ async function resolveCodebaseContextUnmeasured(projectRoot, host, input) {
16137
17253
  fromFilePath,
16138
17254
  toFilePath
16139
17255
  );
16140
- const pathText = formatCallGraphPathResult(path33);
16141
- if (path33.path.length > 0) {
17256
+ const pathText = formatCallGraphPathResult(path34);
17257
+ if (path34.path.length > 0) {
16142
17258
  const fitted2 = fitTextToContextBudget(
16143
17259
  pathText,
16144
17260
  tokenBudget
16145
17261
  );
16146
17262
  return {
16147
17263
  text: fitted2.text,
16148
- details: fittedDetails("path", fitted2, path33.path.length)
17264
+ details: fittedDetails("path", fitted2, path34.path.length)
16149
17265
  };
16150
17266
  }
16151
- if (path33.from.status !== "resolved" || path33.to.status !== "resolved") {
17267
+ if (path34.from.status !== "resolved" || path34.to.status !== "resolved") {
16152
17268
  const fitted2 = fitTextToContextBudget(pathText, tokenBudget);
16153
17269
  return {
16154
17270
  text: fitted2.text,
16155
17271
  details: fittedDetails("path", fitted2, 0)
16156
17272
  };
16157
17273
  }
16158
- const resolvedFrom = path33.from;
17274
+ const resolvedFrom = path34.from;
16159
17275
  const { callers } = await getCallGraphData(projectRoot, host, {
16160
17276
  name: to,
16161
17277
  direction: "callers",
@@ -16633,9 +17749,9 @@ function getRelevantEvidence(query) {
16633
17749
  });
16634
17750
  }
16635
17751
  if (query.expected.acceptableFiles) {
16636
- for (const path33 of query.expected.acceptableFiles) {
17752
+ for (const path34 of query.expected.acceptableFiles) {
16637
17753
  legacyEvidence.push({
16638
- path: path33,
17754
+ path: path34,
16639
17755
  ...query.expected.symbol !== void 0 ? { symbol: query.expected.symbol } : {},
16640
17756
  relevance: 1
16641
17757
  });
@@ -16957,9 +18073,9 @@ function computeEvalMetrics(queries, perQuery, embeddingCallCount, embeddingToke
16957
18073
  }
16958
18074
 
16959
18075
  // src/eval/runner-config.ts
16960
- import { existsSync as existsSync13, mkdirSync as mkdirSync6, readFileSync as readFileSync10, rmSync as rmSync3, writeFileSync as writeFileSync5 } from "fs";
16961
- import * as os7 from "os";
16962
- 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";
16963
18079
  function isRecord2(value) {
16964
18080
  return typeof value === "object" && value !== null && !Array.isArray(value);
16965
18081
  }
@@ -16993,7 +18109,7 @@ function validateEvalConfigShape(rawConfig, filePath) {
16993
18109
  }
16994
18110
  function parseJsonConfigFile(filePath) {
16995
18111
  try {
16996
- return validateEvalConfigShape(JSON.parse(readFileSync10(filePath, "utf-8")), filePath);
18112
+ return validateEvalConfigShape(JSON.parse(readFileSync11(filePath, "utf-8")), filePath);
16997
18113
  } catch (error) {
16998
18114
  if (error instanceof Error && error.message.startsWith("Eval config at ")) {
16999
18115
  throw error;
@@ -17003,20 +18119,20 @@ function parseJsonConfigFile(filePath) {
17003
18119
  }
17004
18120
  }
17005
18121
  function toAbsolute(projectRoot, maybeRelative) {
17006
- return path22.isAbsolute(maybeRelative) ? maybeRelative : path22.join(projectRoot, maybeRelative);
18122
+ return path23.isAbsolute(maybeRelative) ? maybeRelative : path23.join(projectRoot, maybeRelative);
17007
18123
  }
17008
18124
  function isProjectScopedConfigPath(configPath) {
17009
- 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";
17010
18126
  }
17011
18127
  function normalizeEvalConfigKnowledgeBases(rawConfig, projectRoot, resolvedConfigPath) {
17012
18128
  const config = rawConfig && typeof rawConfig === "object" ? { ...rawConfig } : {};
17013
18129
  const rebaseEntries = (values) => isProjectScopedConfigPath(resolvedConfigPath) ? resolveInheritedKnowledgeBaseEntries(
17014
18130
  values,
17015
- path22.dirname(path22.dirname(resolvedConfigPath)),
18131
+ path23.dirname(path23.dirname(resolvedConfigPath)),
17016
18132
  projectRoot
17017
18133
  ) : rebasePathEntries(
17018
18134
  values,
17019
- path22.dirname(resolvedConfigPath),
18135
+ path23.dirname(resolvedConfigPath),
17020
18136
  projectRoot
17021
18137
  );
17022
18138
  if (Array.isArray(config.knowledgeBases)) {
@@ -17029,7 +18145,7 @@ function normalizeEvalConfigKnowledgeBases(rawConfig, projectRoot, resolvedConfi
17029
18145
  }
17030
18146
  function loadRawConfig(projectRoot, configPath) {
17031
18147
  const fromPath = configPath ? toAbsolute(projectRoot, configPath) : null;
17032
- if (fromPath && existsSync13(fromPath)) {
18148
+ if (fromPath && existsSync14(fromPath)) {
17033
18149
  return normalizeEvalConfigKnowledgeBases(
17034
18150
  parseJsonConfigFile(fromPath),
17035
18151
  projectRoot,
@@ -17037,15 +18153,15 @@ function loadRawConfig(projectRoot, configPath) {
17037
18153
  );
17038
18154
  }
17039
18155
  const projectConfig = resolveProjectConfigPath(projectRoot, "opencode");
17040
- if (existsSync13(projectConfig)) {
18156
+ if (existsSync14(projectConfig)) {
17041
18157
  return normalizeEvalConfigKnowledgeBases(
17042
18158
  parseJsonConfigFile(projectConfig),
17043
18159
  projectRoot,
17044
18160
  projectConfig
17045
18161
  );
17046
18162
  }
17047
- const globalConfig = path22.join(os7.homedir(), ".config", "opencode", "codebase-index.json");
17048
- if (existsSync13(globalConfig)) {
18163
+ const globalConfig = path23.join(os8.homedir(), ".config", "opencode", "codebase-index.json");
18164
+ if (existsSync14(globalConfig)) {
17049
18165
  return parseJsonConfigFile(globalConfig);
17050
18166
  }
17051
18167
  return {};
@@ -17054,24 +18170,24 @@ function getIndexRootPath(projectRoot, scope) {
17054
18170
  return scope === "global" ? getGlobalIndexPath("opencode") : resolveProjectIndexPath(projectRoot, scope, "opencode");
17055
18171
  }
17056
18172
  function getLocalProjectIndexRoot(projectRoot) {
17057
- return path22.join(projectRoot, ".opencode", "index");
18173
+ return path23.join(projectRoot, ".opencode", "index");
17058
18174
  }
17059
18175
  function getLocalProjectConfigPath(projectRoot) {
17060
- return path22.join(projectRoot, ".opencode", "codebase-index.json");
18176
+ return path23.join(projectRoot, ".opencode", "codebase-index.json");
17061
18177
  }
17062
18178
  function clearIndexRoot(projectRoot, scope) {
17063
18179
  const indexRoot = scope === "global" ? getIndexRootPath(projectRoot, scope) : getLocalProjectIndexRoot(projectRoot);
17064
- if (existsSync13(indexRoot)) {
17065
- rmSync3(indexRoot, { recursive: true, force: true });
18180
+ if (existsSync14(indexRoot)) {
18181
+ rmSync4(indexRoot, { recursive: true, force: true });
17066
18182
  }
17067
18183
  }
17068
18184
  function ensureLocalEvalProjectConfig(projectRoot, configPath) {
17069
18185
  const localConfigPath = getLocalProjectConfigPath(projectRoot);
17070
18186
  const resolvedConfigPath = configPath ? toAbsolute(projectRoot, configPath) : resolveProjectConfigPath(projectRoot, "opencode");
17071
- if (!configPath && existsSync13(localConfigPath)) {
18187
+ if (!configPath && existsSync14(localConfigPath)) {
17072
18188
  return localConfigPath;
17073
18189
  }
17074
- if (!existsSync13(resolvedConfigPath) || resolvedConfigPath === localConfigPath) {
18190
+ if (!existsSync14(resolvedConfigPath) || resolvedConfigPath === localConfigPath) {
17075
18191
  return resolvedConfigPath;
17076
18192
  }
17077
18193
  const sourceConfig = normalizeEvalConfigKnowledgeBases(
@@ -17079,8 +18195,8 @@ function ensureLocalEvalProjectConfig(projectRoot, configPath) {
17079
18195
  projectRoot,
17080
18196
  resolvedConfigPath
17081
18197
  );
17082
- mkdirSync6(path22.dirname(localConfigPath), { recursive: true });
17083
- 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");
17084
18200
  return localConfigPath;
17085
18201
  }
17086
18202
  function loadParsedConfig(projectRoot, configPath) {
@@ -17113,9 +18229,9 @@ function getEmbeddingCostPer1MTokens(embeddingProvider) {
17113
18229
  }
17114
18230
 
17115
18231
  // src/eval/schema.ts
17116
- import { readFileSync as readFileSync11 } from "fs";
18232
+ import { readFileSync as readFileSync12 } from "fs";
17117
18233
  function parseJsonFile(filePath) {
17118
- const content = readFileSync11(filePath, "utf-8");
18234
+ const content = readFileSync12(filePath, "utf-8");
17119
18235
  try {
17120
18236
  return JSON.parse(content);
17121
18237
  } catch (error) {
@@ -17132,68 +18248,68 @@ function isStringArray4(value) {
17132
18248
  function isNonEmptyString(value) {
17133
18249
  return typeof value === "string" && value.trim().length > 0;
17134
18250
  }
17135
- function asPositiveNumber(value, path33) {
18251
+ function asPositiveNumber(value, path34) {
17136
18252
  if (typeof value !== "number" || Number.isNaN(value) || value < 0) {
17137
- throw new Error(`${path33} must be a non-negative number`);
18253
+ throw new Error(`${path34} must be a non-negative number`);
17138
18254
  }
17139
18255
  return value;
17140
18256
  }
17141
- function parseQueryType(value, path33) {
18257
+ function parseQueryType(value, path34) {
17142
18258
  if (value === "definition" || value === "implementation-intent" || value === "similarity" || value === "keyword-heavy" || value === "conceptual") {
17143
18259
  return value;
17144
18260
  }
17145
18261
  throw new Error(
17146
- `${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`
17147
18263
  );
17148
18264
  }
17149
- function parseExpectedRoute(value, path33) {
18265
+ function parseExpectedRoute(value, path34) {
17150
18266
  if (value === void 0) return void 0;
17151
18267
  if (value === "search" || value === "definition") return value;
17152
- throw new Error(`${path33} must be one of: search, definition`);
18268
+ throw new Error(`${path34} must be one of: search, definition`);
17153
18269
  }
17154
- function parseExpectedOutcome(value, path33) {
18270
+ function parseExpectedOutcome(value, path34) {
17155
18271
  if (value === void 0) return void 0;
17156
18272
  if (value === "results" || value === "no-results") {
17157
18273
  return value;
17158
18274
  }
17159
- throw new Error(`${path33} must be one of: results, no-results`);
18275
+ throw new Error(`${path34} must be one of: results, no-results`);
17160
18276
  }
17161
- function parseRecoveryExpectation(value, path33) {
18277
+ function parseRecoveryExpectation(value, path34) {
17162
18278
  if (value === void 0) return void 0;
17163
18279
  if (value === "none" || value === "filter-relaxed") {
17164
18280
  return value;
17165
18281
  }
17166
- throw new Error(`${path33} must be one of: none, filter-relaxed`);
18282
+ throw new Error(`${path34} must be one of: none, filter-relaxed`);
17167
18283
  }
17168
- function parseQueryDifficulty(value, path33) {
18284
+ function parseQueryDifficulty(value, path34) {
17169
18285
  if (value === void 0) return void 0;
17170
18286
  if (value === "easy" || value === "medium" || value === "hard") {
17171
18287
  return value;
17172
18288
  }
17173
- throw new Error(`${path33} must be one of: easy, medium, hard`);
18289
+ throw new Error(`${path34} must be one of: easy, medium, hard`);
17174
18290
  }
17175
- function parseQueryTags(value, path33) {
18291
+ function parseQueryTags(value, path34) {
17176
18292
  if (value === void 0) return void 0;
17177
18293
  if (!isStringArray4(value) || value.some((tag) => tag.trim().length === 0)) {
17178
- 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`);
17179
18295
  }
17180
18296
  if (value.length > 16) {
17181
- throw new Error(`${path33} must contain at most 16 tags`);
18297
+ throw new Error(`${path34} must contain at most 16 tags`);
17182
18298
  }
17183
18299
  return value;
17184
18300
  }
17185
- function parseQueryArgs(value, path33) {
18301
+ function parseQueryArgs(value, path34) {
17186
18302
  if (value === void 0) return void 0;
17187
18303
  if (!isRecord3(value)) {
17188
- throw new Error(`${path33} must be an object`);
17189
- }
17190
- const symbol = parseStringOrUndefined(value.symbol, `${path33}.symbol`);
17191
- const filePath = parseStringOrUndefined(value.filePath, `${path33}.filePath`);
17192
- const fileType = parseStringOrUndefined(value.fileType, `${path33}.fileType`);
17193
- const directory = parseStringOrUndefined(value.directory, `${path33}.directory`);
17194
- const callerLimit = parsePositiveIntegerOrUndefined(value.callerLimit, `${path33}.callerLimit`);
17195
- const calleeLimit = parsePositiveIntegerOrUndefined(value.calleeLimit, `${path33}.calleeLimit`);
17196
- 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`);
17197
18313
  return {
17198
18314
  ...symbol !== void 0 ? { symbol } : {},
17199
18315
  ...filePath !== void 0 ? { filePath } : {},
@@ -17204,50 +18320,50 @@ function parseQueryArgs(value, path33) {
17204
18320
  ...tokenBudget !== void 0 ? { tokenBudget } : {}
17205
18321
  };
17206
18322
  }
17207
- function parsePositiveIntegerOrUndefined(value, path33) {
18323
+ function parsePositiveIntegerOrUndefined(value, path34) {
17208
18324
  if (value === void 0 || value === null) return void 0;
17209
18325
  if (typeof value !== "number" || !Number.isInteger(value) || value <= 0) {
17210
- throw new Error(`${path33} must be a positive integer`);
18326
+ throw new Error(`${path34} must be a positive integer`);
17211
18327
  }
17212
18328
  return value;
17213
18329
  }
17214
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-]+)*)?$/;
17215
- function parseSemanticVersion(value, path33) {
18331
+ function parseSemanticVersion(value, path34) {
17216
18332
  if (!isNonEmptyString(value)) {
17217
- throw new Error(`${path33} must be a non-empty string`);
18333
+ throw new Error(`${path34} must be a non-empty string`);
17218
18334
  }
17219
18335
  if (!SEMVER_VERSION_PATTERN.test(value)) {
17220
- 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)`);
17221
18337
  }
17222
18338
  return value;
17223
18339
  }
17224
- function parseRetrievalMode(value, path33) {
18340
+ function parseRetrievalMode(value, path34) {
17225
18341
  if (value === void 0 || value === "search") return "search";
17226
18342
  if (value === "context" || value === "edit-context") return value;
17227
- 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`);
17228
18344
  }
17229
- function parseStringOrUndefined(value, path33) {
18345
+ function parseStringOrUndefined(value, path34) {
17230
18346
  if (value === void 0 || value === null) return void 0;
17231
18347
  if (!isNonEmptyString(value)) {
17232
- throw new Error(`${path33} must be a non-empty string`);
18348
+ throw new Error(`${path34} must be a non-empty string`);
17233
18349
  }
17234
18350
  return value;
17235
18351
  }
17236
- function parseGradedEvidence(value, path33) {
18352
+ function parseGradedEvidence(value, path34) {
17237
18353
  if (value === void 0) return [];
17238
18354
  if (!Array.isArray(value)) {
17239
- throw new Error(`${path33} must be an array`);
18355
+ throw new Error(`${path34} must be an array`);
17240
18356
  }
17241
18357
  return value.map((entry, index) => {
17242
18358
  if (!isRecord3(entry)) {
17243
- throw new Error(`${path33}[${index}] must be an object`);
18359
+ throw new Error(`${path34}[${index}] must be an object`);
17244
18360
  }
17245
- const evidencePath = parseStringOrUndefined(entry.path, `${path33}[${index}].path`);
18361
+ const evidencePath = parseStringOrUndefined(entry.path, `${path34}[${index}].path`);
17246
18362
  if (evidencePath === void 0) {
17247
- throw new Error(`${path33}[${index}].path is required`);
18363
+ throw new Error(`${path34}[${index}].path is required`);
17248
18364
  }
17249
- const symbol = parseStringOrUndefined(entry.symbol, `${path33}[${index}].symbol`);
17250
- 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`);
17251
18367
  return {
17252
18368
  path: evidencePath,
17253
18369
  ...symbol !== void 0 ? { symbol } : {},
@@ -17255,27 +18371,27 @@ function parseGradedEvidence(value, path33) {
17255
18371
  };
17256
18372
  });
17257
18373
  }
17258
- function parseEvidenceRelevance(value, path33) {
18374
+ function parseEvidenceRelevance(value, path34) {
17259
18375
  if (value === void 0) {
17260
- throw new Error(`${path33} is required`);
18376
+ throw new Error(`${path34} is required`);
17261
18377
  }
17262
18378
  if (value !== 1 && value !== 2 && value !== 3) {
17263
- throw new Error(`${path33} must be 1, 2, or 3`);
18379
+ throw new Error(`${path34} must be 1, 2, or 3`);
17264
18380
  }
17265
18381
  return value;
17266
18382
  }
17267
- function parseExpectedGraphNeighbor(value, path33) {
18383
+ function parseExpectedGraphNeighbor(value, path34) {
17268
18384
  if (value === void 0) return void 0;
17269
18385
  if (!isRecord3(value)) {
17270
- throw new Error(`${path33} must be an object`);
18386
+ throw new Error(`${path34} must be an object`);
17271
18387
  }
17272
18388
  if (value.direction !== "caller" && value.direction !== "callee") {
17273
- throw new Error(`${path33}.direction must be one of: caller, callee`);
18389
+ throw new Error(`${path34}.direction must be one of: caller, callee`);
17274
18390
  }
17275
- const filePath = parseStringOrUndefined(value.filePath, `${path33}.filePath`);
17276
- const symbol = parseStringOrUndefined(value.symbol, `${path33}.symbol`);
18391
+ const filePath = parseStringOrUndefined(value.filePath, `${path34}.filePath`);
18392
+ const symbol = parseStringOrUndefined(value.symbol, `${path34}.symbol`);
17277
18393
  if (filePath === void 0 && symbol === void 0) {
17278
- throw new Error(`${path33} must include filePath or symbol`);
18394
+ throw new Error(`${path34} must include filePath or symbol`);
17279
18395
  }
17280
18396
  return {
17281
18397
  direction: value.direction,
@@ -17283,9 +18399,9 @@ function parseExpectedGraphNeighbor(value, path33) {
17283
18399
  ...symbol !== void 0 ? { symbol } : {}
17284
18400
  };
17285
18401
  }
17286
- function parseExpected(input, path33) {
18402
+ function parseExpected(input, path34) {
17287
18403
  if (!isRecord3(input)) {
17288
- throw new Error(`${path33} must be an object`);
18404
+ throw new Error(`${path34} must be an object`);
17289
18405
  }
17290
18406
  const filePathRaw = input.filePath;
17291
18407
  const acceptableFilesRaw = input.acceptableFiles;
@@ -17296,29 +18412,29 @@ function parseExpected(input, path33) {
17296
18412
  const recoveryExpectationRaw = input.recoveryExpectation;
17297
18413
  const gradedEvidenceRaw = input.gradedEvidence;
17298
18414
  const graphNeighborRaw = input.graphNeighbor;
17299
- const filePath = parseStringOrUndefined(filePathRaw, `${path33}.filePath`);
18415
+ const filePath = parseStringOrUndefined(filePathRaw, `${path34}.filePath`);
17300
18416
  const acceptableFiles = isStringArray4(acceptableFilesRaw) ? acceptableFilesRaw : void 0;
17301
- const gradedEvidence = parseGradedEvidence(gradedEvidenceRaw, `${path33}.gradedEvidence`);
17302
- const graphNeighbor = parseExpectedGraphNeighbor(graphNeighborRaw, `${path33}.graphNeighbor`);
17303
- 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`);
17304
18420
  if (expectedOutcome !== "no-results" && !filePath && (!acceptableFiles || acceptableFiles.length === 0) && gradedEvidence.length === 0) {
17305
18421
  throw new Error(
17306
- `${path33} must include expected.filePath, expected.acceptableFiles, or expected.gradedEvidence`
18422
+ `${path34} must include expected.filePath, expected.acceptableFiles, or expected.gradedEvidence`
17307
18423
  );
17308
18424
  }
17309
18425
  if (acceptableFilesRaw !== void 0 && !isStringArray4(acceptableFilesRaw)) {
17310
- throw new Error(`${path33}.acceptableFiles must be an array of strings`);
18426
+ throw new Error(`${path34}.acceptableFiles must be an array of strings`);
17311
18427
  }
17312
18428
  if (symbolRaw !== void 0 && typeof symbolRaw !== "string") {
17313
- throw new Error(`${path33}.symbol must be a string when provided`);
18429
+ throw new Error(`${path34}.symbol must be a string when provided`);
17314
18430
  }
17315
18431
  if (branchRaw !== void 0 && typeof branchRaw !== "string") {
17316
- throw new Error(`${path33}.branch must be a string when provided`);
18432
+ throw new Error(`${path34}.branch must be a string when provided`);
17317
18433
  }
17318
- const expectedRoute = parseExpectedRoute(expectedRouteRaw, `${path33}.expectedRoute`);
18434
+ const expectedRoute = parseExpectedRoute(expectedRouteRaw, `${path34}.expectedRoute`);
17319
18435
  const recoveryExpectation = parseRecoveryExpectation(
17320
18436
  recoveryExpectationRaw,
17321
- `${path33}.recoveryExpectation`
18437
+ `${path34}.recoveryExpectation`
17322
18438
  );
17323
18439
  return {
17324
18440
  filePath,
@@ -17332,13 +18448,13 @@ function parseExpected(input, path33) {
17332
18448
  ...graphNeighbor !== void 0 ? { graphNeighbor } : {}
17333
18449
  };
17334
18450
  }
17335
- function parseQueryLanguage(value, path33) {
17336
- return parseStringOrUndefined(value, path33);
18451
+ function parseQueryLanguage(value, path34) {
18452
+ return parseStringOrUndefined(value, path34);
17337
18453
  }
17338
18454
  function parseQuery(input, index) {
17339
- const path33 = `queries[${index}]`;
18455
+ const path34 = `queries[${index}]`;
17340
18456
  if (!isRecord3(input)) {
17341
- throw new Error(`${path33} must be an object`);
18457
+ throw new Error(`${path34} must be an object`);
17342
18458
  }
17343
18459
  const id = input.id;
17344
18460
  const query = input.query;
@@ -17350,21 +18466,21 @@ function parseQuery(input, index) {
17350
18466
  const tags = input.tags;
17351
18467
  const args = input.args;
17352
18468
  if (typeof id !== "string" || id.trim().length === 0) {
17353
- throw new Error(`${path33}.id must be a non-empty string`);
18469
+ throw new Error(`${path34}.id must be a non-empty string`);
17354
18470
  }
17355
18471
  if (typeof query !== "string" || query.trim().length === 0) {
17356
- throw new Error(`${path33}.query must be a non-empty string`);
18472
+ throw new Error(`${path34}.query must be a non-empty string`);
17357
18473
  }
17358
18474
  return {
17359
18475
  id,
17360
18476
  query,
17361
- queryType: parseQueryType(queryType, `${path33}.queryType`),
17362
- retrievalMode: parseRetrievalMode(retrievalMode, `${path33}.retrievalMode`),
17363
- language: parseQueryLanguage(language, `${path33}.language`),
17364
- difficulty: parseQueryDifficulty(difficulty, `${path33}.difficulty`),
17365
- args: parseQueryArgs(args, `${path33}.args`),
17366
- tags: parseQueryTags(tags, `${path33}.tags`),
17367
- 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`)
17368
18484
  };
17369
18485
  }
17370
18486
  function parseGoldenDataset(raw, sourceLabel) {
@@ -17780,13 +18896,13 @@ async function runEvaluation(options) {
17780
18896
  };
17781
18897
  const outputDir = createRunDirectory(toAbsolute(options.projectRoot, options.outputRoot));
17782
18898
  const perQueryArtifact = buildPerQueryArtifact(perQuery);
17783
- writeJson(path23.join(outputDir, "summary.json"), summary);
17784
- 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);
17785
18901
  let comparison;
17786
18902
  if (againstPath) {
17787
18903
  const baseline = loadSummary(againstPath);
17788
18904
  comparison = compareSummaries(summary, baseline, againstPath);
17789
- writeJson(path23.join(outputDir, "compare.json"), comparison);
18905
+ writeJson(path24.join(outputDir, "compare.json"), comparison);
17790
18906
  }
17791
18907
  let gate;
17792
18908
  if (options.ciMode) {
@@ -17796,10 +18912,10 @@ async function runEvaluation(options) {
17796
18912
  const budget = loadBudget(budgetPath);
17797
18913
  if (!comparison && budget.baselinePath) {
17798
18914
  const resolvedBaseline = toAbsolute(options.projectRoot, budget.baselinePath);
17799
- if (existsSync14(resolvedBaseline)) {
18915
+ if (existsSync15(resolvedBaseline)) {
17800
18916
  const baselineSummary = loadSummary(resolvedBaseline);
17801
18917
  comparison = compareSummaries(summary, baselineSummary, resolvedBaseline);
17802
- writeJson(path23.join(outputDir, "compare.json"), comparison);
18918
+ writeJson(path24.join(outputDir, "compare.json"), comparison);
17803
18919
  } else if (budget.failOnMissingBaseline) {
17804
18920
  throw new Error(
17805
18921
  `Budget baseline is missing: ${resolvedBaseline}. Set failOnMissingBaseline=false to allow CI run without baseline.`
@@ -17809,7 +18925,7 @@ async function runEvaluation(options) {
17809
18925
  gate = evaluateBudgetGate(budget, summary, comparison);
17810
18926
  }
17811
18927
  const markdown = createSummaryMarkdown(summary, comparison, gate);
17812
- writeText(path23.join(outputDir, "summary.md"), markdown);
18928
+ writeText(path24.join(outputDir, "summary.md"), markdown);
17813
18929
  return { outputDir, summary, perQuery, comparison, gate };
17814
18930
  } finally {
17815
18931
  await indexer.close();
@@ -17867,23 +18983,23 @@ async function runSweep(options, sweep) {
17867
18983
  bestByMrrAt10,
17868
18984
  bestByP95Latency
17869
18985
  };
17870
- writeJson(path23.join(outputDir, "compare.json"), aggregate);
18986
+ writeJson(path24.join(outputDir, "compare.json"), aggregate);
17871
18987
  const md = createSummaryMarkdown(
17872
18988
  bestByHitAt5?.summary ?? runs[0].summary,
17873
18989
  bestByHitAt5?.comparison,
17874
18990
  void 0,
17875
18991
  aggregate
17876
18992
  );
17877
- writeText(path23.join(outputDir, "summary.md"), md);
17878
- 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);
17879
18995
  return { outputDir, aggregate };
17880
18996
  }
17881
18997
 
17882
18998
  // src/eval/cli.ts
17883
- import * as path25 from "path";
18999
+ import * as path26 from "path";
17884
19000
 
17885
19001
  // src/eval/cli-parser.ts
17886
- import * as path24 from "path";
19002
+ import * as path25 from "path";
17887
19003
  function printUsage() {
17888
19004
  console.log(`
17889
19005
  Usage:
@@ -17955,12 +19071,12 @@ function parseEvalArgs(argv, cwd) {
17955
19071
  const arg = argv[i];
17956
19072
  const next = argv[i + 1];
17957
19073
  if (arg === "--project" && next) {
17958
- parsed.projectRoot = path24.resolve(cwd, next);
19074
+ parsed.projectRoot = path25.resolve(cwd, next);
17959
19075
  i += 1;
17960
19076
  continue;
17961
19077
  }
17962
19078
  if (arg === "--config" && next) {
17963
- parsed.configPath = path24.resolve(cwd, next);
19079
+ parsed.configPath = path25.resolve(cwd, next);
17964
19080
  i += 1;
17965
19081
  continue;
17966
19082
  }
@@ -18156,22 +19272,22 @@ async function handleEvalCommand(args, cwd) {
18156
19272
  if (!parsed.againstPath.endsWith(".json")) {
18157
19273
  throw new Error("eval diff --against must point to a summary JSON file");
18158
19274
  }
18159
- const currentSummary = loadSummary(path25.resolve(parsed.projectRoot, currentPath), {
19275
+ const currentSummary = loadSummary(path26.resolve(parsed.projectRoot, currentPath), {
18160
19276
  allowLegacyDiversityMetrics: true
18161
19277
  });
18162
- const baselineSummary = loadSummary(path25.resolve(parsed.projectRoot, parsed.againstPath), {
19278
+ const baselineSummary = loadSummary(path26.resolve(parsed.projectRoot, parsed.againstPath), {
18163
19279
  allowLegacyDiversityMetrics: true
18164
19280
  });
18165
19281
  const comparison = compareSummaries(
18166
19282
  currentSummary,
18167
19283
  baselineSummary,
18168
- path25.resolve(parsed.projectRoot, parsed.againstPath)
19284
+ path26.resolve(parsed.projectRoot, parsed.againstPath)
18169
19285
  );
18170
- const outputDir = createRunDirectory(path25.resolve(parsed.projectRoot, parsed.outputRoot));
19286
+ const outputDir = createRunDirectory(path26.resolve(parsed.projectRoot, parsed.outputRoot));
18171
19287
  const summaryMd = createSummaryMarkdown(currentSummary, comparison);
18172
- writeJson(path25.join(outputDir, "compare.json"), comparison);
18173
- writeText(path25.join(outputDir, "summary.md"), summaryMd);
18174
- 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);
18175
19291
  console.log(`Eval diff complete. Artifacts: ${outputDir}`);
18176
19292
  return 0;
18177
19293
  }
@@ -18182,9 +19298,9 @@ async function handleEvalCommand(args, cwd) {
18182
19298
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
18183
19299
 
18184
19300
  // src/package-metadata.ts
18185
- import { readFileSync as readFileSync12 } from "fs";
19301
+ import { readFileSync as readFileSync13 } from "fs";
18186
19302
  function getPackageVersion() {
18187
- 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"));
18188
19304
  if (raw && typeof raw === "object" && "version" in raw && typeof raw.version === "string") {
18189
19305
  return raw.version;
18190
19306
  }
@@ -18295,6 +19411,7 @@ async function executeCodebaseEditContext(projectRoot, host, args) {
18295
19411
  async function executeIndexCodebase(projectRoot, host, args, onProgress) {
18296
19412
  const result = await runIndexCodebase(projectRoot, host, args, onProgress);
18297
19413
  if (result.kind === "estimate") return { text: formatCostEstimate(result.estimate) };
19414
+ if (result.kind === "dryrun") return { text: formatDryRunEstimate(result.dryrun) };
18298
19415
  if (result.kind === "busy") return { text: result.text, isError: true };
18299
19416
  if (result.kind === "message") return { text: result.text };
18300
19417
  return { text: formatIndexStats(result.stats, args.verbose ?? false) };
@@ -18331,7 +19448,7 @@ async function executeCallGraph(projectRoot, host, args) {
18331
19448
  return { text: formatCallGraphResult(await getCallGraphData(projectRoot, host, args)) };
18332
19449
  }
18333
19450
  async function executeCallGraphPath(projectRoot, host, args) {
18334
- const path33 = await getCallGraphPath(
19451
+ const path34 = await getCallGraphPath(
18335
19452
  projectRoot,
18336
19453
  host,
18337
19454
  args.from,
@@ -18340,7 +19457,7 @@ async function executeCallGraphPath(projectRoot, host, args) {
18340
19457
  args.fromFilePath,
18341
19458
  args.toFilePath
18342
19459
  );
18343
- return { text: formatCallGraphPathResult(path33) };
19460
+ return { text: formatCallGraphPathResult(path34) };
18344
19461
  }
18345
19462
  async function executeCodeCommunities(projectRoot, host, args) {
18346
19463
  const result = await getCodeCommunities(projectRoot, host, args);
@@ -18622,6 +19739,7 @@ ${formatCodebasePeek(results)}`;
18622
19739
  {
18623
19740
  force: allowNullAsUndefined(z2.boolean().optional().default(false)).describe("Force reindex even if already indexed"),
18624
19741
  estimateOnly: allowNullAsUndefined(z2.boolean().optional().default(false)).describe("Only show cost estimate without indexing"),
19742
+ dryRun: allowNullAsUndefined(z2.boolean().optional().default(false)).describe("Parse the file set and report the exact embedding token total without indexing. Read-only; the index is not changed. The total is the value 'Tokens used' climbs to for a force index (and an upper bound for an incremental)."),
18625
19743
  verbose: allowNullAsUndefined(z2.boolean().optional().default(false)).describe("Show detailed info about skipped files and parsing failures")
18626
19744
  },
18627
19745
  async (args) => {
@@ -18834,9 +19952,64 @@ ${formatSearchResults(results)}` }] };
18834
19952
  }
18835
19953
 
18836
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
+ }
18837
19979
  function getServerInstructions(host) {
18838
19980
  const hostText = `host ${host}`;
18839
- return `This MCP server is the preferred codebase-understanding path for ${hostText}. Start a repository task with index_status when index readiness or freshness is unknown. Use codebase_context as the preferred first entry point because it returns a token-budgeted location pack and routes to definitions or call-graph helpers when symbol intent is present. Keep the default tokenBudget for normal discovery, then use implementation_lookup, codebase_search, or a targeted file read only for selected locations that need source content. Use codebase_peek for direct conceptual location lookup. For exact identifiers or exhaustive matches, use grep. After identifying symbols, use call_graph or call_graph_path to trace dependencies. If the index is unavailable, run index_codebase, then retry the retrieval tool.`;
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.`;
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);
18840
20013
  }
18841
20014
  function createMcpServer(projectRoot, config, host) {
18842
20015
  const server = new McpServer({
@@ -18845,11 +20018,14 @@ function createMcpServer(projectRoot, config, host) {
18845
20018
  }, {
18846
20019
  instructions: getServerInstructions(host)
18847
20020
  });
18848
- initializeTools(projectRoot, config, host);
18849
- 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
+ }
18850
20026
  let stopCoordinationPromise = null;
18851
20027
  const stopCoordination = () => {
18852
- stopCoordinationPromise ??= stopAutoIndex(projectRoot, host);
20028
+ stopCoordinationPromise ??= backgroundWorker.managesWorker ? releaseMcpBackgroundWorker(projectRoot, host) : Promise.resolve();
18853
20029
  return stopCoordinationPromise;
18854
20030
  };
18855
20031
  const closeProtocol = server.server.close.bind(server.server);
@@ -18865,7 +20041,9 @@ function createMcpServer(projectRoot, config, host) {
18865
20041
  const onServerClose = server.server.onclose;
18866
20042
  server.server.onclose = () => {
18867
20043
  onServerClose?.();
18868
- void stopCoordination();
20044
+ void stopCoordination().catch((error) => {
20045
+ console.error("[codebase-index] Failed to stop MCP background worker after transport close:", error);
20046
+ });
18869
20047
  };
18870
20048
  registerMcpTools(server, {
18871
20049
  projectRoot,
@@ -18876,7 +20054,7 @@ function createMcpServer(projectRoot, config, host) {
18876
20054
  }
18877
20055
 
18878
20056
  // src/watcher/file-watcher.ts
18879
- import { existsSync as existsSync15, statSync as statSync6 } from "fs";
20057
+ import { existsSync as existsSync16, statSync as statSync6 } from "fs";
18880
20058
 
18881
20059
  // node_modules/chokidar/index.js
18882
20060
  import { EventEmitter as EventEmitter2 } from "events";
@@ -18968,7 +20146,7 @@ var ReaddirpStream = class extends Readable {
18968
20146
  this._directoryFilter = normalizeFilter(opts.directoryFilter);
18969
20147
  const statMethod = opts.lstat ? lstat : stat;
18970
20148
  if (wantBigintFsStats) {
18971
- this._stat = (path33) => statMethod(path33, { bigint: true });
20149
+ this._stat = (path34) => statMethod(path34, { bigint: true });
18972
20150
  } else {
18973
20151
  this._stat = statMethod;
18974
20152
  }
@@ -18993,8 +20171,8 @@ var ReaddirpStream = class extends Readable {
18993
20171
  const par = this.parent;
18994
20172
  const fil = par && par.files;
18995
20173
  if (fil && fil.length > 0) {
18996
- const { path: path33, depth } = par;
18997
- 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));
18998
20176
  const awaited = await Promise.all(slice);
18999
20177
  for (const entry of awaited) {
19000
20178
  if (!entry)
@@ -19034,21 +20212,21 @@ var ReaddirpStream = class extends Readable {
19034
20212
  this.reading = false;
19035
20213
  }
19036
20214
  }
19037
- async _exploreDir(path33, depth) {
20215
+ async _exploreDir(path34, depth) {
19038
20216
  let files;
19039
20217
  try {
19040
- files = await readdir(path33, this._rdOptions);
20218
+ files = await readdir(path34, this._rdOptions);
19041
20219
  } catch (error) {
19042
20220
  this._onError(error);
19043
20221
  }
19044
- return { files, depth, path: path33 };
20222
+ return { files, depth, path: path34 };
19045
20223
  }
19046
- async _formatEntry(dirent, path33) {
20224
+ async _formatEntry(dirent, path34) {
19047
20225
  let entry;
19048
- const basename8 = this._isDirent ? dirent.name : dirent;
20226
+ const basename9 = this._isDirent ? dirent.name : dirent;
19049
20227
  try {
19050
- const fullPath = presolve(pjoin(path33, basename8));
19051
- 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 };
19052
20230
  entry[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath);
19053
20231
  } catch (err) {
19054
20232
  this._onError(err);
@@ -19447,16 +20625,16 @@ var delFromSet = (main, prop, item) => {
19447
20625
  };
19448
20626
  var isEmptySet = (val) => val instanceof Set ? val.size === 0 : !val;
19449
20627
  var FsWatchInstances = /* @__PURE__ */ new Map();
19450
- function createFsWatchInstance(path33, options, listener, errHandler, emitRaw) {
20628
+ function createFsWatchInstance(path34, options, listener, errHandler, emitRaw) {
19451
20629
  const handleEvent = (rawEvent, evPath) => {
19452
- listener(path33);
19453
- emitRaw(rawEvent, evPath, { watchedPath: path33 });
19454
- if (evPath && path33 !== evPath) {
19455
- 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));
19456
20634
  }
19457
20635
  };
19458
20636
  try {
19459
- return fs_watch(path33, {
20637
+ return fs_watch(path34, {
19460
20638
  persistent: options.persistent
19461
20639
  }, handleEvent);
19462
20640
  } catch (error) {
@@ -19472,12 +20650,12 @@ var fsWatchBroadcast = (fullPath, listenerType, val1, val2, val3) => {
19472
20650
  listener(val1, val2, val3);
19473
20651
  });
19474
20652
  };
19475
- var setFsWatchListener = (path33, fullPath, options, handlers) => {
20653
+ var setFsWatchListener = (path34, fullPath, options, handlers) => {
19476
20654
  const { listener, errHandler, rawEmitter } = handlers;
19477
20655
  let cont = FsWatchInstances.get(fullPath);
19478
20656
  let watcher;
19479
20657
  if (!options.persistent) {
19480
- watcher = createFsWatchInstance(path33, options, listener, errHandler, rawEmitter);
20658
+ watcher = createFsWatchInstance(path34, options, listener, errHandler, rawEmitter);
19481
20659
  if (!watcher)
19482
20660
  return;
19483
20661
  return watcher.close.bind(watcher);
@@ -19488,7 +20666,7 @@ var setFsWatchListener = (path33, fullPath, options, handlers) => {
19488
20666
  addAndConvert(cont, KEY_RAW, rawEmitter);
19489
20667
  } else {
19490
20668
  watcher = createFsWatchInstance(
19491
- path33,
20669
+ path34,
19492
20670
  options,
19493
20671
  fsWatchBroadcast.bind(null, fullPath, KEY_LISTENERS),
19494
20672
  errHandler,
@@ -19503,7 +20681,7 @@ var setFsWatchListener = (path33, fullPath, options, handlers) => {
19503
20681
  cont.watcherUnusable = true;
19504
20682
  if (isWindows && error.code === "EPERM") {
19505
20683
  try {
19506
- const fd = await open(path33, "r");
20684
+ const fd = await open(path34, "r");
19507
20685
  await fd.close();
19508
20686
  broadcastErr(error);
19509
20687
  } catch (err) {
@@ -19534,7 +20712,7 @@ var setFsWatchListener = (path33, fullPath, options, handlers) => {
19534
20712
  };
19535
20713
  };
19536
20714
  var FsWatchFileInstances = /* @__PURE__ */ new Map();
19537
- var setFsWatchFileListener = (path33, fullPath, options, handlers) => {
20715
+ var setFsWatchFileListener = (path34, fullPath, options, handlers) => {
19538
20716
  const { listener, rawEmitter } = handlers;
19539
20717
  let cont = FsWatchFileInstances.get(fullPath);
19540
20718
  const copts = cont && cont.options;
@@ -19556,7 +20734,7 @@ var setFsWatchFileListener = (path33, fullPath, options, handlers) => {
19556
20734
  });
19557
20735
  const currmtime = curr.mtimeMs;
19558
20736
  if (curr.size !== prev.size || currmtime > prev.mtimeMs || currmtime === 0) {
19559
- foreach(cont.listeners, (listener2) => listener2(path33, curr));
20737
+ foreach(cont.listeners, (listener2) => listener2(path34, curr));
19560
20738
  }
19561
20739
  })
19562
20740
  };
@@ -19586,13 +20764,13 @@ var NodeFsHandler = class {
19586
20764
  * @param listener on fs change
19587
20765
  * @returns closer for the watcher instance
19588
20766
  */
19589
- _watchWithNodeFs(path33, listener) {
20767
+ _watchWithNodeFs(path34, listener) {
19590
20768
  const opts = this.fsw.options;
19591
- const directory = sp.dirname(path33);
19592
- const basename8 = sp.basename(path33);
20769
+ const directory = sp.dirname(path34);
20770
+ const basename9 = sp.basename(path34);
19593
20771
  const parent = this.fsw._getWatchedDir(directory);
19594
- parent.add(basename8);
19595
- const absolutePath = sp.resolve(path33);
20772
+ parent.add(basename9);
20773
+ const absolutePath = sp.resolve(path34);
19596
20774
  const options = {
19597
20775
  persistent: opts.persistent
19598
20776
  };
@@ -19601,13 +20779,13 @@ var NodeFsHandler = class {
19601
20779
  let closer;
19602
20780
  if (opts.usePolling) {
19603
20781
  const enableBin = opts.interval !== opts.binaryInterval;
19604
- options.interval = enableBin && isBinaryPath(basename8) ? opts.binaryInterval : opts.interval;
19605
- closer = setFsWatchFileListener(path33, absolutePath, options, {
20782
+ options.interval = enableBin && isBinaryPath(basename9) ? opts.binaryInterval : opts.interval;
20783
+ closer = setFsWatchFileListener(path34, absolutePath, options, {
19606
20784
  listener,
19607
20785
  rawEmitter: this.fsw._emitRaw
19608
20786
  });
19609
20787
  } else {
19610
- closer = setFsWatchListener(path33, absolutePath, options, {
20788
+ closer = setFsWatchListener(path34, absolutePath, options, {
19611
20789
  listener,
19612
20790
  errHandler: this._boundHandleError,
19613
20791
  rawEmitter: this.fsw._emitRaw
@@ -19623,13 +20801,13 @@ var NodeFsHandler = class {
19623
20801
  if (this.fsw.closed) {
19624
20802
  return;
19625
20803
  }
19626
- const dirname15 = sp.dirname(file);
19627
- const basename8 = sp.basename(file);
19628
- 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);
19629
20807
  let prevStats = stats;
19630
- if (parent.has(basename8))
20808
+ if (parent.has(basename9))
19631
20809
  return;
19632
- const listener = async (path33, newStats) => {
20810
+ const listener = async (path34, newStats) => {
19633
20811
  if (!this.fsw._throttle(THROTTLE_MODE_WATCH, file, 5))
19634
20812
  return;
19635
20813
  if (!newStats || newStats.mtimeMs === 0) {
@@ -19643,18 +20821,18 @@ var NodeFsHandler = class {
19643
20821
  this.fsw._emit(EV.CHANGE, file, newStats2);
19644
20822
  }
19645
20823
  if ((isMacos || isLinux || isFreeBSD) && prevStats.ino !== newStats2.ino) {
19646
- this.fsw._closeFile(path33);
20824
+ this.fsw._closeFile(path34);
19647
20825
  prevStats = newStats2;
19648
20826
  const closer2 = this._watchWithNodeFs(file, listener);
19649
20827
  if (closer2)
19650
- this.fsw._addPathCloser(path33, closer2);
20828
+ this.fsw._addPathCloser(path34, closer2);
19651
20829
  } else {
19652
20830
  prevStats = newStats2;
19653
20831
  }
19654
20832
  } catch (error) {
19655
- this.fsw._remove(dirname15, basename8);
20833
+ this.fsw._remove(dirname16, basename9);
19656
20834
  }
19657
- } else if (parent.has(basename8)) {
20835
+ } else if (parent.has(basename9)) {
19658
20836
  const at = newStats.atimeMs;
19659
20837
  const mt = newStats.mtimeMs;
19660
20838
  if (!at || at <= mt || mt !== prevStats.mtimeMs) {
@@ -19679,7 +20857,7 @@ var NodeFsHandler = class {
19679
20857
  * @param item basename of this item
19680
20858
  * @returns true if no more processing is needed for this entry.
19681
20859
  */
19682
- async _handleSymlink(entry, directory, path33, item) {
20860
+ async _handleSymlink(entry, directory, path34, item) {
19683
20861
  if (this.fsw.closed) {
19684
20862
  return;
19685
20863
  }
@@ -19689,7 +20867,7 @@ var NodeFsHandler = class {
19689
20867
  this.fsw._incrReadyCount();
19690
20868
  let linkPath;
19691
20869
  try {
19692
- linkPath = await fsrealpath(path33);
20870
+ linkPath = await fsrealpath(path34);
19693
20871
  } catch (e) {
19694
20872
  this.fsw._emitReady();
19695
20873
  return true;
@@ -19699,12 +20877,12 @@ var NodeFsHandler = class {
19699
20877
  if (dir.has(item)) {
19700
20878
  if (this.fsw._symlinkPaths.get(full) !== linkPath) {
19701
20879
  this.fsw._symlinkPaths.set(full, linkPath);
19702
- this.fsw._emit(EV.CHANGE, path33, entry.stats);
20880
+ this.fsw._emit(EV.CHANGE, path34, entry.stats);
19703
20881
  }
19704
20882
  } else {
19705
20883
  dir.add(item);
19706
20884
  this.fsw._symlinkPaths.set(full, linkPath);
19707
- this.fsw._emit(EV.ADD, path33, entry.stats);
20885
+ this.fsw._emit(EV.ADD, path34, entry.stats);
19708
20886
  }
19709
20887
  this.fsw._emitReady();
19710
20888
  return true;
@@ -19734,9 +20912,9 @@ var NodeFsHandler = class {
19734
20912
  return;
19735
20913
  }
19736
20914
  const item = entry.path;
19737
- let path33 = sp.join(directory, item);
20915
+ let path34 = sp.join(directory, item);
19738
20916
  current.add(item);
19739
- if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path33, item)) {
20917
+ if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path34, item)) {
19740
20918
  return;
19741
20919
  }
19742
20920
  if (this.fsw.closed) {
@@ -19745,11 +20923,11 @@ var NodeFsHandler = class {
19745
20923
  }
19746
20924
  if (item === target || !target && !previous.has(item)) {
19747
20925
  this.fsw._incrReadyCount();
19748
- path33 = sp.join(dir, sp.relative(dir, path33));
19749
- this._addToNodeFs(path33, initialAdd, wh, depth + 1);
20926
+ path34 = sp.join(dir, sp.relative(dir, path34));
20927
+ this._addToNodeFs(path34, initialAdd, wh, depth + 1);
19750
20928
  }
19751
20929
  }).on(EV.ERROR, this._boundHandleError);
19752
- return new Promise((resolve20, reject) => {
20930
+ return new Promise((resolve21, reject) => {
19753
20931
  if (!stream)
19754
20932
  return reject();
19755
20933
  stream.once(STR_END, () => {
@@ -19758,7 +20936,7 @@ var NodeFsHandler = class {
19758
20936
  return;
19759
20937
  }
19760
20938
  const wasThrottled = throttler ? throttler.clear() : false;
19761
- resolve20(void 0);
20939
+ resolve21(void 0);
19762
20940
  previous.getChildren().filter((item) => {
19763
20941
  return item !== directory && !current.has(item);
19764
20942
  }).forEach((item) => {
@@ -19815,13 +20993,13 @@ var NodeFsHandler = class {
19815
20993
  * @param depth Child path actually targeted for watch
19816
20994
  * @param target Child path actually targeted for watch
19817
20995
  */
19818
- async _addToNodeFs(path33, initialAdd, priorWh, depth, target) {
20996
+ async _addToNodeFs(path34, initialAdd, priorWh, depth, target) {
19819
20997
  const ready = this.fsw._emitReady;
19820
- if (this.fsw._isIgnored(path33) || this.fsw.closed) {
20998
+ if (this.fsw._isIgnored(path34) || this.fsw.closed) {
19821
20999
  ready();
19822
21000
  return false;
19823
21001
  }
19824
- const wh = this.fsw._getWatchHelpers(path33);
21002
+ const wh = this.fsw._getWatchHelpers(path34);
19825
21003
  if (priorWh) {
19826
21004
  wh.filterPath = (entry) => priorWh.filterPath(entry);
19827
21005
  wh.filterDir = (entry) => priorWh.filterDir(entry);
@@ -19837,8 +21015,8 @@ var NodeFsHandler = class {
19837
21015
  const follow = this.fsw.options.followSymlinks;
19838
21016
  let closer;
19839
21017
  if (stats.isDirectory()) {
19840
- const absPath = sp.resolve(path33);
19841
- const targetPath = follow ? await fsrealpath(path33) : path33;
21018
+ const absPath = sp.resolve(path34);
21019
+ const targetPath = follow ? await fsrealpath(path34) : path34;
19842
21020
  if (this.fsw.closed)
19843
21021
  return;
19844
21022
  closer = await this._handleDir(wh.watchPath, stats, initialAdd, depth, target, wh, targetPath);
@@ -19848,29 +21026,29 @@ var NodeFsHandler = class {
19848
21026
  this.fsw._symlinkPaths.set(absPath, targetPath);
19849
21027
  }
19850
21028
  } else if (stats.isSymbolicLink()) {
19851
- const targetPath = follow ? await fsrealpath(path33) : path33;
21029
+ const targetPath = follow ? await fsrealpath(path34) : path34;
19852
21030
  if (this.fsw.closed)
19853
21031
  return;
19854
21032
  const parent = sp.dirname(wh.watchPath);
19855
21033
  this.fsw._getWatchedDir(parent).add(wh.watchPath);
19856
21034
  this.fsw._emit(EV.ADD, wh.watchPath, stats);
19857
- closer = await this._handleDir(parent, stats, initialAdd, depth, path33, wh, targetPath);
21035
+ closer = await this._handleDir(parent, stats, initialAdd, depth, path34, wh, targetPath);
19858
21036
  if (this.fsw.closed)
19859
21037
  return;
19860
21038
  if (targetPath !== void 0) {
19861
- this.fsw._symlinkPaths.set(sp.resolve(path33), targetPath);
21039
+ this.fsw._symlinkPaths.set(sp.resolve(path34), targetPath);
19862
21040
  }
19863
21041
  } else {
19864
21042
  closer = this._handleFile(wh.watchPath, stats, initialAdd);
19865
21043
  }
19866
21044
  ready();
19867
21045
  if (closer)
19868
- this.fsw._addPathCloser(path33, closer);
21046
+ this.fsw._addPathCloser(path34, closer);
19869
21047
  return false;
19870
21048
  } catch (error) {
19871
21049
  if (this.fsw._handleError(error)) {
19872
21050
  ready();
19873
- return path33;
21051
+ return path34;
19874
21052
  }
19875
21053
  }
19876
21054
  }
@@ -19913,24 +21091,24 @@ function createPattern(matcher) {
19913
21091
  }
19914
21092
  return () => false;
19915
21093
  }
19916
- function normalizePath3(path33) {
19917
- if (typeof path33 !== "string")
21094
+ function normalizePath3(path34) {
21095
+ if (typeof path34 !== "string")
19918
21096
  throw new Error("string expected");
19919
- path33 = sp2.normalize(path33);
19920
- path33 = path33.replace(/\\/g, "/");
21097
+ path34 = sp2.normalize(path34);
21098
+ path34 = path34.replace(/\\/g, "/");
19921
21099
  let prepend = false;
19922
- if (path33.startsWith("//"))
21100
+ if (path34.startsWith("//"))
19923
21101
  prepend = true;
19924
- path33 = path33.replace(DOUBLE_SLASH_RE, "/");
21102
+ path34 = path34.replace(DOUBLE_SLASH_RE, "/");
19925
21103
  if (prepend)
19926
- path33 = "/" + path33;
19927
- return path33;
21104
+ path34 = "/" + path34;
21105
+ return path34;
19928
21106
  }
19929
21107
  function matchPatterns(patterns, testString, stats) {
19930
- const path33 = normalizePath3(testString);
21108
+ const path34 = normalizePath3(testString);
19931
21109
  for (let index = 0; index < patterns.length; index++) {
19932
21110
  const pattern = patterns[index];
19933
- if (pattern(path33, stats)) {
21111
+ if (pattern(path34, stats)) {
19934
21112
  return true;
19935
21113
  }
19936
21114
  }
@@ -19968,19 +21146,19 @@ var toUnix = (string) => {
19968
21146
  }
19969
21147
  return str;
19970
21148
  };
19971
- var normalizePathToUnix = (path33) => toUnix(sp2.normalize(toUnix(path33)));
19972
- var normalizeIgnored = (cwd = "") => (path33) => {
19973
- if (typeof path33 === "string") {
19974
- 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));
19975
21153
  } else {
19976
- return path33;
21154
+ return path34;
19977
21155
  }
19978
21156
  };
19979
- var getAbsolutePath = (path33, cwd) => {
19980
- if (sp2.isAbsolute(path33)) {
19981
- return path33;
21157
+ var getAbsolutePath = (path34, cwd) => {
21158
+ if (sp2.isAbsolute(path34)) {
21159
+ return path34;
19982
21160
  }
19983
- return sp2.join(cwd, path33);
21161
+ return sp2.join(cwd, path34);
19984
21162
  };
19985
21163
  var EMPTY_SET = Object.freeze(/* @__PURE__ */ new Set());
19986
21164
  var DirEntry = class {
@@ -20045,10 +21223,10 @@ var WatchHelper = class {
20045
21223
  dirParts;
20046
21224
  followSymlinks;
20047
21225
  statMethod;
20048
- constructor(path33, follow, fsw) {
21226
+ constructor(path34, follow, fsw) {
20049
21227
  this.fsw = fsw;
20050
- const watchPath = path33;
20051
- this.path = path33 = path33.replace(REPLACER_RE, "");
21228
+ const watchPath = path34;
21229
+ this.path = path34 = path34.replace(REPLACER_RE, "");
20052
21230
  this.watchPath = watchPath;
20053
21231
  this.fullWatchPath = sp2.resolve(watchPath);
20054
21232
  this.dirParts = [];
@@ -20188,20 +21366,20 @@ var FSWatcher = class extends EventEmitter2 {
20188
21366
  this._closePromise = void 0;
20189
21367
  let paths = unifyPaths(paths_);
20190
21368
  if (cwd) {
20191
- paths = paths.map((path33) => {
20192
- const absPath = getAbsolutePath(path33, cwd);
21369
+ paths = paths.map((path34) => {
21370
+ const absPath = getAbsolutePath(path34, cwd);
20193
21371
  return absPath;
20194
21372
  });
20195
21373
  }
20196
- paths.forEach((path33) => {
20197
- this._removeIgnoredPath(path33);
21374
+ paths.forEach((path34) => {
21375
+ this._removeIgnoredPath(path34);
20198
21376
  });
20199
21377
  this._userIgnored = void 0;
20200
21378
  if (!this._readyCount)
20201
21379
  this._readyCount = 0;
20202
21380
  this._readyCount += paths.length;
20203
- Promise.all(paths.map(async (path33) => {
20204
- 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);
20205
21383
  if (res)
20206
21384
  this._emitReady();
20207
21385
  return res;
@@ -20223,17 +21401,17 @@ var FSWatcher = class extends EventEmitter2 {
20223
21401
  return this;
20224
21402
  const paths = unifyPaths(paths_);
20225
21403
  const { cwd } = this.options;
20226
- paths.forEach((path33) => {
20227
- if (!sp2.isAbsolute(path33) && !this._closers.has(path33)) {
21404
+ paths.forEach((path34) => {
21405
+ if (!sp2.isAbsolute(path34) && !this._closers.has(path34)) {
20228
21406
  if (cwd)
20229
- path33 = sp2.join(cwd, path33);
20230
- path33 = sp2.resolve(path33);
21407
+ path34 = sp2.join(cwd, path34);
21408
+ path34 = sp2.resolve(path34);
20231
21409
  }
20232
- this._closePath(path33);
20233
- this._addIgnoredPath(path33);
20234
- if (this._watched.has(path33)) {
21410
+ this._closePath(path34);
21411
+ this._addIgnoredPath(path34);
21412
+ if (this._watched.has(path34)) {
20235
21413
  this._addIgnoredPath({
20236
- path: path33,
21414
+ path: path34,
20237
21415
  recursive: true
20238
21416
  });
20239
21417
  }
@@ -20297,38 +21475,38 @@ var FSWatcher = class extends EventEmitter2 {
20297
21475
  * @param stats arguments to be passed with event
20298
21476
  * @returns the error if defined, otherwise the value of the FSWatcher instance's `closed` flag
20299
21477
  */
20300
- async _emit(event, path33, stats) {
21478
+ async _emit(event, path34, stats) {
20301
21479
  if (this.closed)
20302
21480
  return;
20303
21481
  const opts = this.options;
20304
21482
  if (isWindows)
20305
- path33 = sp2.normalize(path33);
21483
+ path34 = sp2.normalize(path34);
20306
21484
  if (opts.cwd)
20307
- path33 = sp2.relative(opts.cwd, path33);
20308
- const args = [path33];
21485
+ path34 = sp2.relative(opts.cwd, path34);
21486
+ const args = [path34];
20309
21487
  if (stats != null)
20310
21488
  args.push(stats);
20311
21489
  const awf = opts.awaitWriteFinish;
20312
21490
  let pw;
20313
- if (awf && (pw = this._pendingWrites.get(path33))) {
21491
+ if (awf && (pw = this._pendingWrites.get(path34))) {
20314
21492
  pw.lastChange = /* @__PURE__ */ new Date();
20315
21493
  return this;
20316
21494
  }
20317
21495
  if (opts.atomic) {
20318
21496
  if (event === EVENTS.UNLINK) {
20319
- this._pendingUnlinks.set(path33, [event, ...args]);
21497
+ this._pendingUnlinks.set(path34, [event, ...args]);
20320
21498
  setTimeout(() => {
20321
- this._pendingUnlinks.forEach((entry, path34) => {
21499
+ this._pendingUnlinks.forEach((entry, path35) => {
20322
21500
  this.emit(...entry);
20323
21501
  this.emit(EVENTS.ALL, ...entry);
20324
- this._pendingUnlinks.delete(path34);
21502
+ this._pendingUnlinks.delete(path35);
20325
21503
  });
20326
21504
  }, typeof opts.atomic === "number" ? opts.atomic : 100);
20327
21505
  return this;
20328
21506
  }
20329
- if (event === EVENTS.ADD && this._pendingUnlinks.has(path33)) {
21507
+ if (event === EVENTS.ADD && this._pendingUnlinks.has(path34)) {
20330
21508
  event = EVENTS.CHANGE;
20331
- this._pendingUnlinks.delete(path33);
21509
+ this._pendingUnlinks.delete(path34);
20332
21510
  }
20333
21511
  }
20334
21512
  if (awf && (event === EVENTS.ADD || event === EVENTS.CHANGE) && this._readyEmitted) {
@@ -20346,16 +21524,16 @@ var FSWatcher = class extends EventEmitter2 {
20346
21524
  this.emitWithAll(event, args);
20347
21525
  }
20348
21526
  };
20349
- this._awaitWriteFinish(path33, awf.stabilityThreshold, event, awfEmit);
21527
+ this._awaitWriteFinish(path34, awf.stabilityThreshold, event, awfEmit);
20350
21528
  return this;
20351
21529
  }
20352
21530
  if (event === EVENTS.CHANGE) {
20353
- const isThrottled = !this._throttle(EVENTS.CHANGE, path33, 50);
21531
+ const isThrottled = !this._throttle(EVENTS.CHANGE, path34, 50);
20354
21532
  if (isThrottled)
20355
21533
  return this;
20356
21534
  }
20357
21535
  if (opts.alwaysStat && stats === void 0 && (event === EVENTS.ADD || event === EVENTS.ADD_DIR || event === EVENTS.CHANGE)) {
20358
- const fullPath = opts.cwd ? sp2.join(opts.cwd, path33) : path33;
21536
+ const fullPath = opts.cwd ? sp2.join(opts.cwd, path34) : path34;
20359
21537
  let stats2;
20360
21538
  try {
20361
21539
  stats2 = await stat3(fullPath);
@@ -20386,23 +21564,23 @@ var FSWatcher = class extends EventEmitter2 {
20386
21564
  * @param timeout duration of time to suppress duplicate actions
20387
21565
  * @returns tracking object or false if action should be suppressed
20388
21566
  */
20389
- _throttle(actionType, path33, timeout) {
21567
+ _throttle(actionType, path34, timeout) {
20390
21568
  if (!this._throttled.has(actionType)) {
20391
21569
  this._throttled.set(actionType, /* @__PURE__ */ new Map());
20392
21570
  }
20393
21571
  const action = this._throttled.get(actionType);
20394
21572
  if (!action)
20395
21573
  throw new Error("invalid throttle");
20396
- const actionPath = action.get(path33);
21574
+ const actionPath = action.get(path34);
20397
21575
  if (actionPath) {
20398
21576
  actionPath.count++;
20399
21577
  return false;
20400
21578
  }
20401
21579
  let timeoutObject;
20402
21580
  const clear = () => {
20403
- const item = action.get(path33);
21581
+ const item = action.get(path34);
20404
21582
  const count = item ? item.count : 0;
20405
- action.delete(path33);
21583
+ action.delete(path34);
20406
21584
  clearTimeout(timeoutObject);
20407
21585
  if (item)
20408
21586
  clearTimeout(item.timeoutObject);
@@ -20410,7 +21588,7 @@ var FSWatcher = class extends EventEmitter2 {
20410
21588
  };
20411
21589
  timeoutObject = setTimeout(clear, timeout);
20412
21590
  const thr = { timeoutObject, clear, count: 0 };
20413
- action.set(path33, thr);
21591
+ action.set(path34, thr);
20414
21592
  return thr;
20415
21593
  }
20416
21594
  _incrReadyCount() {
@@ -20424,44 +21602,44 @@ var FSWatcher = class extends EventEmitter2 {
20424
21602
  * @param event
20425
21603
  * @param awfEmit Callback to be called when ready for event to be emitted.
20426
21604
  */
20427
- _awaitWriteFinish(path33, threshold, event, awfEmit) {
21605
+ _awaitWriteFinish(path34, threshold, event, awfEmit) {
20428
21606
  const awf = this.options.awaitWriteFinish;
20429
21607
  if (typeof awf !== "object")
20430
21608
  return;
20431
21609
  const pollInterval = awf.pollInterval;
20432
21610
  let timeoutHandler;
20433
- let fullPath = path33;
20434
- if (this.options.cwd && !sp2.isAbsolute(path33)) {
20435
- 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);
20436
21614
  }
20437
21615
  const now2 = /* @__PURE__ */ new Date();
20438
21616
  const writes = this._pendingWrites;
20439
21617
  function awaitWriteFinishFn(prevStat) {
20440
21618
  statcb(fullPath, (err, curStat) => {
20441
- if (err || !writes.has(path33)) {
21619
+ if (err || !writes.has(path34)) {
20442
21620
  if (err && err.code !== "ENOENT")
20443
21621
  awfEmit(err);
20444
21622
  return;
20445
21623
  }
20446
21624
  const now3 = Number(/* @__PURE__ */ new Date());
20447
21625
  if (prevStat && curStat.size !== prevStat.size) {
20448
- writes.get(path33).lastChange = now3;
21626
+ writes.get(path34).lastChange = now3;
20449
21627
  }
20450
- const pw = writes.get(path33);
21628
+ const pw = writes.get(path34);
20451
21629
  const df = now3 - pw.lastChange;
20452
21630
  if (df >= threshold) {
20453
- writes.delete(path33);
21631
+ writes.delete(path34);
20454
21632
  awfEmit(void 0, curStat);
20455
21633
  } else {
20456
21634
  timeoutHandler = setTimeout(awaitWriteFinishFn, pollInterval, curStat);
20457
21635
  }
20458
21636
  });
20459
21637
  }
20460
- if (!writes.has(path33)) {
20461
- writes.set(path33, {
21638
+ if (!writes.has(path34)) {
21639
+ writes.set(path34, {
20462
21640
  lastChange: now2,
20463
21641
  cancelWait: () => {
20464
- writes.delete(path33);
21642
+ writes.delete(path34);
20465
21643
  clearTimeout(timeoutHandler);
20466
21644
  return event;
20467
21645
  }
@@ -20472,8 +21650,8 @@ var FSWatcher = class extends EventEmitter2 {
20472
21650
  /**
20473
21651
  * Determines whether user has asked to ignore this path.
20474
21652
  */
20475
- _isIgnored(path33, stats) {
20476
- if (this.options.atomic && DOT_RE.test(path33))
21653
+ _isIgnored(path34, stats) {
21654
+ if (this.options.atomic && DOT_RE.test(path34))
20477
21655
  return true;
20478
21656
  if (!this._userIgnored) {
20479
21657
  const { cwd } = this.options;
@@ -20483,17 +21661,17 @@ var FSWatcher = class extends EventEmitter2 {
20483
21661
  const list = [...ignoredPaths.map(normalizeIgnored(cwd)), ...ignored];
20484
21662
  this._userIgnored = anymatch(list, void 0);
20485
21663
  }
20486
- return this._userIgnored(path33, stats);
21664
+ return this._userIgnored(path34, stats);
20487
21665
  }
20488
- _isntIgnored(path33, stat5) {
20489
- return !this._isIgnored(path33, stat5);
21666
+ _isntIgnored(path34, stat5) {
21667
+ return !this._isIgnored(path34, stat5);
20490
21668
  }
20491
21669
  /**
20492
21670
  * Provides a set of common helpers and properties relating to symlink handling.
20493
21671
  * @param path file or directory pattern being watched
20494
21672
  */
20495
- _getWatchHelpers(path33) {
20496
- return new WatchHelper(path33, this.options.followSymlinks, this);
21673
+ _getWatchHelpers(path34) {
21674
+ return new WatchHelper(path34, this.options.followSymlinks, this);
20497
21675
  }
20498
21676
  // Directory helpers
20499
21677
  // -----------------
@@ -20525,63 +21703,63 @@ var FSWatcher = class extends EventEmitter2 {
20525
21703
  * @param item base path of item/directory
20526
21704
  */
20527
21705
  _remove(directory, item, isDirectory) {
20528
- const path33 = sp2.join(directory, item);
20529
- const fullPath = sp2.resolve(path33);
20530
- isDirectory = isDirectory != null ? isDirectory : this._watched.has(path33) || this._watched.has(fullPath);
20531
- 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))
20532
21710
  return;
20533
21711
  if (!isDirectory && this._watched.size === 1) {
20534
21712
  this.add(directory, item, true);
20535
21713
  }
20536
- const wp = this._getWatchedDir(path33);
21714
+ const wp = this._getWatchedDir(path34);
20537
21715
  const nestedDirectoryChildren = wp.getChildren();
20538
- nestedDirectoryChildren.forEach((nested) => this._remove(path33, nested));
21716
+ nestedDirectoryChildren.forEach((nested) => this._remove(path34, nested));
20539
21717
  const parent = this._getWatchedDir(directory);
20540
21718
  const wasTracked = parent.has(item);
20541
21719
  parent.remove(item);
20542
21720
  if (this._symlinkPaths.has(fullPath)) {
20543
21721
  this._symlinkPaths.delete(fullPath);
20544
21722
  }
20545
- let relPath = path33;
21723
+ let relPath = path34;
20546
21724
  if (this.options.cwd)
20547
- relPath = sp2.relative(this.options.cwd, path33);
21725
+ relPath = sp2.relative(this.options.cwd, path34);
20548
21726
  if (this.options.awaitWriteFinish && this._pendingWrites.has(relPath)) {
20549
21727
  const event = this._pendingWrites.get(relPath).cancelWait();
20550
21728
  if (event === EVENTS.ADD)
20551
21729
  return;
20552
21730
  }
20553
- this._watched.delete(path33);
21731
+ this._watched.delete(path34);
20554
21732
  this._watched.delete(fullPath);
20555
21733
  const eventName = isDirectory ? EVENTS.UNLINK_DIR : EVENTS.UNLINK;
20556
- if (wasTracked && !this._isIgnored(path33))
20557
- this._emit(eventName, path33);
20558
- this._closePath(path33);
21734
+ if (wasTracked && !this._isIgnored(path34))
21735
+ this._emit(eventName, path34);
21736
+ this._closePath(path34);
20559
21737
  }
20560
21738
  /**
20561
21739
  * Closes all watchers for a path
20562
21740
  */
20563
- _closePath(path33) {
20564
- this._closeFile(path33);
20565
- const dir = sp2.dirname(path33);
20566
- 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));
20567
21745
  }
20568
21746
  /**
20569
21747
  * Closes only file-specific watchers
20570
21748
  */
20571
- _closeFile(path33) {
20572
- const closers = this._closers.get(path33);
21749
+ _closeFile(path34) {
21750
+ const closers = this._closers.get(path34);
20573
21751
  if (!closers)
20574
21752
  return;
20575
21753
  closers.forEach((closer) => closer());
20576
- this._closers.delete(path33);
21754
+ this._closers.delete(path34);
20577
21755
  }
20578
- _addPathCloser(path33, closer) {
21756
+ _addPathCloser(path34, closer) {
20579
21757
  if (!closer)
20580
21758
  return;
20581
- let list = this._closers.get(path33);
21759
+ let list = this._closers.get(path34);
20582
21760
  if (!list) {
20583
21761
  list = [];
20584
- this._closers.set(path33, list);
21762
+ this._closers.set(path34, list);
20585
21763
  }
20586
21764
  list.push(closer);
20587
21765
  }
@@ -20611,11 +21789,11 @@ function watch(paths, options = {}) {
20611
21789
  var chokidar_default = { watch, FSWatcher };
20612
21790
 
20613
21791
  // src/watcher/file-watcher.ts
20614
- import * as path28 from "path";
21792
+ import * as path29 from "path";
20615
21793
 
20616
21794
  // src/watcher/native-recursive-watcher.ts
20617
21795
  import { watch as watch2 } from "fs";
20618
- import * as path26 from "path";
21796
+ import * as path27 from "path";
20619
21797
  var NativeRecursiveWatcher = class {
20620
21798
  constructor(root, onChange, options = {}) {
20621
21799
  this.root = root;
@@ -20663,9 +21841,9 @@ var NativeRecursiveWatcher = class {
20663
21841
  toAbsolutePath(filename) {
20664
21842
  if (filename == null) return null;
20665
21843
  const normalizedFilename = typeof filename === "string" ? filename : filename.toString();
20666
- const absolutePath = path26.resolve(this.root, normalizedFilename);
20667
- const relativePath = path26.relative(this.root, absolutePath);
20668
- 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);
20669
21847
  return outsideRoot ? null : absolutePath;
20670
21848
  }
20671
21849
  defaultWatchFactory = (root, listener, options) => watch2(root, options, listener);
@@ -20673,16 +21851,16 @@ var NativeRecursiveWatcher = class {
20673
21851
 
20674
21852
  // src/watcher/snapshot.ts
20675
21853
  import * as fsPromises4 from "fs/promises";
20676
- import * as path27 from "path";
21854
+ import * as path28 from "path";
20677
21855
  async function buildFileSnapshotScan(projectRoot, config, configPaths = []) {
20678
- const normalizedProjectRoot = path27.resolve(projectRoot);
21856
+ const normalizedProjectRoot = path28.resolve(projectRoot);
20679
21857
  const ignoreFilter = createIgnoreFilter(normalizedProjectRoot);
20680
21858
  const includePatterns = [...config.include, ...config.additionalInclude ?? []];
20681
21859
  const maxDepth = config.indexing?.maxDepth ?? -1;
20682
21860
  const snapshot = /* @__PURE__ */ new Map();
20683
21861
  const unreadablePrefixes = /* @__PURE__ */ new Set();
20684
21862
  const includeFile = async (filePath) => {
20685
- const normalizedPath3 = path27.resolve(filePath);
21863
+ const normalizedPath3 = path28.resolve(filePath);
20686
21864
  if (!shouldIncludeFile(normalizedPath3, normalizedProjectRoot, includePatterns, config.exclude, ignoreFilter)) return;
20687
21865
  const stat5 = await readStatIfFile(normalizedPath3, unreadablePrefixes);
20688
21866
  if (stat5) snapshot.set(normalizedPath3, { size: stat5.size, mtimeMs: stat5.mtimeMs });
@@ -20694,16 +21872,16 @@ async function buildFileSnapshotScan(projectRoot, config, configPaths = []) {
20694
21872
  } catch (error) {
20695
21873
  if (isMissingFsError(error)) return;
20696
21874
  if (isPermissionFsError(error)) {
20697
- unreadablePrefixes.add(path27.resolve(directoryPath));
21875
+ unreadablePrefixes.add(path28.resolve(directoryPath));
20698
21876
  return;
20699
21877
  }
20700
21878
  throw error;
20701
21879
  }
20702
21880
  for (const entry of entries) {
20703
- const fullPath = path27.join(directoryPath, entry.name);
20704
- const relativePath = path27.relative(normalizedProjectRoot, fullPath);
21881
+ const fullPath = path28.join(directoryPath, entry.name);
21882
+ const relativePath = path28.relative(normalizedProjectRoot, fullPath);
20705
21883
  if (entry.isDirectory()) {
20706
- if (hasFilteredPathSegment(relativePath, path27.sep) || isRestrictedDirectory(relativePath, path27.sep)) continue;
21884
+ if (hasFilteredPathSegment(relativePath, path28.sep) || isRestrictedDirectory(relativePath, path28.sep)) continue;
20707
21885
  if (ignoreFilter.ignores(relativePath)) continue;
20708
21886
  if (maxDepth === -1 || depth < maxDepth) await walk(fullPath, depth + 1);
20709
21887
  } else if (entry.isFile()) {
@@ -20716,19 +21894,19 @@ async function buildFileSnapshotScan(projectRoot, config, configPaths = []) {
20716
21894
  return { entries: snapshot, unreadablePrefixes };
20717
21895
  }
20718
21896
  async function buildFileSnapshotForPathScan(projectRoot, config, configPaths, targetPath) {
20719
- const normalizedProjectRoot = path27.resolve(projectRoot);
20720
- const normalizedTargetPath = path27.resolve(targetPath);
21897
+ const normalizedProjectRoot = path28.resolve(projectRoot);
21898
+ const normalizedTargetPath = path28.resolve(targetPath);
20721
21899
  if (!isWithinPath(normalizedProjectRoot, normalizedTargetPath)) {
20722
21900
  return { entries: /* @__PURE__ */ new Map(), unreadablePrefixes: /* @__PURE__ */ new Set() };
20723
21901
  }
20724
21902
  const ignoreFilter = createIgnoreFilter(normalizedProjectRoot);
20725
21903
  const includePatterns = [...config.include, ...config.additionalInclude ?? []];
20726
21904
  const maxDepth = config.indexing?.maxDepth ?? -1;
20727
- const explicitConfigPaths = new Set(configPaths.map((configPath) => path27.resolve(configPath)));
21905
+ const explicitConfigPaths = new Set(configPaths.map((configPath) => path28.resolve(configPath)));
20728
21906
  const snapshot = /* @__PURE__ */ new Map();
20729
21907
  const unreadablePrefixes = /* @__PURE__ */ new Set();
20730
21908
  const includeFile = async (filePath) => {
20731
- const normalizedPath3 = path27.resolve(filePath);
21909
+ const normalizedPath3 = path28.resolve(filePath);
20732
21910
  if (!explicitConfigPaths.has(normalizedPath3) && !shouldIncludeFile(
20733
21911
  normalizedPath3,
20734
21912
  normalizedProjectRoot,
@@ -20746,16 +21924,16 @@ async function buildFileSnapshotForPathScan(projectRoot, config, configPaths, ta
20746
21924
  } catch (error) {
20747
21925
  if (isMissingFsError(error)) return;
20748
21926
  if (isPermissionFsError(error)) {
20749
- unreadablePrefixes.add(path27.resolve(directoryPath));
21927
+ unreadablePrefixes.add(path28.resolve(directoryPath));
20750
21928
  return;
20751
21929
  }
20752
21930
  throw error;
20753
21931
  }
20754
21932
  for (const entry of entries) {
20755
- const fullPath = path27.join(directoryPath, entry.name);
20756
- const relativePath = path27.relative(normalizedProjectRoot, fullPath);
21933
+ const fullPath = path28.join(directoryPath, entry.name);
21934
+ const relativePath = path28.relative(normalizedProjectRoot, fullPath);
20757
21935
  if (entry.isDirectory()) {
20758
- if (hasFilteredPathSegment(relativePath, path27.sep) || isRestrictedDirectory(relativePath, path27.sep)) continue;
21936
+ if (hasFilteredPathSegment(relativePath, path28.sep) || isRestrictedDirectory(relativePath, path28.sep)) continue;
20759
21937
  if (ignoreFilter.ignores(relativePath)) continue;
20760
21938
  if (maxDepth === -1 || depth < maxDepth) await walk(fullPath, depth + 1);
20761
21939
  } else if (entry.isFile()) {
@@ -20779,7 +21957,7 @@ function completeFileSnapshot(previous, scan) {
20779
21957
  return completed;
20780
21958
  }
20781
21959
  async function includeExplicitConfigPaths(snapshot, unreadablePrefixes, configPaths) {
20782
- 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)))]) {
20783
21961
  if (snapshot.has(configPath)) continue;
20784
21962
  const stat5 = await readStatIfFile(configPath, unreadablePrefixes);
20785
21963
  if (stat5) snapshot.set(configPath, { size: stat5.size, mtimeMs: stat5.mtimeMs });
@@ -20789,12 +21967,12 @@ async function includeExplicitConfigPathsInPath(snapshot, unreadablePrefixes, co
20789
21967
  await includeExplicitConfigPaths(
20790
21968
  snapshot,
20791
21969
  unreadablePrefixes,
20792
- configPaths.filter((configPath) => isWithinPath(targetPath, path27.resolve(configPath)))
21970
+ configPaths.filter((configPath) => isWithinPath(targetPath, path28.resolve(configPath)))
20793
21971
  );
20794
21972
  }
20795
21973
  function isWithinPath(parentPath, childPath) {
20796
- const relativePath = path27.relative(parentPath, childPath);
20797
- 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);
20798
21976
  }
20799
21977
  async function readStatIfFile(filePath, unreadablePrefixes) {
20800
21978
  try {
@@ -20803,7 +21981,7 @@ async function readStatIfFile(filePath, unreadablePrefixes) {
20803
21981
  } catch (error) {
20804
21982
  if (isMissingFsError(error)) return null;
20805
21983
  if (isPermissionFsError(error)) {
20806
- unreadablePrefixes.add(path27.resolve(filePath));
21984
+ unreadablePrefixes.add(path28.resolve(filePath));
20807
21985
  return null;
20808
21986
  }
20809
21987
  throw error;
@@ -20940,8 +22118,8 @@ var FileWatcher = class {
20940
22118
  this.createWatcher();
20941
22119
  }
20942
22120
  resetReady() {
20943
- this.readyPromise = new Promise((resolve20) => {
20944
- this.resolveReady = resolve20;
22121
+ this.readyPromise = new Promise((resolve21) => {
22122
+ this.resolveReady = resolve21;
20945
22123
  });
20946
22124
  this.startupReadySignals = 1;
20947
22125
  }
@@ -20972,7 +22150,7 @@ var FileWatcher = class {
20972
22150
  const resolvedWatchTargets = watchTargets ?? this.getFullChokidarWatchTargets();
20973
22151
  const watcherOptions = {
20974
22152
  ignored: (filePath) => {
20975
- const relativePath = path28.relative(this.projectRoot, filePath);
22153
+ const relativePath = path29.relative(this.projectRoot, filePath);
20976
22154
  if (!relativePath) return false;
20977
22155
  if (this.isProjectConfigPathOrAncestor(relativePath)) {
20978
22156
  return false;
@@ -20980,10 +22158,10 @@ var FileWatcher = class {
20980
22158
  if (this.isOutsideProjectPath(relativePath)) {
20981
22159
  return true;
20982
22160
  }
20983
- if (hasFilteredPathSegment(relativePath, path28.sep)) {
22161
+ if (hasFilteredPathSegment(relativePath, path29.sep)) {
20984
22162
  return true;
20985
22163
  }
20986
- if (isRestrictedDirectory(relativePath, path28.sep)) {
22164
+ if (isRestrictedDirectory(relativePath, path29.sep)) {
20987
22165
  return true;
20988
22166
  }
20989
22167
  if (ignoreFilter.ignores(relativePath)) {
@@ -21074,13 +22252,13 @@ var FileWatcher = class {
21074
22252
  getExternalConfigWatchTargets() {
21075
22253
  return [...new Set(
21076
22254
  this.projectConfigPaths.filter((projectConfigPath) => {
21077
- const relativeConfigPath = path28.relative(this.projectRoot, projectConfigPath);
22255
+ const relativeConfigPath = path29.relative(this.projectRoot, projectConfigPath);
21078
22256
  return this.isOutsideProjectPath(relativeConfigPath);
21079
22257
  }).map((projectConfigPath) => {
21080
- if (existsSync15(projectConfigPath)) {
22258
+ if (existsSync16(projectConfigPath)) {
21081
22259
  return projectConfigPath;
21082
22260
  }
21083
- return this.getNearestExistingDirectory(path28.dirname(projectConfigPath));
22261
+ return this.getNearestExistingDirectory(path29.dirname(projectConfigPath));
21084
22262
  })
21085
22263
  )];
21086
22264
  }
@@ -21142,7 +22320,7 @@ var FileWatcher = class {
21142
22320
  }
21143
22321
  scheduleNativeReconciliation(generation, filePath) {
21144
22322
  if (!this.isCurrentNativeSetup(generation)) return;
21145
- const requiresFullReconciliation = filePath === path28.join(this.projectRoot, ".gitignore");
22323
+ const requiresFullReconciliation = filePath === path29.join(this.projectRoot, ".gitignore");
21146
22324
  const invalidatedPath = requiresFullReconciliation ? null : filePath;
21147
22325
  this.nativeInvalidatedPaths.set(invalidatedPath, invalidatedPath !== null);
21148
22326
  if (this.nativeReconcileTimer) {
@@ -21237,23 +22415,23 @@ var FileWatcher = class {
21237
22415
  this.scheduleFlush();
21238
22416
  }
21239
22417
  isProjectConfigPath(filePath) {
21240
- const relativePath = path28.relative(this.projectRoot, filePath);
21241
- const normalizedRelativePath = path28.normalize(relativePath);
22418
+ const relativePath = path29.relative(this.projectRoot, filePath);
22419
+ const normalizedRelativePath = path29.normalize(relativePath);
21242
22420
  return this.getProjectConfigRelativePaths().some((configPath) => configPath === normalizedRelativePath);
21243
22421
  }
21244
22422
  isProjectConfigPathOrAncestor(relativePath) {
21245
- const normalizedRelativePath = path28.normalize(relativePath);
22423
+ const normalizedRelativePath = path29.normalize(relativePath);
21246
22424
  return this.getProjectConfigRelativePaths().some(
21247
- (configPath) => configPath === normalizedRelativePath || configPath.startsWith(`${normalizedRelativePath}${path28.sep}`)
22425
+ (configPath) => configPath === normalizedRelativePath || configPath.startsWith(`${normalizedRelativePath}${path29.sep}`)
21248
22426
  );
21249
22427
  }
21250
22428
  isOutsideProjectPath(relativePath) {
21251
- return relativePath === ".." || relativePath.startsWith(`..${path28.sep}`) || path28.isAbsolute(relativePath);
22429
+ return relativePath === ".." || relativePath.startsWith(`..${path29.sep}`) || path29.isAbsolute(relativePath);
21252
22430
  }
21253
22431
  getNearestExistingDirectory(directoryPath) {
21254
22432
  let candidate = directoryPath;
21255
- while (!existsSync15(candidate)) {
21256
- const parent = path28.dirname(candidate);
22433
+ while (!existsSync16(candidate)) {
22434
+ const parent = path29.dirname(candidate);
21257
22435
  if (parent === candidate) break;
21258
22436
  candidate = parent;
21259
22437
  }
@@ -21261,7 +22439,7 @@ var FileWatcher = class {
21261
22439
  }
21262
22440
  getProjectConfigRelativePaths() {
21263
22441
  return this.projectConfigPaths.map(
21264
- (configPath) => path28.normalize(path28.relative(this.projectRoot, configPath))
22442
+ (configPath) => path29.normalize(path29.relative(this.projectRoot, configPath))
21265
22443
  );
21266
22444
  }
21267
22445
  getConfigPathStates() {
@@ -21319,7 +22497,7 @@ var FileWatcher = class {
21319
22497
  return;
21320
22498
  }
21321
22499
  const changes = Array.from(this.pendingChanges.entries()).map(
21322
- ([path33, type]) => ({ path: path33, type })
22500
+ ([path34, type]) => ({ path: path34, type })
21323
22501
  );
21324
22502
  this.pendingChanges.clear();
21325
22503
  try {
@@ -21365,7 +22543,7 @@ var FileWatcher = class {
21365
22543
  };
21366
22544
 
21367
22545
  // src/watcher/git-head-watcher.ts
21368
- import * as path29 from "path";
22546
+ import * as path30 from "path";
21369
22547
  var GitHeadWatcher = class {
21370
22548
  watcher = null;
21371
22549
  projectRoot;
@@ -21387,13 +22565,13 @@ var GitHeadWatcher = class {
21387
22565
  this.readyPromise = Promise.resolve();
21388
22566
  return;
21389
22567
  }
21390
- this.readyPromise = new Promise((resolve20) => {
21391
- this.resolveReady = resolve20;
22568
+ this.readyPromise = new Promise((resolve21) => {
22569
+ this.resolveReady = resolve21;
21392
22570
  });
21393
22571
  this.onBranchChange = handler;
21394
22572
  this.currentBranch = getCurrentBranch(this.projectRoot);
21395
22573
  const headPath = getHeadPath(this.projectRoot);
21396
- const refsPath = path29.join(this.projectRoot, ".git", "refs", "heads");
22574
+ const refsPath = path30.join(this.projectRoot, ".git", "refs", "heads");
21397
22575
  this.watcher = chokidar_default.watch([headPath, refsPath], {
21398
22576
  persistent: true,
21399
22577
  ignoreInitial: true,
@@ -21461,7 +22639,9 @@ var GitHeadWatcher = class {
21461
22639
  function createWatcherWithIndexer(getIndexer, projectRoot, config, host, options = {}) {
21462
22640
  const fileWatcher = new FileWatcher(projectRoot, config, host, options);
21463
22641
  const configPaths = getConfigPaths(projectRoot, host, options);
21464
- configureAutoIndex(projectRoot, host, parseConfig(config), getIndexer);
22642
+ configureAutoIndex(projectRoot, host, parseConfig(config), getIndexer, {
22643
+ synchronizeBackgroundWorker: false
22644
+ });
21465
22645
  let stopped = false;
21466
22646
  const requestReindex = () => {
21467
22647
  if (stopped) return;
@@ -21481,7 +22661,9 @@ function createWatcherWithIndexer(getIndexer, projectRoot, config, host, options
21481
22661
  const parsedConfig = options.configPath ? parseConfig(loadConfigFile(options.configPath)) : void 0;
21482
22662
  const refreshedConfig = refreshIndexerForDirectory(projectRoot, host, parsedConfig);
21483
22663
  if (refreshedConfig) {
21484
- configureAutoIndex(projectRoot, host, refreshedConfig, getIndexer);
22664
+ configureAutoIndex(projectRoot, host, refreshedConfig, getIndexer, {
22665
+ synchronizeBackgroundWorker: false
22666
+ });
21485
22667
  }
21486
22668
  }
21487
22669
  requestReindex();
@@ -21529,7 +22711,7 @@ function getConfigPaths(projectRoot, host, options) {
21529
22711
 
21530
22712
  // src/tools/visualize/activity.ts
21531
22713
  import { execFileSync } from "child_process";
21532
- import * as path30 from "path";
22714
+ import * as path31 from "path";
21533
22715
  function attachRecentActivity(data, projectRoot) {
21534
22716
  const activity = readGitActivity(projectRoot);
21535
22717
  const changes = activity.size > 0 ? buildGitChanges(data, activity, projectRoot) : buildGraphChanges(data);
@@ -21691,7 +22873,7 @@ function normalizePath4(filePath) {
21691
22873
  return filePath.replace(/\\/g, "/");
21692
22874
  }
21693
22875
  function toGitRelativePath(projectRoot, filePath) {
21694
- const relativePath = path30.isAbsolute(filePath) ? path30.relative(projectRoot, filePath) : filePath;
22876
+ const relativePath = path31.isAbsolute(filePath) ? path31.relative(projectRoot, filePath) : filePath;
21695
22877
  return normalizePath4(relativePath);
21696
22878
  }
21697
22879
 
@@ -21949,7 +23131,7 @@ render();
21949
23131
  }
21950
23132
 
21951
23133
  // src/tools/visualize/transform.ts
21952
- import * as path31 from "path";
23134
+ import * as path32 from "path";
21953
23135
 
21954
23136
  // src/tools/visualize/modules.ts
21955
23137
  var MAX_MODULES = 18;
@@ -22209,7 +23391,7 @@ function transformForVisualization(symbols, edges, options = {}) {
22209
23391
  filePath: s.filePath,
22210
23392
  kind: s.kind,
22211
23393
  line: s.startLine,
22212
- directory: path31.dirname(s.filePath),
23394
+ directory: path32.dirname(s.filePath),
22213
23395
  moduleId: "",
22214
23396
  moduleLabel: ""
22215
23397
  }));
@@ -22237,9 +23419,9 @@ function parseArgs(argv) {
22237
23419
  let host = "opencode";
22238
23420
  for (let i = 2; i < argv.length; i++) {
22239
23421
  if (argv[i] === "--project" && argv[i + 1]) {
22240
- project = path32.resolve(argv[++i]);
23422
+ project = path33.resolve(argv[++i]);
22241
23423
  } else if (argv[i] === "--config" && argv[i + 1]) {
22242
- config = path32.resolve(argv[++i]);
23424
+ config = path33.resolve(argv[++i]);
22243
23425
  } else if (argv[i] === "--host" && argv[i + 1]) {
22244
23426
  host = parseHostMode(argv[++i]);
22245
23427
  } else if (argv[i] === "--host") {
@@ -22254,6 +23436,7 @@ function parseIndexArgs(argv, cwd) {
22254
23436
  let config;
22255
23437
  let force = false;
22256
23438
  let estimateOnly = false;
23439
+ let dryRun = false;
22257
23440
  let verbose = false;
22258
23441
  for (let i = 0; i < argv.length; i += 1) {
22259
23442
  const arg = argv[i];
@@ -22266,7 +23449,7 @@ function parseIndexArgs(argv, cwd) {
22266
23449
  if (!arg.startsWith("--project=")) {
22267
23450
  i += 1;
22268
23451
  }
22269
- project = path32.resolve(cwd, value);
23452
+ project = path33.resolve(cwd, value);
22270
23453
  continue;
22271
23454
  }
22272
23455
  if (arg === "--config" || arg.startsWith("--config=")) {
@@ -22277,7 +23460,7 @@ function parseIndexArgs(argv, cwd) {
22277
23460
  if (!arg.startsWith("--config=")) {
22278
23461
  i += 1;
22279
23462
  }
22280
- config = path32.resolve(cwd, value);
23463
+ config = path33.resolve(cwd, value);
22281
23464
  continue;
22282
23465
  }
22283
23466
  if (arg === "--host" || arg.startsWith("--host=")) {
@@ -22291,13 +23474,16 @@ function parseIndexArgs(argv, cwd) {
22291
23474
  host = parseHostMode(value);
22292
23475
  continue;
22293
23476
  }
22294
- if (arg === "--force" || arg === "--estimate-only" || arg === "--verbose") {
23477
+ if (arg === "--force" || arg === "--estimate-only" || arg === "--dry-run" || arg === "--verbose") {
22295
23478
  if (arg === "--force") {
22296
23479
  force = true;
22297
23480
  }
22298
23481
  if (arg === "--estimate-only") {
22299
23482
  estimateOnly = true;
22300
23483
  }
23484
+ if (arg === "--dry-run") {
23485
+ dryRun = true;
23486
+ }
22301
23487
  if (arg === "--verbose") {
22302
23488
  verbose = true;
22303
23489
  }
@@ -22308,7 +23494,7 @@ function parseIndexArgs(argv, cwd) {
22308
23494
  }
22309
23495
  throw new Error(`Unknown index option: ${arg}`);
22310
23496
  }
22311
- return { project, host, config, force, estimateOnly, verbose };
23497
+ return { project, host, config, force, estimateOnly, dryRun, verbose };
22312
23498
  }
22313
23499
  function loadCliRawConfig(args) {
22314
23500
  return args.config ? loadConfigFile(args.config) : loadMergedConfig(args.project, args.host);
@@ -22325,6 +23511,7 @@ Options:
22325
23511
  --config <path> Explicit JSON config path
22326
23512
  --force Rebuild index even if already up to date
22327
23513
  --estimate-only Estimate indexing cost only
23514
+ --dry-run Parse only; report the exact embedding token total without indexing
22328
23515
  --verbose Include detailed final index statistics
22329
23516
  --help Show this message
22330
23517
 
@@ -22333,7 +23520,7 @@ Progress and diagnostics are written to stderr. Final index output is written to
22333
23520
  );
22334
23521
  }
22335
23522
  function isCliEntrypoint(moduleUrl, argvPath) {
22336
- return argvPath !== void 0 && realpathSync6(fileURLToPath2(moduleUrl)) === realpathSync6(argvPath);
23523
+ return argvPath !== void 0 && realpathSync7(fileURLToPath2(moduleUrl)) === realpathSync7(argvPath);
22337
23524
  }
22338
23525
  function parseVisualizeArgs(argv, cwd) {
22339
23526
  let project = cwd;
@@ -22343,7 +23530,7 @@ function parseVisualizeArgs(argv, cwd) {
22343
23530
  for (let i = 0; i < argv.length; i++) {
22344
23531
  const arg = argv[i];
22345
23532
  if (arg === "--project" && argv[i + 1]) {
22346
- project = path32.resolve(argv[++i]);
23533
+ project = path33.resolve(argv[++i]);
22347
23534
  } else if (arg === "--max" && argv[i + 1]) {
22348
23535
  maxNodes = Number(argv[++i]);
22349
23536
  } else if (arg.startsWith("--max=") || arg.startsWith("max=")) {
@@ -22380,8 +23567,8 @@ async function handleVisualizeCommand(argv, cwd) {
22380
23567
  console.error("No connected symbols found. Retry with: npm run visualize -- orphans");
22381
23568
  return 1;
22382
23569
  }
22383
- const outputPath = path32.join(os8.tmpdir(), `call-graph-${Date.now()}.html`);
22384
- 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");
22385
23572
  console.log(`Temporal call graph visualization generated: ${outputPath}`);
22386
23573
  console.log(`Nodes: ${vizData.nodes.length} | Edges: ${vizData.edges.length}`);
22387
23574
  console.log(`Recent change lenses: ${vizData.changes?.length ?? 0}`);
@@ -22412,7 +23599,6 @@ async function runMcpCli(argv) {
22412
23599
  const config = parseConfig(rawConfig);
22413
23600
  const server = createMcpServer(args.project, config, args.host);
22414
23601
  const transport = new StdioServerTransport();
22415
- let watcher = null;
22416
23602
  let shutdownPromise;
22417
23603
  const onServerClose = server.server.onclose;
22418
23604
  const shutdown = () => {
@@ -22426,16 +23612,19 @@ async function runMcpCli(argv) {
22426
23612
  shutdownPromise = (async () => {
22427
23613
  let exitCode = 0;
22428
23614
  try {
22429
- await watcher?.stop();
22430
- } catch (error) {
22431
- exitCode = 1;
22432
- console.error("Failed to stop MCP file watcher cleanly:", error);
22433
- }
22434
- try {
22435
- await stopAutoIndex(args.project, args.host);
23615
+ await stopBackgroundWorker(args.project, args.host);
22436
23616
  } catch (error) {
22437
23617
  exitCode = 1;
22438
- 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
+ }
22439
23628
  }
22440
23629
  try {
22441
23630
  await server.close();
@@ -22464,19 +23653,25 @@ async function runMcpCli(argv) {
22464
23653
  process.once("SIGHUP", requestShutdown);
22465
23654
  process.once("SIGTERM", requestShutdown);
22466
23655
  }
22467
- await server.connect(transport);
22468
- if (shutdownPromise) return;
22469
23656
  const isHomeDir = isHomeDirectory(args.project);
22470
23657
  const isValidProject = !isHomeDir && (!config.indexing.requireProjectMarker || hasProjectMarker(args.project));
22471
- if (config.indexing.watchFiles && isValidProject) {
22472
- watcher = createWatcherWithIndexer(
22473
- () => getIndexerForProject(args.project, args.host),
22474
- args.project,
22475
- config,
22476
- args.host,
22477
- args.config ? { configPath: args.config } : {}
22478
- );
22479
- }
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;
22480
23675
  }
22481
23676
  function printIndexProgress(onProgress, title, metadata) {
22482
23677
  const details = Object.entries(metadata).filter(([, value]) => value !== void 0 && value !== null).map(([key, value]) => `${key}=${isSensitiveKey(key) ? "[REDACTED]" : String(value)}`).join(" ");
@@ -22520,6 +23715,7 @@ async function handleIndexCommand(argv, cwd, deps = {}) {
22520
23715
  const indexArgs = {
22521
23716
  force: parsedArgs.force,
22522
23717
  estimateOnly: parsedArgs.estimateOnly,
23718
+ dryRun: parsedArgs.dryRun,
22523
23719
  verbose: parsedArgs.verbose
22524
23720
  };
22525
23721
  const result = await runIndex(parsedArgs.project, parsedArgs.host, indexArgs, (title, metadata) => {