opencode-codebase-index 0.22.5 → 0.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.cjs CHANGED
@@ -496,7 +496,7 @@ var require_ignore = __commonJS({
496
496
  // path matching.
497
497
  // - check `string` either `MODE_IGNORE` or `MODE_CHECK_IGNORE`
498
498
  // @returns {TestResult} true if a file is ignored
499
- test(path31, checkUnignored, mode) {
499
+ test(path33, checkUnignored, mode) {
500
500
  let ignored = false;
501
501
  let unignored = false;
502
502
  let matchedRule;
@@ -505,7 +505,7 @@ var require_ignore = __commonJS({
505
505
  if (unignored === negative && ignored !== unignored || negative && !ignored && !unignored && !checkUnignored) {
506
506
  return;
507
507
  }
508
- const matched = rule[mode].test(path31);
508
+ const matched = rule[mode].test(path33);
509
509
  if (!matched) {
510
510
  return;
511
511
  }
@@ -526,17 +526,17 @@ var require_ignore = __commonJS({
526
526
  var throwError = (message, Ctor) => {
527
527
  throw new Ctor(message);
528
528
  };
529
- var checkPath = (path31, originalPath, doThrow) => {
530
- if (!isString(path31)) {
529
+ var checkPath = (path33, originalPath, doThrow) => {
530
+ if (!isString(path33)) {
531
531
  return doThrow(
532
532
  `path must be a string, but got \`${originalPath}\``,
533
533
  TypeError
534
534
  );
535
535
  }
536
- if (!path31) {
536
+ if (!path33) {
537
537
  return doThrow(`path must not be empty`, TypeError);
538
538
  }
539
- if (checkPath.isNotRelative(path31)) {
539
+ if (checkPath.isNotRelative(path33)) {
540
540
  const r = "`path.relative()`d";
541
541
  return doThrow(
542
542
  `path should be a ${r} string, but got "${originalPath}"`,
@@ -545,7 +545,7 @@ var require_ignore = __commonJS({
545
545
  }
546
546
  return true;
547
547
  };
548
- var isNotRelative = (path31) => REGEX_TEST_INVALID_PATH.test(path31);
548
+ var isNotRelative = (path33) => REGEX_TEST_INVALID_PATH.test(path33);
549
549
  checkPath.isNotRelative = isNotRelative;
550
550
  checkPath.convert = (p) => p;
551
551
  var Ignore2 = class {
@@ -575,19 +575,19 @@ var require_ignore = __commonJS({
575
575
  }
576
576
  // @returns {TestResult}
577
577
  _test(originalPath, cache, checkUnignored, slices) {
578
- const path31 = originalPath && checkPath.convert(originalPath);
578
+ const path33 = originalPath && checkPath.convert(originalPath);
579
579
  checkPath(
580
- path31,
580
+ path33,
581
581
  originalPath,
582
582
  this._strictPathCheck ? throwError : RETURN_FALSE
583
583
  );
584
- return this._t(path31, cache, checkUnignored, slices);
584
+ return this._t(path33, cache, checkUnignored, slices);
585
585
  }
586
- checkIgnore(path31) {
587
- if (!REGEX_TEST_TRAILING_SLASH.test(path31)) {
588
- return this.test(path31);
586
+ checkIgnore(path33) {
587
+ if (!REGEX_TEST_TRAILING_SLASH.test(path33)) {
588
+ return this.test(path33);
589
589
  }
590
- const slices = path31.split(SLASH2).filter(Boolean);
590
+ const slices = path33.split(SLASH2).filter(Boolean);
591
591
  slices.pop();
592
592
  if (slices.length) {
593
593
  const parent = this._t(
@@ -600,18 +600,18 @@ var require_ignore = __commonJS({
600
600
  return parent;
601
601
  }
602
602
  }
603
- return this._rules.test(path31, false, MODE_CHECK_IGNORE);
603
+ return this._rules.test(path33, false, MODE_CHECK_IGNORE);
604
604
  }
605
- _t(path31, cache, checkUnignored, slices) {
606
- if (path31 in cache) {
607
- return cache[path31];
605
+ _t(path33, cache, checkUnignored, slices) {
606
+ if (path33 in cache) {
607
+ return cache[path33];
608
608
  }
609
609
  if (!slices) {
610
- slices = path31.split(SLASH2).filter(Boolean);
610
+ slices = path33.split(SLASH2).filter(Boolean);
611
611
  }
612
612
  slices.pop();
613
613
  if (!slices.length) {
614
- return cache[path31] = this._rules.test(path31, checkUnignored, MODE_IGNORE);
614
+ return cache[path33] = this._rules.test(path33, checkUnignored, MODE_IGNORE);
615
615
  }
616
616
  const parent = this._t(
617
617
  slices.join(SLASH2) + SLASH2,
@@ -619,29 +619,29 @@ var require_ignore = __commonJS({
619
619
  checkUnignored,
620
620
  slices
621
621
  );
622
- return cache[path31] = parent.ignored ? parent : this._rules.test(path31, checkUnignored, MODE_IGNORE);
622
+ return cache[path33] = parent.ignored ? parent : this._rules.test(path33, checkUnignored, MODE_IGNORE);
623
623
  }
624
- ignores(path31) {
625
- return this._test(path31, this._ignoreCache, false).ignored;
624
+ ignores(path33) {
625
+ return this._test(path33, this._ignoreCache, false).ignored;
626
626
  }
627
627
  createFilter() {
628
- return (path31) => !this.ignores(path31);
628
+ return (path33) => !this.ignores(path33);
629
629
  }
630
630
  filter(paths) {
631
631
  return makeArray(paths).filter(this.createFilter());
632
632
  }
633
633
  // @returns {TestResult}
634
- test(path31) {
635
- return this._test(path31, this._testCache, true);
634
+ test(path33) {
635
+ return this._test(path33, this._testCache, true);
636
636
  }
637
637
  };
638
638
  var factory = (options) => new Ignore2(options);
639
- var isPathValid = (path31) => checkPath(path31 && checkPath.convert(path31), path31, RETURN_FALSE);
639
+ var isPathValid = (path33) => checkPath(path33 && checkPath.convert(path33), path33, RETURN_FALSE);
640
640
  var setupWindows = () => {
641
641
  const makePosix = (str) => /^\\\\\?\\/.test(str) || /["<>|\u0000-\u001F]+/u.test(str) ? str : str.replace(/\\/g, "/");
642
642
  checkPath.convert = makePosix;
643
643
  const REGEX_TEST_WINDOWS_PATH_ABSOLUTE = /^[a-z]:\//i;
644
- checkPath.isNotRelative = (path31) => REGEX_TEST_WINDOWS_PATH_ABSOLUTE.test(path31) || isNotRelative(path31);
644
+ checkPath.isNotRelative = (path33) => REGEX_TEST_WINDOWS_PATH_ABSOLUTE.test(path33) || isNotRelative(path33);
645
645
  };
646
646
  if (
647
647
  // Detect `process` so that it can run in browsers.
@@ -669,7 +669,7 @@ module.exports = __toCommonJS(cli_exports);
669
669
  var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
670
670
  var import_fs20 = require("fs");
671
671
  var os8 = __toESM(require("os"), 1);
672
- var path30 = __toESM(require("path"), 1);
672
+ var path32 = __toESM(require("path"), 1);
673
673
  var import_url = require("url");
674
674
 
675
675
  // src/config/constants.ts
@@ -1179,9 +1179,9 @@ var import_fs = require("fs");
1179
1179
  var path = __toESM(require("path"), 1);
1180
1180
 
1181
1181
  // src/eval/report-formatters.ts
1182
- function assertFiniteNumber(value, path31) {
1182
+ function assertFiniteNumber(value, path33) {
1183
1183
  if (typeof value !== "number" || Number.isNaN(value) || !Number.isFinite(value)) {
1184
- throw new Error(`${path31} must be a finite number`);
1184
+ throw new Error(`${path33} must be a finite number`);
1185
1185
  }
1186
1186
  return value;
1187
1187
  }
@@ -1453,7 +1453,7 @@ function pTimeout(promise, options) {
1453
1453
  } = options;
1454
1454
  let timer;
1455
1455
  let abortHandler;
1456
- const wrappedPromise = new Promise((resolve18, reject) => {
1456
+ const wrappedPromise = new Promise((resolve20, reject) => {
1457
1457
  if (typeof milliseconds !== "number" || Math.sign(milliseconds) !== 1) {
1458
1458
  throw new TypeError(`Expected \`milliseconds\` to be a positive number, got \`${milliseconds}\``);
1459
1459
  }
@@ -1467,7 +1467,7 @@ function pTimeout(promise, options) {
1467
1467
  };
1468
1468
  signal.addEventListener("abort", abortHandler, { once: true });
1469
1469
  }
1470
- promise.then(resolve18, reject);
1470
+ promise.then(resolve20, reject);
1471
1471
  if (milliseconds === Number.POSITIVE_INFINITY) {
1472
1472
  return;
1473
1473
  }
@@ -1475,7 +1475,7 @@ function pTimeout(promise, options) {
1475
1475
  timer = customTimers.setTimeout.call(void 0, () => {
1476
1476
  if (fallback) {
1477
1477
  try {
1478
- resolve18(fallback());
1478
+ resolve20(fallback());
1479
1479
  } catch (error) {
1480
1480
  reject(error);
1481
1481
  }
@@ -1485,7 +1485,7 @@ function pTimeout(promise, options) {
1485
1485
  promise.cancel();
1486
1486
  }
1487
1487
  if (message === false) {
1488
- resolve18();
1488
+ resolve20();
1489
1489
  } else if (message instanceof Error) {
1490
1490
  reject(message);
1491
1491
  } else {
@@ -1887,7 +1887,7 @@ var PQueue = class extends import_index.default {
1887
1887
  // Assign unique ID if not provided
1888
1888
  id: options.id ?? (this.#idAssigner++).toString()
1889
1889
  };
1890
- return new Promise((resolve18, reject) => {
1890
+ return new Promise((resolve20, reject) => {
1891
1891
  const taskSymbol = /* @__PURE__ */ Symbol(`task-${options.id}`);
1892
1892
  let cleanupQueueAbortHandler = () => void 0;
1893
1893
  const run = async () => {
@@ -1927,7 +1927,7 @@ var PQueue = class extends import_index.default {
1927
1927
  })]);
1928
1928
  }
1929
1929
  const result = await operation;
1930
- resolve18(result);
1930
+ resolve20(result);
1931
1931
  this.emit("completed", result);
1932
1932
  } catch (error) {
1933
1933
  reject(error);
@@ -2115,13 +2115,13 @@ var PQueue = class extends import_index.default {
2115
2115
  });
2116
2116
  }
2117
2117
  async #onEvent(event, filter) {
2118
- return new Promise((resolve18) => {
2118
+ return new Promise((resolve20) => {
2119
2119
  const listener = () => {
2120
2120
  if (filter && !filter()) {
2121
2121
  return;
2122
2122
  }
2123
2123
  this.off(event, listener);
2124
- resolve18();
2124
+ resolve20();
2125
2125
  };
2126
2126
  this.on(event, listener);
2127
2127
  });
@@ -2407,7 +2407,7 @@ async function onAttemptFailure({ error, attemptNumber, retriesConsumed, startTi
2407
2407
  const finalDelay = Math.min(delayTime, remainingTime);
2408
2408
  options.signal?.throwIfAborted();
2409
2409
  if (finalDelay > 0) {
2410
- await new Promise((resolve18, reject) => {
2410
+ await new Promise((resolve20, reject) => {
2411
2411
  const onAbort = () => {
2412
2412
  clearTimeout(timeoutToken);
2413
2413
  options.signal?.removeEventListener("abort", onAbort);
@@ -2415,7 +2415,7 @@ async function onAttemptFailure({ error, attemptNumber, retriesConsumed, startTi
2415
2415
  };
2416
2416
  const timeoutToken = setTimeout(() => {
2417
2417
  options.signal?.removeEventListener("abort", onAbort);
2418
- resolve18();
2418
+ resolve20();
2419
2419
  }, finalDelay);
2420
2420
  if (options.unref) {
2421
2421
  timeoutToken.unref?.();
@@ -3221,85 +3221,6 @@ function createEmbeddingProvider(configuredProviderInfo) {
3221
3221
  }
3222
3222
  }
3223
3223
 
3224
- // src/rerank/index.ts
3225
- function createReranker(config) {
3226
- if (!config.enabled) {
3227
- return new NoOpReranker();
3228
- }
3229
- return new SiliconFlowReranker(config);
3230
- }
3231
- var NoOpReranker = class {
3232
- isAvailable() {
3233
- return false;
3234
- }
3235
- async rerank(_query, documents, _topN) {
3236
- return {
3237
- results: documents.map((_, index) => ({ index, relevanceScore: 0 }))
3238
- };
3239
- }
3240
- };
3241
- var SiliconFlowReranker = class {
3242
- config;
3243
- constructor(config) {
3244
- this.config = config;
3245
- }
3246
- isAvailable() {
3247
- return this.config.enabled && !!this.config.baseUrl && !!this.config.model;
3248
- }
3249
- async rerank(query, documents, topN) {
3250
- if (documents.length === 0) {
3251
- return { results: [] };
3252
- }
3253
- const headers = {
3254
- "Content-Type": "application/json"
3255
- };
3256
- if (this.config.apiKey) {
3257
- headers["Authorization"] = `Bearer ${this.config.apiKey}`;
3258
- }
3259
- const baseUrl = this.config.baseUrl;
3260
- if (!baseUrl) {
3261
- throw new Error("Reranker baseUrl is required. Configure reranker.baseUrl in your codebase-index.json.");
3262
- }
3263
- const timeoutMs = this.config.timeoutMs ?? 3e4;
3264
- const controller = new AbortController();
3265
- const timeout = setTimeout(() => controller.abort(), timeoutMs);
3266
- try {
3267
- const response = await fetch(`${baseUrl}/rerank`, {
3268
- method: "POST",
3269
- headers,
3270
- body: JSON.stringify({
3271
- model: this.config.model,
3272
- query,
3273
- documents,
3274
- top_n: topN ?? this.config.topN ?? 20,
3275
- return_documents: false
3276
- }),
3277
- signal: controller.signal
3278
- });
3279
- clearTimeout(timeout);
3280
- if (!response.ok) {
3281
- const errorText = await response.text();
3282
- throw new Error(`Rerank API error: ${response.status} - ${errorText}`);
3283
- }
3284
- const data = await response.json();
3285
- return {
3286
- results: data.results.map((r) => ({
3287
- index: r.index,
3288
- relevanceScore: r.relevance_score,
3289
- document: r.document?.text
3290
- })),
3291
- tokensUsed: data.meta?.tokens?.input_tokens
3292
- };
3293
- } catch (error) {
3294
- clearTimeout(timeout);
3295
- if (error instanceof Error && error.name === "AbortError") {
3296
- throw new Error(`Rerank API request timed out after ${timeoutMs}ms`);
3297
- }
3298
- throw error;
3299
- }
3300
- }
3301
- };
3302
-
3303
3224
  // src/utils/files.ts
3304
3225
  var import_ignore = __toESM(require_ignore(), 1);
3305
3226
  var import_fs3 = require("fs");
@@ -3461,8 +3382,8 @@ async function* walkDirectory(dir, projectRoot, includePatterns, excludePatterns
3461
3382
  if (entry.isDirectory()) {
3462
3383
  subdirs.push({ fullPath, relativePath });
3463
3384
  } else if (entry.isFile()) {
3464
- const stat4 = await import_fs3.promises.stat(fullPath);
3465
- if (stat4.size > maxFileSize) {
3385
+ const stat5 = await import_fs3.promises.stat(fullPath);
3386
+ if (stat5.size > maxFileSize) {
3466
3387
  skipped.push({ path: relativePath, reason: "too_large" });
3467
3388
  continue;
3468
3389
  }
@@ -3480,7 +3401,7 @@ async function* walkDirectory(dir, projectRoot, includePatterns, excludePatterns
3480
3401
  }
3481
3402
  }
3482
3403
  if (matched) {
3483
- filesInDir.push({ path: fullPath, size: stat4.size });
3404
+ filesInDir.push({ path: fullPath, size: stat5.size });
3484
3405
  }
3485
3406
  }
3486
3407
  }
@@ -3537,8 +3458,8 @@ async function collectFiles(projectRoot, includePatterns, excludePatterns, maxFi
3537
3458
  }
3538
3459
  for (const resolvedKbRoot of normalizedRoots) {
3539
3460
  try {
3540
- const stat4 = await import_fs3.promises.stat(resolvedKbRoot);
3541
- if (!stat4.isDirectory()) {
3461
+ const stat5 = await import_fs3.promises.stat(resolvedKbRoot);
3462
+ if (!stat5.isDirectory()) {
3542
3463
  skipped.push({ path: resolvedKbRoot, reason: "excluded" });
3543
3464
  continue;
3544
3465
  }
@@ -4865,11 +4786,11 @@ function resolveGitDir(repoRoot) {
4865
4786
  return null;
4866
4787
  }
4867
4788
  try {
4868
- const stat4 = (0, import_fs5.statSync)(gitPath);
4869
- if (stat4.isDirectory()) {
4789
+ const stat5 = (0, import_fs5.statSync)(gitPath);
4790
+ if (stat5.isDirectory()) {
4870
4791
  return gitPath;
4871
4792
  }
4872
- if (stat4.isFile()) {
4793
+ if (stat5.isFile()) {
4873
4794
  const content = (0, import_fs5.readFileSync)(gitPath, "utf-8").trim();
4874
4795
  const match = content.match(/^gitdir:\s*(.+)$/);
4875
4796
  if (match) {
@@ -5374,8 +5295,8 @@ async function isWorktreeRegistered(projectRoot, worktreePath) {
5374
5295
  return false;
5375
5296
  }
5376
5297
  function isPathWithinRoot(filePath, rootPath) {
5377
- const relative12 = path9.relative(path9.resolve(rootPath), path9.resolve(filePath));
5378
- return relative12 === "" || !relative12.startsWith(`..${path9.sep}`) && relative12 !== ".." && !path9.isAbsolute(relative12);
5298
+ const relative14 = path9.relative(path9.resolve(rootPath), path9.resolve(filePath));
5299
+ return relative14 === "" || !relative14.startsWith(`..${path9.sep}`) && relative14 !== ".." && !path9.isAbsolute(relative14);
5379
5300
  }
5380
5301
  async function pruneExactMissingWorktreeRegistration(projectRoot, worktreePath) {
5381
5302
  if (await pathExists(worktreePath)) return false;
@@ -5933,11 +5854,11 @@ function normalizeFiles(rawFiles, projectRoot) {
5933
5854
  for (const raw of rawFiles) {
5934
5855
  if (raw.length === 0) continue;
5935
5856
  const absolute = path11.resolve(root, raw);
5936
- const relative12 = path11.relative(root, absolute);
5937
- if (path11.isAbsolute(raw) || relative12 === ".." || relative12.startsWith(`..${path11.sep}`) || path11.isAbsolute(relative12)) {
5857
+ const relative14 = path11.relative(root, absolute);
5858
+ if (path11.isAbsolute(raw) || relative14 === ".." || relative14.startsWith(`..${path11.sep}`) || path11.isAbsolute(relative14)) {
5938
5859
  throw new Error(`Changed file escapes the project root: ${JSON.stringify(raw)}`);
5939
5860
  }
5940
- const cleaned = relative12.startsWith(`.${path11.sep}`) ? relative12.slice(2) : relative12;
5861
+ const cleaned = relative14.startsWith(`.${path11.sep}`) ? relative14.slice(2) : relative14;
5941
5862
  if (!seen.has(cleaned)) {
5942
5863
  seen.add(cleaned);
5943
5864
  result.push(cleaned);
@@ -6274,7 +6195,7 @@ function analyzeQueryIntent(query) {
6274
6195
  }
6275
6196
  function isTestPath(filePath) {
6276
6197
  const normalized = normalizePath(filePath);
6277
- return /(?:^|\/)(?:test|tests|__tests__|spec|specs)(?:\/|$)/u.test(normalized) || /\.(?:test|spec)\.[^/]+$/u.test(normalized);
6198
+ return /(?:^|\/)(?:test|tests|__tests__|spec|specs)(?:\/|$)/u.test(normalized) || /(?:\.(?:test|spec)|_(?:test|spec))\.[^/]+$/u.test(normalized) || /(?:^|\/)(?:test|spec)_[^/]+\.[^/]+$/u.test(normalized);
6278
6199
  }
6279
6200
  function isFixturePath(filePath) {
6280
6201
  const normalized = normalizePath(filePath);
@@ -6681,7 +6602,8 @@ function rankHybridResults(query, semanticResults, keywordResults, options) {
6681
6602
  return cached;
6682
6603
  }
6683
6604
  }
6684
- const overfetchLimit = Math.max(options.limit * 4, options.limit);
6605
+ const overfetchFactor = prioritizeSourcePaths ? 12 : 4;
6606
+ const overfetchLimit = Math.max(options.limit * overfetchFactor, options.limit);
6685
6607
  const fused = options.fusionStrategy === "rrf" ? fuseResultsRrf(semanticResults, keywordResults, options.rrfK, overfetchLimit) : fuseResultsWeighted(semanticResults, keywordResults, options.hybridWeight, overfetchLimit);
6686
6608
  const rerankPoolLimit = Math.max(overfetchLimit, options.rerankTopN * 3, options.limit * 6);
6687
6609
  const rerankPool = fused.slice(0, rerankPoolLimit);
@@ -8583,7 +8505,6 @@ var Indexer = class _Indexer {
8583
8505
  database = null;
8584
8506
  provider = null;
8585
8507
  configuredProviderInfo = null;
8586
- reranker = null;
8587
8508
  fileHashCache = /* @__PURE__ */ new Map();
8588
8509
  fileHashCachePath = "";
8589
8510
  failedBatchesPath = "";
@@ -8743,7 +8664,6 @@ var Indexer = class _Indexer {
8743
8664
  this.database = null;
8744
8665
  this.provider = null;
8745
8666
  this.configuredProviderInfo = null;
8746
- this.reranker = null;
8747
8667
  this.indexCompatibility = null;
8748
8668
  this.initializationMode = "none";
8749
8669
  this.readIssues = [];
@@ -9473,7 +9393,7 @@ var Indexer = class _Indexer {
9473
9393
  await options.queue.onSizeLessThan(Math.max(1, options.providerRateLimits.concurrency));
9474
9394
  const task = options.queue.add(async () => {
9475
9395
  if (options.rateLimitState.backoffMs > 0) {
9476
- await new Promise((resolve18) => setTimeout(resolve18, options.rateLimitState.backoffMs));
9396
+ await new Promise((resolve20) => setTimeout(resolve20, options.rateLimitState.backoffMs));
9477
9397
  }
9478
9398
  try {
9479
9399
  const embeddingResult = await pRetry(
@@ -10040,15 +9960,6 @@ var Indexer = class _Indexer {
10040
9960
  rerankerEnabled: this.config.reranker?.enabled ?? false
10041
9961
  });
10042
9962
  this.provider = createEmbeddingProvider(this.configuredProviderInfo);
10043
- if (this.config.reranker?.enabled) {
10044
- this.reranker = createReranker(this.config.reranker);
10045
- if (this.reranker.isAvailable()) {
10046
- this.logger.info("Reranker initialized", {
10047
- model: this.config.reranker.model,
10048
- baseUrl: this.config.reranker.baseUrl
10049
- });
10050
- }
10051
- }
10052
9963
  const dimensions = this.configuredProviderInfo.modelInfo.dimensions;
10053
9964
  const storePath = path15.join(this.indexPath, "vectors");
10054
9965
  const vectorMetadataPath = `${storePath}.meta.json`;
@@ -11453,6 +11364,7 @@ var Indexer = class _Indexer {
11453
11364
  const filterByBranch = options?.filterByBranch ?? true;
11454
11365
  const sourceIntent = options?.definitionIntent === true || classifyQueryIntentRaw(query) === "source";
11455
11366
  const identifierHints = extractIdentifierHints(query);
11367
+ const candidateLimit = maxResults * (sourceIntent ? 12 : 4);
11456
11368
  this.logger.search("debug", "Starting search", {
11457
11369
  query,
11458
11370
  maxResults,
@@ -11489,7 +11401,7 @@ var Indexer = class _Indexer {
11489
11401
  const semanticCandidates = embedding ? this.searchSemanticCandidates(
11490
11402
  store,
11491
11403
  embedding,
11492
- maxResults * 4,
11404
+ candidateLimit,
11493
11405
  branchChunkIds,
11494
11406
  shouldPrefilterByBranch
11495
11407
  ) : [];
@@ -11497,7 +11409,7 @@ var Indexer = class _Indexer {
11497
11409
  const keywordStartTime = import_perf_hooks.performance.now();
11498
11410
  const keywordCandidates = await this.keywordSearch(
11499
11411
  query,
11500
- maxResults * 4,
11412
+ candidateLimit,
11501
11413
  store,
11502
11414
  invertedIndex,
11503
11415
  branchChunkIds,
@@ -12255,9 +12167,9 @@ var Indexer = class _Indexer {
12255
12167
  this.requireReadableComponents(readIssues, "database");
12256
12168
  let shortest = [];
12257
12169
  for (const branchKey of this.getBranchCatalogKeys()) {
12258
- const path31 = database.findShortestPath(fromName, toName, branchKey, maxDepth);
12259
- if (path31.length > 0 && (shortest.length === 0 || path31.length < shortest.length)) {
12260
- shortest = path31;
12170
+ const path33 = database.findShortestPath(fromName, toName, branchKey, maxDepth);
12171
+ if (path33.length > 0 && (shortest.length === 0 || path33.length < shortest.length)) {
12172
+ shortest = path33;
12261
12173
  }
12262
12174
  }
12263
12175
  return shortest.map((hop) => this.resolveFilePathRecord(hop));
@@ -12305,13 +12217,13 @@ var Indexer = class _Indexer {
12305
12217
  }
12306
12218
  }
12307
12219
  if (!found) continue;
12308
- const path31 = [];
12220
+ const path33 = [];
12309
12221
  let currentSymbolId = toSymbolId;
12310
12222
  while (true) {
12311
12223
  const symbol = symbolsById.get(currentSymbolId);
12312
12224
  if (!symbol) break;
12313
12225
  const parent = parentBySymbolId.get(currentSymbolId);
12314
- path31.push({
12226
+ path33.push({
12315
12227
  symbolId: symbol.id,
12316
12228
  symbolName: symbol.name,
12317
12229
  filePath: symbol.filePath,
@@ -12321,9 +12233,9 @@ var Indexer = class _Indexer {
12321
12233
  if (!parent) break;
12322
12234
  currentSymbolId = parent.parentId;
12323
12235
  }
12324
- path31.reverse();
12325
- if (path31.length > 0 && (shortest.length === 0 || path31.length < shortest.length)) {
12326
- shortest = path31;
12236
+ path33.reverse();
12237
+ if (path33.length > 0 && (shortest.length === 0 || path33.length < shortest.length)) {
12238
+ shortest = path33;
12327
12239
  }
12328
12240
  }
12329
12241
  return shortest.map((hop) => this.resolveFilePathRecord(hop));
@@ -12659,7 +12571,6 @@ var Indexer = class _Indexer {
12659
12571
  this.store = null;
12660
12572
  this.invertedIndex = null;
12661
12573
  this.provider = null;
12662
- this.reranker = null;
12663
12574
  this.configuredProviderInfo = null;
12664
12575
  this.indexCompatibility = null;
12665
12576
  this.initializationMode = "none";
@@ -12973,8 +12884,8 @@ function formatExactSearchHandoff(results) {
12973
12884
  }
12974
12885
  function formatContextEvidence(result, index) {
12975
12886
  const symbol = result.name ? ` ${JSON.stringify(compactEvidenceValue(result.name, 80))}` : "";
12976
- const path31 = compactEvidenceValue(result.filePath, 120);
12977
- return `[${index}] ${result.chunkType}${symbol} in ${path31}:${result.startLine}-${result.endLine} (score ${result.score.toFixed(2)})`;
12887
+ const path33 = compactEvidenceValue(result.filePath, 120);
12888
+ return `[${index}] ${result.chunkType}${symbol} in ${path33}:${result.startLine}-${result.endLine} (score ${result.score.toFixed(2)})`;
12978
12889
  }
12979
12890
  function formatContextPack(heading, selected, candidateCount, duplicateCount, limitOmittedCount, budgetOmittedCount, includeExactSearchHandoff) {
12980
12891
  const lines = selected.map((result, index) => formatContextEvidence(result, index + 1));
@@ -13628,7 +13539,7 @@ function getErrorMessage4(error) {
13628
13539
  return error instanceof Error ? error.message : String(error);
13629
13540
  }
13630
13541
  function runCommand(file, args, options) {
13631
- return new Promise((resolve18, reject) => {
13542
+ return new Promise((resolve20, reject) => {
13632
13543
  childProcess.execFile(
13633
13544
  file,
13634
13545
  args,
@@ -13638,7 +13549,7 @@ function runCommand(file, args, options) {
13638
13549
  reject(error);
13639
13550
  return;
13640
13551
  }
13641
- resolve18(stdout);
13552
+ resolve20(stdout);
13642
13553
  }
13643
13554
  );
13644
13555
  });
@@ -13783,10 +13694,10 @@ function safeFailureMessage(error) {
13783
13694
  }
13784
13695
  function cancellableDelay(delayMs, signal) {
13785
13696
  if (signal.aborted) return Promise.reject(new AutoIndexCancelledError());
13786
- return new Promise((resolve18, reject) => {
13697
+ return new Promise((resolve20, reject) => {
13787
13698
  const timer = setTimeout(() => {
13788
13699
  signal.removeEventListener("abort", onAbort);
13789
- resolve18();
13700
+ resolve20();
13790
13701
  }, delayMs);
13791
13702
  timer.unref?.();
13792
13703
  const onAbort = () => {
@@ -13798,15 +13709,15 @@ function cancellableDelay(delayMs, signal) {
13798
13709
  }
13799
13710
  function withTimeout(promise, timeoutMs) {
13800
13711
  if (timeoutMs <= 0) return Promise.resolve(void 0);
13801
- return new Promise((resolve18) => {
13802
- const timer = setTimeout(() => resolve18(void 0), timeoutMs);
13712
+ return new Promise((resolve20) => {
13713
+ const timer = setTimeout(() => resolve20(void 0), timeoutMs);
13803
13714
  timer.unref?.();
13804
13715
  void promise.then((value) => {
13805
13716
  clearTimeout(timer);
13806
- resolve18(value);
13717
+ resolve20(value);
13807
13718
  }, () => {
13808
13719
  clearTimeout(timer);
13809
- resolve18(void 0);
13720
+ resolve20(void 0);
13810
13721
  });
13811
13722
  });
13812
13723
  }
@@ -14188,17 +14099,17 @@ var AutoIndexCoordinator = class {
14188
14099
  }
14189
14100
  }
14190
14101
  waitForBatteryRetry(delayMs) {
14191
- return new Promise((resolve18) => {
14102
+ return new Promise((resolve20) => {
14192
14103
  const timer = setTimeout(() => {
14193
14104
  if (this.batteryRetryTimer === timer) {
14194
14105
  this.batteryRetryTimer = null;
14195
14106
  this.resolveBatteryRetry = null;
14196
14107
  }
14197
- resolve18();
14108
+ resolve20();
14198
14109
  }, delayMs);
14199
14110
  timer.unref?.();
14200
14111
  this.batteryRetryTimer = timer;
14201
- this.resolveBatteryRetry = resolve18;
14112
+ this.resolveBatteryRetry = resolve20;
14202
14113
  });
14203
14114
  }
14204
14115
  cancelBatteryRetry() {
@@ -14206,9 +14117,9 @@ var AutoIndexCoordinator = class {
14206
14117
  clearTimeout(this.batteryRetryTimer);
14207
14118
  this.batteryRetryTimer = null;
14208
14119
  }
14209
- const resolve18 = this.resolveBatteryRetry;
14120
+ const resolve20 = this.resolveBatteryRetry;
14210
14121
  this.resolveBatteryRetry = null;
14211
- resolve18?.();
14122
+ resolve20?.();
14212
14123
  }
14213
14124
  finishBatteryCheck(batteryCheck) {
14214
14125
  if (this.batteryCheck !== batteryCheck) return;
@@ -14883,12 +14794,12 @@ async function getCallGraphPath(projectRoot, host, from, to, maxDepth, fromFileP
14883
14794
  if (fromResolution.status !== "resolved" || toResolution.status !== "resolved") {
14884
14795
  return { from: fromResolution, to: toResolution, path: [] };
14885
14796
  }
14886
- const path31 = await indexer.findCallPathBySymbolIds(
14797
+ const path33 = await indexer.findCallPathBySymbolIds(
14887
14798
  fromResolution.symbolId,
14888
14799
  toResolution.symbolId,
14889
14800
  maxDepth
14890
14801
  );
14891
- return { from: fromResolution, to: toResolution, path: path31 };
14802
+ return { from: fromResolution, to: toResolution, path: path33 };
14892
14803
  }
14893
14804
  async function runIndexCodebase(projectRoot, host, args, onProgress) {
14894
14805
  const root = getProjectRoot(projectRoot, host);
@@ -15513,7 +15424,7 @@ async function resolveCodebaseContextUnmeasured(projectRoot, host, input) {
15513
15424
  const directory = input.directory ?? void 0;
15514
15425
  const tokenBudget = input.tokenBudget ?? void 0;
15515
15426
  if (from && to) {
15516
- const path31 = await getCallGraphPath(
15427
+ const path33 = await getCallGraphPath(
15517
15428
  projectRoot,
15518
15429
  host,
15519
15430
  from,
@@ -15522,25 +15433,25 @@ async function resolveCodebaseContextUnmeasured(projectRoot, host, input) {
15522
15433
  fromFilePath,
15523
15434
  toFilePath
15524
15435
  );
15525
- const pathText = formatCallGraphPathResult(path31);
15526
- if (path31.path.length > 0) {
15436
+ const pathText = formatCallGraphPathResult(path33);
15437
+ if (path33.path.length > 0) {
15527
15438
  const fitted2 = fitTextToContextBudget(
15528
15439
  pathText,
15529
15440
  tokenBudget
15530
15441
  );
15531
15442
  return {
15532
15443
  text: fitted2.text,
15533
- details: fittedDetails("path", fitted2, path31.path.length)
15444
+ details: fittedDetails("path", fitted2, path33.path.length)
15534
15445
  };
15535
15446
  }
15536
- if (path31.from.status !== "resolved" || path31.to.status !== "resolved") {
15447
+ if (path33.from.status !== "resolved" || path33.to.status !== "resolved") {
15537
15448
  const fitted2 = fitTextToContextBudget(pathText, tokenBudget);
15538
15449
  return {
15539
15450
  text: fitted2.text,
15540
15451
  details: fittedDetails("path", fitted2, 0)
15541
15452
  };
15542
15453
  }
15543
- const resolvedFrom = path31.from;
15454
+ const resolvedFrom = path33.from;
15544
15455
  const { callers } = await getCallGraphData(projectRoot, host, {
15545
15456
  name: to,
15546
15457
  direction: "callers",
@@ -16017,9 +15928,9 @@ function getRelevantEvidence(query) {
16017
15928
  });
16018
15929
  }
16019
15930
  if (query.expected.acceptableFiles) {
16020
- for (const path31 of query.expected.acceptableFiles) {
15931
+ for (const path33 of query.expected.acceptableFiles) {
16021
15932
  legacyEvidence.push({
16022
- path: path31,
15933
+ path: path33,
16023
15934
  ...query.expected.symbol !== void 0 ? { symbol: query.expected.symbol } : {},
16024
15935
  relevance: 1
16025
15936
  });
@@ -16516,68 +16427,68 @@ function isStringArray4(value) {
16516
16427
  function isNonEmptyString(value) {
16517
16428
  return typeof value === "string" && value.trim().length > 0;
16518
16429
  }
16519
- function asPositiveNumber(value, path31) {
16430
+ function asPositiveNumber(value, path33) {
16520
16431
  if (typeof value !== "number" || Number.isNaN(value) || value < 0) {
16521
- throw new Error(`${path31} must be a non-negative number`);
16432
+ throw new Error(`${path33} must be a non-negative number`);
16522
16433
  }
16523
16434
  return value;
16524
16435
  }
16525
- function parseQueryType(value, path31) {
16436
+ function parseQueryType(value, path33) {
16526
16437
  if (value === "definition" || value === "implementation-intent" || value === "similarity" || value === "keyword-heavy" || value === "conceptual") {
16527
16438
  return value;
16528
16439
  }
16529
16440
  throw new Error(
16530
- `${path31} must be one of: definition, implementation-intent, similarity, keyword-heavy, conceptual`
16441
+ `${path33} must be one of: definition, implementation-intent, similarity, keyword-heavy, conceptual`
16531
16442
  );
16532
16443
  }
16533
- function parseExpectedRoute(value, path31) {
16444
+ function parseExpectedRoute(value, path33) {
16534
16445
  if (value === void 0) return void 0;
16535
16446
  if (value === "search" || value === "definition") return value;
16536
- throw new Error(`${path31} must be one of: search, definition`);
16447
+ throw new Error(`${path33} must be one of: search, definition`);
16537
16448
  }
16538
- function parseExpectedOutcome(value, path31) {
16449
+ function parseExpectedOutcome(value, path33) {
16539
16450
  if (value === void 0) return void 0;
16540
16451
  if (value === "results" || value === "no-results") {
16541
16452
  return value;
16542
16453
  }
16543
- throw new Error(`${path31} must be one of: results, no-results`);
16454
+ throw new Error(`${path33} must be one of: results, no-results`);
16544
16455
  }
16545
- function parseRecoveryExpectation(value, path31) {
16456
+ function parseRecoveryExpectation(value, path33) {
16546
16457
  if (value === void 0) return void 0;
16547
16458
  if (value === "none" || value === "filter-relaxed") {
16548
16459
  return value;
16549
16460
  }
16550
- throw new Error(`${path31} must be one of: none, filter-relaxed`);
16461
+ throw new Error(`${path33} must be one of: none, filter-relaxed`);
16551
16462
  }
16552
- function parseQueryDifficulty(value, path31) {
16463
+ function parseQueryDifficulty(value, path33) {
16553
16464
  if (value === void 0) return void 0;
16554
16465
  if (value === "easy" || value === "medium" || value === "hard") {
16555
16466
  return value;
16556
16467
  }
16557
- throw new Error(`${path31} must be one of: easy, medium, hard`);
16468
+ throw new Error(`${path33} must be one of: easy, medium, hard`);
16558
16469
  }
16559
- function parseQueryTags(value, path31) {
16470
+ function parseQueryTags(value, path33) {
16560
16471
  if (value === void 0) return void 0;
16561
16472
  if (!isStringArray4(value) || value.some((tag) => tag.trim().length === 0)) {
16562
- throw new Error(`${path31} must be an array of non-empty strings`);
16473
+ throw new Error(`${path33} must be an array of non-empty strings`);
16563
16474
  }
16564
16475
  if (value.length > 16) {
16565
- throw new Error(`${path31} must contain at most 16 tags`);
16476
+ throw new Error(`${path33} must contain at most 16 tags`);
16566
16477
  }
16567
16478
  return value;
16568
16479
  }
16569
- function parseQueryArgs(value, path31) {
16480
+ function parseQueryArgs(value, path33) {
16570
16481
  if (value === void 0) return void 0;
16571
16482
  if (!isRecord3(value)) {
16572
- throw new Error(`${path31} must be an object`);
16573
- }
16574
- const symbol = parseStringOrUndefined(value.symbol, `${path31}.symbol`);
16575
- const filePath = parseStringOrUndefined(value.filePath, `${path31}.filePath`);
16576
- const fileType = parseStringOrUndefined(value.fileType, `${path31}.fileType`);
16577
- const directory = parseStringOrUndefined(value.directory, `${path31}.directory`);
16578
- const callerLimit = parsePositiveIntegerOrUndefined(value.callerLimit, `${path31}.callerLimit`);
16579
- const calleeLimit = parsePositiveIntegerOrUndefined(value.calleeLimit, `${path31}.calleeLimit`);
16580
- const tokenBudget = parsePositiveIntegerOrUndefined(value.tokenBudget, `${path31}.tokenBudget`);
16483
+ throw new Error(`${path33} must be an object`);
16484
+ }
16485
+ const symbol = parseStringOrUndefined(value.symbol, `${path33}.symbol`);
16486
+ const filePath = parseStringOrUndefined(value.filePath, `${path33}.filePath`);
16487
+ const fileType = parseStringOrUndefined(value.fileType, `${path33}.fileType`);
16488
+ const directory = parseStringOrUndefined(value.directory, `${path33}.directory`);
16489
+ const callerLimit = parsePositiveIntegerOrUndefined(value.callerLimit, `${path33}.callerLimit`);
16490
+ const calleeLimit = parsePositiveIntegerOrUndefined(value.calleeLimit, `${path33}.calleeLimit`);
16491
+ const tokenBudget = parsePositiveIntegerOrUndefined(value.tokenBudget, `${path33}.tokenBudget`);
16581
16492
  return {
16582
16493
  ...symbol !== void 0 ? { symbol } : {},
16583
16494
  ...filePath !== void 0 ? { filePath } : {},
@@ -16588,50 +16499,50 @@ function parseQueryArgs(value, path31) {
16588
16499
  ...tokenBudget !== void 0 ? { tokenBudget } : {}
16589
16500
  };
16590
16501
  }
16591
- function parsePositiveIntegerOrUndefined(value, path31) {
16502
+ function parsePositiveIntegerOrUndefined(value, path33) {
16592
16503
  if (value === void 0 || value === null) return void 0;
16593
16504
  if (typeof value !== "number" || !Number.isInteger(value) || value <= 0) {
16594
- throw new Error(`${path31} must be a positive integer`);
16505
+ throw new Error(`${path33} must be a positive integer`);
16595
16506
  }
16596
16507
  return value;
16597
16508
  }
16598
16509
  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-]+)*)?$/;
16599
- function parseSemanticVersion(value, path31) {
16510
+ function parseSemanticVersion(value, path33) {
16600
16511
  if (!isNonEmptyString(value)) {
16601
- throw new Error(`${path31} must be a non-empty string`);
16512
+ throw new Error(`${path33} must be a non-empty string`);
16602
16513
  }
16603
16514
  if (!SEMVER_VERSION_PATTERN.test(value)) {
16604
- throw new Error(`${path31} must be a valid semantic version (MAJOR.MINOR.PATCH)`);
16515
+ throw new Error(`${path33} must be a valid semantic version (MAJOR.MINOR.PATCH)`);
16605
16516
  }
16606
16517
  return value;
16607
16518
  }
16608
- function parseRetrievalMode(value, path31) {
16519
+ function parseRetrievalMode(value, path33) {
16609
16520
  if (value === void 0 || value === "search") return "search";
16610
16521
  if (value === "context" || value === "edit-context") return value;
16611
- throw new Error(`${path31} must be one of: search, context, edit-context`);
16522
+ throw new Error(`${path33} must be one of: search, context, edit-context`);
16612
16523
  }
16613
- function parseStringOrUndefined(value, path31) {
16524
+ function parseStringOrUndefined(value, path33) {
16614
16525
  if (value === void 0 || value === null) return void 0;
16615
16526
  if (!isNonEmptyString(value)) {
16616
- throw new Error(`${path31} must be a non-empty string`);
16527
+ throw new Error(`${path33} must be a non-empty string`);
16617
16528
  }
16618
16529
  return value;
16619
16530
  }
16620
- function parseGradedEvidence(value, path31) {
16531
+ function parseGradedEvidence(value, path33) {
16621
16532
  if (value === void 0) return [];
16622
16533
  if (!Array.isArray(value)) {
16623
- throw new Error(`${path31} must be an array`);
16534
+ throw new Error(`${path33} must be an array`);
16624
16535
  }
16625
16536
  return value.map((entry, index) => {
16626
16537
  if (!isRecord3(entry)) {
16627
- throw new Error(`${path31}[${index}] must be an object`);
16538
+ throw new Error(`${path33}[${index}] must be an object`);
16628
16539
  }
16629
- const evidencePath = parseStringOrUndefined(entry.path, `${path31}[${index}].path`);
16540
+ const evidencePath = parseStringOrUndefined(entry.path, `${path33}[${index}].path`);
16630
16541
  if (evidencePath === void 0) {
16631
- throw new Error(`${path31}[${index}].path is required`);
16542
+ throw new Error(`${path33}[${index}].path is required`);
16632
16543
  }
16633
- const symbol = parseStringOrUndefined(entry.symbol, `${path31}[${index}].symbol`);
16634
- const relevance = parseEvidenceRelevance(entry.relevance, `${path31}[${index}].relevance`);
16544
+ const symbol = parseStringOrUndefined(entry.symbol, `${path33}[${index}].symbol`);
16545
+ const relevance = parseEvidenceRelevance(entry.relevance, `${path33}[${index}].relevance`);
16635
16546
  return {
16636
16547
  path: evidencePath,
16637
16548
  ...symbol !== void 0 ? { symbol } : {},
@@ -16639,27 +16550,27 @@ function parseGradedEvidence(value, path31) {
16639
16550
  };
16640
16551
  });
16641
16552
  }
16642
- function parseEvidenceRelevance(value, path31) {
16553
+ function parseEvidenceRelevance(value, path33) {
16643
16554
  if (value === void 0) {
16644
- throw new Error(`${path31} is required`);
16555
+ throw new Error(`${path33} is required`);
16645
16556
  }
16646
16557
  if (value !== 1 && value !== 2 && value !== 3) {
16647
- throw new Error(`${path31} must be 1, 2, or 3`);
16558
+ throw new Error(`${path33} must be 1, 2, or 3`);
16648
16559
  }
16649
16560
  return value;
16650
16561
  }
16651
- function parseExpectedGraphNeighbor(value, path31) {
16562
+ function parseExpectedGraphNeighbor(value, path33) {
16652
16563
  if (value === void 0) return void 0;
16653
16564
  if (!isRecord3(value)) {
16654
- throw new Error(`${path31} must be an object`);
16565
+ throw new Error(`${path33} must be an object`);
16655
16566
  }
16656
16567
  if (value.direction !== "caller" && value.direction !== "callee") {
16657
- throw new Error(`${path31}.direction must be one of: caller, callee`);
16568
+ throw new Error(`${path33}.direction must be one of: caller, callee`);
16658
16569
  }
16659
- const filePath = parseStringOrUndefined(value.filePath, `${path31}.filePath`);
16660
- const symbol = parseStringOrUndefined(value.symbol, `${path31}.symbol`);
16570
+ const filePath = parseStringOrUndefined(value.filePath, `${path33}.filePath`);
16571
+ const symbol = parseStringOrUndefined(value.symbol, `${path33}.symbol`);
16661
16572
  if (filePath === void 0 && symbol === void 0) {
16662
- throw new Error(`${path31} must include filePath or symbol`);
16573
+ throw new Error(`${path33} must include filePath or symbol`);
16663
16574
  }
16664
16575
  return {
16665
16576
  direction: value.direction,
@@ -16667,9 +16578,9 @@ function parseExpectedGraphNeighbor(value, path31) {
16667
16578
  ...symbol !== void 0 ? { symbol } : {}
16668
16579
  };
16669
16580
  }
16670
- function parseExpected(input, path31) {
16581
+ function parseExpected(input, path33) {
16671
16582
  if (!isRecord3(input)) {
16672
- throw new Error(`${path31} must be an object`);
16583
+ throw new Error(`${path33} must be an object`);
16673
16584
  }
16674
16585
  const filePathRaw = input.filePath;
16675
16586
  const acceptableFilesRaw = input.acceptableFiles;
@@ -16680,29 +16591,29 @@ function parseExpected(input, path31) {
16680
16591
  const recoveryExpectationRaw = input.recoveryExpectation;
16681
16592
  const gradedEvidenceRaw = input.gradedEvidence;
16682
16593
  const graphNeighborRaw = input.graphNeighbor;
16683
- const filePath = parseStringOrUndefined(filePathRaw, `${path31}.filePath`);
16594
+ const filePath = parseStringOrUndefined(filePathRaw, `${path33}.filePath`);
16684
16595
  const acceptableFiles = isStringArray4(acceptableFilesRaw) ? acceptableFilesRaw : void 0;
16685
- const gradedEvidence = parseGradedEvidence(gradedEvidenceRaw, `${path31}.gradedEvidence`);
16686
- const graphNeighbor = parseExpectedGraphNeighbor(graphNeighborRaw, `${path31}.graphNeighbor`);
16687
- const expectedOutcome = parseExpectedOutcome(expectedOutcomeRaw, `${path31}.expectedOutcome`);
16596
+ const gradedEvidence = parseGradedEvidence(gradedEvidenceRaw, `${path33}.gradedEvidence`);
16597
+ const graphNeighbor = parseExpectedGraphNeighbor(graphNeighborRaw, `${path33}.graphNeighbor`);
16598
+ const expectedOutcome = parseExpectedOutcome(expectedOutcomeRaw, `${path33}.expectedOutcome`);
16688
16599
  if (expectedOutcome !== "no-results" && !filePath && (!acceptableFiles || acceptableFiles.length === 0) && gradedEvidence.length === 0) {
16689
16600
  throw new Error(
16690
- `${path31} must include expected.filePath, expected.acceptableFiles, or expected.gradedEvidence`
16601
+ `${path33} must include expected.filePath, expected.acceptableFiles, or expected.gradedEvidence`
16691
16602
  );
16692
16603
  }
16693
16604
  if (acceptableFilesRaw !== void 0 && !isStringArray4(acceptableFilesRaw)) {
16694
- throw new Error(`${path31}.acceptableFiles must be an array of strings`);
16605
+ throw new Error(`${path33}.acceptableFiles must be an array of strings`);
16695
16606
  }
16696
16607
  if (symbolRaw !== void 0 && typeof symbolRaw !== "string") {
16697
- throw new Error(`${path31}.symbol must be a string when provided`);
16608
+ throw new Error(`${path33}.symbol must be a string when provided`);
16698
16609
  }
16699
16610
  if (branchRaw !== void 0 && typeof branchRaw !== "string") {
16700
- throw new Error(`${path31}.branch must be a string when provided`);
16611
+ throw new Error(`${path33}.branch must be a string when provided`);
16701
16612
  }
16702
- const expectedRoute = parseExpectedRoute(expectedRouteRaw, `${path31}.expectedRoute`);
16613
+ const expectedRoute = parseExpectedRoute(expectedRouteRaw, `${path33}.expectedRoute`);
16703
16614
  const recoveryExpectation = parseRecoveryExpectation(
16704
16615
  recoveryExpectationRaw,
16705
- `${path31}.recoveryExpectation`
16616
+ `${path33}.recoveryExpectation`
16706
16617
  );
16707
16618
  return {
16708
16619
  filePath,
@@ -16716,13 +16627,13 @@ function parseExpected(input, path31) {
16716
16627
  ...graphNeighbor !== void 0 ? { graphNeighbor } : {}
16717
16628
  };
16718
16629
  }
16719
- function parseQueryLanguage(value, path31) {
16720
- return parseStringOrUndefined(value, path31);
16630
+ function parseQueryLanguage(value, path33) {
16631
+ return parseStringOrUndefined(value, path33);
16721
16632
  }
16722
16633
  function parseQuery(input, index) {
16723
- const path31 = `queries[${index}]`;
16634
+ const path33 = `queries[${index}]`;
16724
16635
  if (!isRecord3(input)) {
16725
- throw new Error(`${path31} must be an object`);
16636
+ throw new Error(`${path33} must be an object`);
16726
16637
  }
16727
16638
  const id = input.id;
16728
16639
  const query = input.query;
@@ -16734,21 +16645,21 @@ function parseQuery(input, index) {
16734
16645
  const tags = input.tags;
16735
16646
  const args = input.args;
16736
16647
  if (typeof id !== "string" || id.trim().length === 0) {
16737
- throw new Error(`${path31}.id must be a non-empty string`);
16648
+ throw new Error(`${path33}.id must be a non-empty string`);
16738
16649
  }
16739
16650
  if (typeof query !== "string" || query.trim().length === 0) {
16740
- throw new Error(`${path31}.query must be a non-empty string`);
16651
+ throw new Error(`${path33}.query must be a non-empty string`);
16741
16652
  }
16742
16653
  return {
16743
16654
  id,
16744
16655
  query,
16745
- queryType: parseQueryType(queryType, `${path31}.queryType`),
16746
- retrievalMode: parseRetrievalMode(retrievalMode, `${path31}.retrievalMode`),
16747
- language: parseQueryLanguage(language, `${path31}.language`),
16748
- difficulty: parseQueryDifficulty(difficulty, `${path31}.difficulty`),
16749
- args: parseQueryArgs(args, `${path31}.args`),
16750
- tags: parseQueryTags(tags, `${path31}.tags`),
16751
- expected: parseExpected(expected, `${path31}.expected`)
16656
+ queryType: parseQueryType(queryType, `${path33}.queryType`),
16657
+ retrievalMode: parseRetrievalMode(retrievalMode, `${path33}.retrievalMode`),
16658
+ language: parseQueryLanguage(language, `${path33}.language`),
16659
+ difficulty: parseQueryDifficulty(difficulty, `${path33}.difficulty`),
16660
+ args: parseQueryArgs(args, `${path33}.args`),
16661
+ tags: parseQueryTags(tags, `${path33}.tags`),
16662
+ expected: parseExpected(expected, `${path33}.expected`)
16752
16663
  };
16753
16664
  }
16754
16665
  function parseGoldenDataset(raw, sourceLabel) {
@@ -17715,7 +17626,7 @@ async function executeCallGraph(projectRoot, host, args) {
17715
17626
  return { text: formatCallGraphResult(await getCallGraphData(projectRoot, host, args)) };
17716
17627
  }
17717
17628
  async function executeCallGraphPath(projectRoot, host, args) {
17718
- const path31 = await getCallGraphPath(
17629
+ const path33 = await getCallGraphPath(
17719
17630
  projectRoot,
17720
17631
  host,
17721
17632
  args.from,
@@ -17724,7 +17635,7 @@ async function executeCallGraphPath(projectRoot, host, args) {
17724
17635
  args.fromFilePath,
17725
17636
  args.toFilePath
17726
17637
  );
17727
- return { text: formatCallGraphPathResult(path31) };
17638
+ return { text: formatCallGraphPathResult(path33) };
17728
17639
  }
17729
17640
  async function executeCodeCommunities(projectRoot, host, args) {
17730
17641
  const result = await getCodeCommunities(projectRoot, host, args);
@@ -18303,7 +18214,7 @@ var ReaddirpStream = class extends import_node_stream.Readable {
18303
18214
  this._directoryFilter = normalizeFilter(opts.directoryFilter);
18304
18215
  const statMethod = opts.lstat ? import_promises.lstat : import_promises.stat;
18305
18216
  if (wantBigintFsStats) {
18306
- this._stat = (path31) => statMethod(path31, { bigint: true });
18217
+ this._stat = (path33) => statMethod(path33, { bigint: true });
18307
18218
  } else {
18308
18219
  this._stat = statMethod;
18309
18220
  }
@@ -18328,8 +18239,8 @@ var ReaddirpStream = class extends import_node_stream.Readable {
18328
18239
  const par = this.parent;
18329
18240
  const fil = par && par.files;
18330
18241
  if (fil && fil.length > 0) {
18331
- const { path: path31, depth } = par;
18332
- const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path31));
18242
+ const { path: path33, depth } = par;
18243
+ const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path33));
18333
18244
  const awaited = await Promise.all(slice);
18334
18245
  for (const entry of awaited) {
18335
18246
  if (!entry)
@@ -18369,20 +18280,20 @@ var ReaddirpStream = class extends import_node_stream.Readable {
18369
18280
  this.reading = false;
18370
18281
  }
18371
18282
  }
18372
- async _exploreDir(path31, depth) {
18283
+ async _exploreDir(path33, depth) {
18373
18284
  let files;
18374
18285
  try {
18375
- files = await (0, import_promises.readdir)(path31, this._rdOptions);
18286
+ files = await (0, import_promises.readdir)(path33, this._rdOptions);
18376
18287
  } catch (error) {
18377
18288
  this._onError(error);
18378
18289
  }
18379
- return { files, depth, path: path31 };
18290
+ return { files, depth, path: path33 };
18380
18291
  }
18381
- async _formatEntry(dirent, path31) {
18292
+ async _formatEntry(dirent, path33) {
18382
18293
  let entry;
18383
18294
  const basename8 = this._isDirent ? dirent.name : dirent;
18384
18295
  try {
18385
- const fullPath = (0, import_node_path.resolve)((0, import_node_path.join)(path31, basename8));
18296
+ const fullPath = (0, import_node_path.resolve)((0, import_node_path.join)(path33, basename8));
18386
18297
  entry = { path: (0, import_node_path.relative)(this._root, fullPath), fullPath, basename: basename8 };
18387
18298
  entry[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath);
18388
18299
  } catch (err) {
@@ -18782,16 +18693,16 @@ var delFromSet = (main, prop, item) => {
18782
18693
  };
18783
18694
  var isEmptySet = (val) => val instanceof Set ? val.size === 0 : !val;
18784
18695
  var FsWatchInstances = /* @__PURE__ */ new Map();
18785
- function createFsWatchInstance(path31, options, listener, errHandler, emitRaw) {
18696
+ function createFsWatchInstance(path33, options, listener, errHandler, emitRaw) {
18786
18697
  const handleEvent = (rawEvent, evPath) => {
18787
- listener(path31);
18788
- emitRaw(rawEvent, evPath, { watchedPath: path31 });
18789
- if (evPath && path31 !== evPath) {
18790
- fsWatchBroadcast(sp.resolve(path31, evPath), KEY_LISTENERS, sp.join(path31, evPath));
18698
+ listener(path33);
18699
+ emitRaw(rawEvent, evPath, { watchedPath: path33 });
18700
+ if (evPath && path33 !== evPath) {
18701
+ fsWatchBroadcast(sp.resolve(path33, evPath), KEY_LISTENERS, sp.join(path33, evPath));
18791
18702
  }
18792
18703
  };
18793
18704
  try {
18794
- return (0, import_node_fs.watch)(path31, {
18705
+ return (0, import_node_fs.watch)(path33, {
18795
18706
  persistent: options.persistent
18796
18707
  }, handleEvent);
18797
18708
  } catch (error) {
@@ -18807,12 +18718,12 @@ var fsWatchBroadcast = (fullPath, listenerType, val1, val2, val3) => {
18807
18718
  listener(val1, val2, val3);
18808
18719
  });
18809
18720
  };
18810
- var setFsWatchListener = (path31, fullPath, options, handlers) => {
18721
+ var setFsWatchListener = (path33, fullPath, options, handlers) => {
18811
18722
  const { listener, errHandler, rawEmitter } = handlers;
18812
18723
  let cont = FsWatchInstances.get(fullPath);
18813
18724
  let watcher;
18814
18725
  if (!options.persistent) {
18815
- watcher = createFsWatchInstance(path31, options, listener, errHandler, rawEmitter);
18726
+ watcher = createFsWatchInstance(path33, options, listener, errHandler, rawEmitter);
18816
18727
  if (!watcher)
18817
18728
  return;
18818
18729
  return watcher.close.bind(watcher);
@@ -18823,7 +18734,7 @@ var setFsWatchListener = (path31, fullPath, options, handlers) => {
18823
18734
  addAndConvert(cont, KEY_RAW, rawEmitter);
18824
18735
  } else {
18825
18736
  watcher = createFsWatchInstance(
18826
- path31,
18737
+ path33,
18827
18738
  options,
18828
18739
  fsWatchBroadcast.bind(null, fullPath, KEY_LISTENERS),
18829
18740
  errHandler,
@@ -18838,7 +18749,7 @@ var setFsWatchListener = (path31, fullPath, options, handlers) => {
18838
18749
  cont.watcherUnusable = true;
18839
18750
  if (isWindows && error.code === "EPERM") {
18840
18751
  try {
18841
- const fd = await (0, import_promises2.open)(path31, "r");
18752
+ const fd = await (0, import_promises2.open)(path33, "r");
18842
18753
  await fd.close();
18843
18754
  broadcastErr(error);
18844
18755
  } catch (err) {
@@ -18869,7 +18780,7 @@ var setFsWatchListener = (path31, fullPath, options, handlers) => {
18869
18780
  };
18870
18781
  };
18871
18782
  var FsWatchFileInstances = /* @__PURE__ */ new Map();
18872
- var setFsWatchFileListener = (path31, fullPath, options, handlers) => {
18783
+ var setFsWatchFileListener = (path33, fullPath, options, handlers) => {
18873
18784
  const { listener, rawEmitter } = handlers;
18874
18785
  let cont = FsWatchFileInstances.get(fullPath);
18875
18786
  const copts = cont && cont.options;
@@ -18891,7 +18802,7 @@ var setFsWatchFileListener = (path31, fullPath, options, handlers) => {
18891
18802
  });
18892
18803
  const currmtime = curr.mtimeMs;
18893
18804
  if (curr.size !== prev.size || currmtime > prev.mtimeMs || currmtime === 0) {
18894
- foreach(cont.listeners, (listener2) => listener2(path31, curr));
18805
+ foreach(cont.listeners, (listener2) => listener2(path33, curr));
18895
18806
  }
18896
18807
  })
18897
18808
  };
@@ -18921,13 +18832,13 @@ var NodeFsHandler = class {
18921
18832
  * @param listener on fs change
18922
18833
  * @returns closer for the watcher instance
18923
18834
  */
18924
- _watchWithNodeFs(path31, listener) {
18835
+ _watchWithNodeFs(path33, listener) {
18925
18836
  const opts = this.fsw.options;
18926
- const directory = sp.dirname(path31);
18927
- const basename8 = sp.basename(path31);
18837
+ const directory = sp.dirname(path33);
18838
+ const basename8 = sp.basename(path33);
18928
18839
  const parent = this.fsw._getWatchedDir(directory);
18929
18840
  parent.add(basename8);
18930
- const absolutePath = sp.resolve(path31);
18841
+ const absolutePath = sp.resolve(path33);
18931
18842
  const options = {
18932
18843
  persistent: opts.persistent
18933
18844
  };
@@ -18937,12 +18848,12 @@ var NodeFsHandler = class {
18937
18848
  if (opts.usePolling) {
18938
18849
  const enableBin = opts.interval !== opts.binaryInterval;
18939
18850
  options.interval = enableBin && isBinaryPath(basename8) ? opts.binaryInterval : opts.interval;
18940
- closer = setFsWatchFileListener(path31, absolutePath, options, {
18851
+ closer = setFsWatchFileListener(path33, absolutePath, options, {
18941
18852
  listener,
18942
18853
  rawEmitter: this.fsw._emitRaw
18943
18854
  });
18944
18855
  } else {
18945
- closer = setFsWatchListener(path31, absolutePath, options, {
18856
+ closer = setFsWatchListener(path33, absolutePath, options, {
18946
18857
  listener,
18947
18858
  errHandler: this._boundHandleError,
18948
18859
  rawEmitter: this.fsw._emitRaw
@@ -18964,7 +18875,7 @@ var NodeFsHandler = class {
18964
18875
  let prevStats = stats;
18965
18876
  if (parent.has(basename8))
18966
18877
  return;
18967
- const listener = async (path31, newStats) => {
18878
+ const listener = async (path33, newStats) => {
18968
18879
  if (!this.fsw._throttle(THROTTLE_MODE_WATCH, file, 5))
18969
18880
  return;
18970
18881
  if (!newStats || newStats.mtimeMs === 0) {
@@ -18978,11 +18889,11 @@ var NodeFsHandler = class {
18978
18889
  this.fsw._emit(EV.CHANGE, file, newStats2);
18979
18890
  }
18980
18891
  if ((isMacos || isLinux || isFreeBSD) && prevStats.ino !== newStats2.ino) {
18981
- this.fsw._closeFile(path31);
18892
+ this.fsw._closeFile(path33);
18982
18893
  prevStats = newStats2;
18983
18894
  const closer2 = this._watchWithNodeFs(file, listener);
18984
18895
  if (closer2)
18985
- this.fsw._addPathCloser(path31, closer2);
18896
+ this.fsw._addPathCloser(path33, closer2);
18986
18897
  } else {
18987
18898
  prevStats = newStats2;
18988
18899
  }
@@ -19014,7 +18925,7 @@ var NodeFsHandler = class {
19014
18925
  * @param item basename of this item
19015
18926
  * @returns true if no more processing is needed for this entry.
19016
18927
  */
19017
- async _handleSymlink(entry, directory, path31, item) {
18928
+ async _handleSymlink(entry, directory, path33, item) {
19018
18929
  if (this.fsw.closed) {
19019
18930
  return;
19020
18931
  }
@@ -19024,7 +18935,7 @@ var NodeFsHandler = class {
19024
18935
  this.fsw._incrReadyCount();
19025
18936
  let linkPath;
19026
18937
  try {
19027
- linkPath = await (0, import_promises2.realpath)(path31);
18938
+ linkPath = await (0, import_promises2.realpath)(path33);
19028
18939
  } catch (e) {
19029
18940
  this.fsw._emitReady();
19030
18941
  return true;
@@ -19034,12 +18945,12 @@ var NodeFsHandler = class {
19034
18945
  if (dir.has(item)) {
19035
18946
  if (this.fsw._symlinkPaths.get(full) !== linkPath) {
19036
18947
  this.fsw._symlinkPaths.set(full, linkPath);
19037
- this.fsw._emit(EV.CHANGE, path31, entry.stats);
18948
+ this.fsw._emit(EV.CHANGE, path33, entry.stats);
19038
18949
  }
19039
18950
  } else {
19040
18951
  dir.add(item);
19041
18952
  this.fsw._symlinkPaths.set(full, linkPath);
19042
- this.fsw._emit(EV.ADD, path31, entry.stats);
18953
+ this.fsw._emit(EV.ADD, path33, entry.stats);
19043
18954
  }
19044
18955
  this.fsw._emitReady();
19045
18956
  return true;
@@ -19069,9 +18980,9 @@ var NodeFsHandler = class {
19069
18980
  return;
19070
18981
  }
19071
18982
  const item = entry.path;
19072
- let path31 = sp.join(directory, item);
18983
+ let path33 = sp.join(directory, item);
19073
18984
  current.add(item);
19074
- if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path31, item)) {
18985
+ if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path33, item)) {
19075
18986
  return;
19076
18987
  }
19077
18988
  if (this.fsw.closed) {
@@ -19080,11 +18991,11 @@ var NodeFsHandler = class {
19080
18991
  }
19081
18992
  if (item === target || !target && !previous.has(item)) {
19082
18993
  this.fsw._incrReadyCount();
19083
- path31 = sp.join(dir, sp.relative(dir, path31));
19084
- this._addToNodeFs(path31, initialAdd, wh, depth + 1);
18994
+ path33 = sp.join(dir, sp.relative(dir, path33));
18995
+ this._addToNodeFs(path33, initialAdd, wh, depth + 1);
19085
18996
  }
19086
18997
  }).on(EV.ERROR, this._boundHandleError);
19087
- return new Promise((resolve18, reject) => {
18998
+ return new Promise((resolve20, reject) => {
19088
18999
  if (!stream)
19089
19000
  return reject();
19090
19001
  stream.once(STR_END, () => {
@@ -19093,7 +19004,7 @@ var NodeFsHandler = class {
19093
19004
  return;
19094
19005
  }
19095
19006
  const wasThrottled = throttler ? throttler.clear() : false;
19096
- resolve18(void 0);
19007
+ resolve20(void 0);
19097
19008
  previous.getChildren().filter((item) => {
19098
19009
  return item !== directory && !current.has(item);
19099
19010
  }).forEach((item) => {
@@ -19150,13 +19061,13 @@ var NodeFsHandler = class {
19150
19061
  * @param depth Child path actually targeted for watch
19151
19062
  * @param target Child path actually targeted for watch
19152
19063
  */
19153
- async _addToNodeFs(path31, initialAdd, priorWh, depth, target) {
19064
+ async _addToNodeFs(path33, initialAdd, priorWh, depth, target) {
19154
19065
  const ready = this.fsw._emitReady;
19155
- if (this.fsw._isIgnored(path31) || this.fsw.closed) {
19066
+ if (this.fsw._isIgnored(path33) || this.fsw.closed) {
19156
19067
  ready();
19157
19068
  return false;
19158
19069
  }
19159
- const wh = this.fsw._getWatchHelpers(path31);
19070
+ const wh = this.fsw._getWatchHelpers(path33);
19160
19071
  if (priorWh) {
19161
19072
  wh.filterPath = (entry) => priorWh.filterPath(entry);
19162
19073
  wh.filterDir = (entry) => priorWh.filterDir(entry);
@@ -19172,8 +19083,8 @@ var NodeFsHandler = class {
19172
19083
  const follow = this.fsw.options.followSymlinks;
19173
19084
  let closer;
19174
19085
  if (stats.isDirectory()) {
19175
- const absPath = sp.resolve(path31);
19176
- const targetPath = follow ? await (0, import_promises2.realpath)(path31) : path31;
19086
+ const absPath = sp.resolve(path33);
19087
+ const targetPath = follow ? await (0, import_promises2.realpath)(path33) : path33;
19177
19088
  if (this.fsw.closed)
19178
19089
  return;
19179
19090
  closer = await this._handleDir(wh.watchPath, stats, initialAdd, depth, target, wh, targetPath);
@@ -19183,29 +19094,29 @@ var NodeFsHandler = class {
19183
19094
  this.fsw._symlinkPaths.set(absPath, targetPath);
19184
19095
  }
19185
19096
  } else if (stats.isSymbolicLink()) {
19186
- const targetPath = follow ? await (0, import_promises2.realpath)(path31) : path31;
19097
+ const targetPath = follow ? await (0, import_promises2.realpath)(path33) : path33;
19187
19098
  if (this.fsw.closed)
19188
19099
  return;
19189
19100
  const parent = sp.dirname(wh.watchPath);
19190
19101
  this.fsw._getWatchedDir(parent).add(wh.watchPath);
19191
19102
  this.fsw._emit(EV.ADD, wh.watchPath, stats);
19192
- closer = await this._handleDir(parent, stats, initialAdd, depth, path31, wh, targetPath);
19103
+ closer = await this._handleDir(parent, stats, initialAdd, depth, path33, wh, targetPath);
19193
19104
  if (this.fsw.closed)
19194
19105
  return;
19195
19106
  if (targetPath !== void 0) {
19196
- this.fsw._symlinkPaths.set(sp.resolve(path31), targetPath);
19107
+ this.fsw._symlinkPaths.set(sp.resolve(path33), targetPath);
19197
19108
  }
19198
19109
  } else {
19199
19110
  closer = this._handleFile(wh.watchPath, stats, initialAdd);
19200
19111
  }
19201
19112
  ready();
19202
19113
  if (closer)
19203
- this.fsw._addPathCloser(path31, closer);
19114
+ this.fsw._addPathCloser(path33, closer);
19204
19115
  return false;
19205
19116
  } catch (error) {
19206
19117
  if (this.fsw._handleError(error)) {
19207
19118
  ready();
19208
- return path31;
19119
+ return path33;
19209
19120
  }
19210
19121
  }
19211
19122
  }
@@ -19237,35 +19148,35 @@ function createPattern(matcher) {
19237
19148
  if (matcher.path === string)
19238
19149
  return true;
19239
19150
  if (matcher.recursive) {
19240
- const relative12 = sp2.relative(matcher.path, string);
19241
- if (!relative12) {
19151
+ const relative14 = sp2.relative(matcher.path, string);
19152
+ if (!relative14) {
19242
19153
  return false;
19243
19154
  }
19244
- return !relative12.startsWith("..") && !sp2.isAbsolute(relative12);
19155
+ return !relative14.startsWith("..") && !sp2.isAbsolute(relative14);
19245
19156
  }
19246
19157
  return false;
19247
19158
  };
19248
19159
  }
19249
19160
  return () => false;
19250
19161
  }
19251
- function normalizePath3(path31) {
19252
- if (typeof path31 !== "string")
19162
+ function normalizePath3(path33) {
19163
+ if (typeof path33 !== "string")
19253
19164
  throw new Error("string expected");
19254
- path31 = sp2.normalize(path31);
19255
- path31 = path31.replace(/\\/g, "/");
19165
+ path33 = sp2.normalize(path33);
19166
+ path33 = path33.replace(/\\/g, "/");
19256
19167
  let prepend = false;
19257
- if (path31.startsWith("//"))
19168
+ if (path33.startsWith("//"))
19258
19169
  prepend = true;
19259
- path31 = path31.replace(DOUBLE_SLASH_RE, "/");
19170
+ path33 = path33.replace(DOUBLE_SLASH_RE, "/");
19260
19171
  if (prepend)
19261
- path31 = "/" + path31;
19262
- return path31;
19172
+ path33 = "/" + path33;
19173
+ return path33;
19263
19174
  }
19264
19175
  function matchPatterns(patterns, testString, stats) {
19265
- const path31 = normalizePath3(testString);
19176
+ const path33 = normalizePath3(testString);
19266
19177
  for (let index = 0; index < patterns.length; index++) {
19267
19178
  const pattern = patterns[index];
19268
- if (pattern(path31, stats)) {
19179
+ if (pattern(path33, stats)) {
19269
19180
  return true;
19270
19181
  }
19271
19182
  }
@@ -19303,19 +19214,19 @@ var toUnix = (string) => {
19303
19214
  }
19304
19215
  return str;
19305
19216
  };
19306
- var normalizePathToUnix = (path31) => toUnix(sp2.normalize(toUnix(path31)));
19307
- var normalizeIgnored = (cwd = "") => (path31) => {
19308
- if (typeof path31 === "string") {
19309
- return normalizePathToUnix(sp2.isAbsolute(path31) ? path31 : sp2.join(cwd, path31));
19217
+ var normalizePathToUnix = (path33) => toUnix(sp2.normalize(toUnix(path33)));
19218
+ var normalizeIgnored = (cwd = "") => (path33) => {
19219
+ if (typeof path33 === "string") {
19220
+ return normalizePathToUnix(sp2.isAbsolute(path33) ? path33 : sp2.join(cwd, path33));
19310
19221
  } else {
19311
- return path31;
19222
+ return path33;
19312
19223
  }
19313
19224
  };
19314
- var getAbsolutePath = (path31, cwd) => {
19315
- if (sp2.isAbsolute(path31)) {
19316
- return path31;
19225
+ var getAbsolutePath = (path33, cwd) => {
19226
+ if (sp2.isAbsolute(path33)) {
19227
+ return path33;
19317
19228
  }
19318
- return sp2.join(cwd, path31);
19229
+ return sp2.join(cwd, path33);
19319
19230
  };
19320
19231
  var EMPTY_SET = Object.freeze(/* @__PURE__ */ new Set());
19321
19232
  var DirEntry = class {
@@ -19380,10 +19291,10 @@ var WatchHelper = class {
19380
19291
  dirParts;
19381
19292
  followSymlinks;
19382
19293
  statMethod;
19383
- constructor(path31, follow, fsw) {
19294
+ constructor(path33, follow, fsw) {
19384
19295
  this.fsw = fsw;
19385
- const watchPath = path31;
19386
- this.path = path31 = path31.replace(REPLACER_RE, "");
19296
+ const watchPath = path33;
19297
+ this.path = path33 = path33.replace(REPLACER_RE, "");
19387
19298
  this.watchPath = watchPath;
19388
19299
  this.fullWatchPath = sp2.resolve(watchPath);
19389
19300
  this.dirParts = [];
@@ -19523,20 +19434,20 @@ var FSWatcher = class extends import_node_events.EventEmitter {
19523
19434
  this._closePromise = void 0;
19524
19435
  let paths = unifyPaths(paths_);
19525
19436
  if (cwd) {
19526
- paths = paths.map((path31) => {
19527
- const absPath = getAbsolutePath(path31, cwd);
19437
+ paths = paths.map((path33) => {
19438
+ const absPath = getAbsolutePath(path33, cwd);
19528
19439
  return absPath;
19529
19440
  });
19530
19441
  }
19531
- paths.forEach((path31) => {
19532
- this._removeIgnoredPath(path31);
19442
+ paths.forEach((path33) => {
19443
+ this._removeIgnoredPath(path33);
19533
19444
  });
19534
19445
  this._userIgnored = void 0;
19535
19446
  if (!this._readyCount)
19536
19447
  this._readyCount = 0;
19537
19448
  this._readyCount += paths.length;
19538
- Promise.all(paths.map(async (path31) => {
19539
- const res = await this._nodeFsHandler._addToNodeFs(path31, !_internal, void 0, 0, _origAdd);
19449
+ Promise.all(paths.map(async (path33) => {
19450
+ const res = await this._nodeFsHandler._addToNodeFs(path33, !_internal, void 0, 0, _origAdd);
19540
19451
  if (res)
19541
19452
  this._emitReady();
19542
19453
  return res;
@@ -19558,17 +19469,17 @@ var FSWatcher = class extends import_node_events.EventEmitter {
19558
19469
  return this;
19559
19470
  const paths = unifyPaths(paths_);
19560
19471
  const { cwd } = this.options;
19561
- paths.forEach((path31) => {
19562
- if (!sp2.isAbsolute(path31) && !this._closers.has(path31)) {
19472
+ paths.forEach((path33) => {
19473
+ if (!sp2.isAbsolute(path33) && !this._closers.has(path33)) {
19563
19474
  if (cwd)
19564
- path31 = sp2.join(cwd, path31);
19565
- path31 = sp2.resolve(path31);
19475
+ path33 = sp2.join(cwd, path33);
19476
+ path33 = sp2.resolve(path33);
19566
19477
  }
19567
- this._closePath(path31);
19568
- this._addIgnoredPath(path31);
19569
- if (this._watched.has(path31)) {
19478
+ this._closePath(path33);
19479
+ this._addIgnoredPath(path33);
19480
+ if (this._watched.has(path33)) {
19570
19481
  this._addIgnoredPath({
19571
- path: path31,
19482
+ path: path33,
19572
19483
  recursive: true
19573
19484
  });
19574
19485
  }
@@ -19632,38 +19543,38 @@ var FSWatcher = class extends import_node_events.EventEmitter {
19632
19543
  * @param stats arguments to be passed with event
19633
19544
  * @returns the error if defined, otherwise the value of the FSWatcher instance's `closed` flag
19634
19545
  */
19635
- async _emit(event, path31, stats) {
19546
+ async _emit(event, path33, stats) {
19636
19547
  if (this.closed)
19637
19548
  return;
19638
19549
  const opts = this.options;
19639
19550
  if (isWindows)
19640
- path31 = sp2.normalize(path31);
19551
+ path33 = sp2.normalize(path33);
19641
19552
  if (opts.cwd)
19642
- path31 = sp2.relative(opts.cwd, path31);
19643
- const args = [path31];
19553
+ path33 = sp2.relative(opts.cwd, path33);
19554
+ const args = [path33];
19644
19555
  if (stats != null)
19645
19556
  args.push(stats);
19646
19557
  const awf = opts.awaitWriteFinish;
19647
19558
  let pw;
19648
- if (awf && (pw = this._pendingWrites.get(path31))) {
19559
+ if (awf && (pw = this._pendingWrites.get(path33))) {
19649
19560
  pw.lastChange = /* @__PURE__ */ new Date();
19650
19561
  return this;
19651
19562
  }
19652
19563
  if (opts.atomic) {
19653
19564
  if (event === EVENTS.UNLINK) {
19654
- this._pendingUnlinks.set(path31, [event, ...args]);
19565
+ this._pendingUnlinks.set(path33, [event, ...args]);
19655
19566
  setTimeout(() => {
19656
- this._pendingUnlinks.forEach((entry, path32) => {
19567
+ this._pendingUnlinks.forEach((entry, path34) => {
19657
19568
  this.emit(...entry);
19658
19569
  this.emit(EVENTS.ALL, ...entry);
19659
- this._pendingUnlinks.delete(path32);
19570
+ this._pendingUnlinks.delete(path34);
19660
19571
  });
19661
19572
  }, typeof opts.atomic === "number" ? opts.atomic : 100);
19662
19573
  return this;
19663
19574
  }
19664
- if (event === EVENTS.ADD && this._pendingUnlinks.has(path31)) {
19575
+ if (event === EVENTS.ADD && this._pendingUnlinks.has(path33)) {
19665
19576
  event = EVENTS.CHANGE;
19666
- this._pendingUnlinks.delete(path31);
19577
+ this._pendingUnlinks.delete(path33);
19667
19578
  }
19668
19579
  }
19669
19580
  if (awf && (event === EVENTS.ADD || event === EVENTS.CHANGE) && this._readyEmitted) {
@@ -19681,16 +19592,16 @@ var FSWatcher = class extends import_node_events.EventEmitter {
19681
19592
  this.emitWithAll(event, args);
19682
19593
  }
19683
19594
  };
19684
- this._awaitWriteFinish(path31, awf.stabilityThreshold, event, awfEmit);
19595
+ this._awaitWriteFinish(path33, awf.stabilityThreshold, event, awfEmit);
19685
19596
  return this;
19686
19597
  }
19687
19598
  if (event === EVENTS.CHANGE) {
19688
- const isThrottled = !this._throttle(EVENTS.CHANGE, path31, 50);
19599
+ const isThrottled = !this._throttle(EVENTS.CHANGE, path33, 50);
19689
19600
  if (isThrottled)
19690
19601
  return this;
19691
19602
  }
19692
19603
  if (opts.alwaysStat && stats === void 0 && (event === EVENTS.ADD || event === EVENTS.ADD_DIR || event === EVENTS.CHANGE)) {
19693
- const fullPath = opts.cwd ? sp2.join(opts.cwd, path31) : path31;
19604
+ const fullPath = opts.cwd ? sp2.join(opts.cwd, path33) : path33;
19694
19605
  let stats2;
19695
19606
  try {
19696
19607
  stats2 = await (0, import_promises3.stat)(fullPath);
@@ -19721,23 +19632,23 @@ var FSWatcher = class extends import_node_events.EventEmitter {
19721
19632
  * @param timeout duration of time to suppress duplicate actions
19722
19633
  * @returns tracking object or false if action should be suppressed
19723
19634
  */
19724
- _throttle(actionType, path31, timeout) {
19635
+ _throttle(actionType, path33, timeout) {
19725
19636
  if (!this._throttled.has(actionType)) {
19726
19637
  this._throttled.set(actionType, /* @__PURE__ */ new Map());
19727
19638
  }
19728
19639
  const action = this._throttled.get(actionType);
19729
19640
  if (!action)
19730
19641
  throw new Error("invalid throttle");
19731
- const actionPath = action.get(path31);
19642
+ const actionPath = action.get(path33);
19732
19643
  if (actionPath) {
19733
19644
  actionPath.count++;
19734
19645
  return false;
19735
19646
  }
19736
19647
  let timeoutObject;
19737
19648
  const clear = () => {
19738
- const item = action.get(path31);
19649
+ const item = action.get(path33);
19739
19650
  const count = item ? item.count : 0;
19740
- action.delete(path31);
19651
+ action.delete(path33);
19741
19652
  clearTimeout(timeoutObject);
19742
19653
  if (item)
19743
19654
  clearTimeout(item.timeoutObject);
@@ -19745,7 +19656,7 @@ var FSWatcher = class extends import_node_events.EventEmitter {
19745
19656
  };
19746
19657
  timeoutObject = setTimeout(clear, timeout);
19747
19658
  const thr = { timeoutObject, clear, count: 0 };
19748
- action.set(path31, thr);
19659
+ action.set(path33, thr);
19749
19660
  return thr;
19750
19661
  }
19751
19662
  _incrReadyCount() {
@@ -19759,44 +19670,44 @@ var FSWatcher = class extends import_node_events.EventEmitter {
19759
19670
  * @param event
19760
19671
  * @param awfEmit Callback to be called when ready for event to be emitted.
19761
19672
  */
19762
- _awaitWriteFinish(path31, threshold, event, awfEmit) {
19673
+ _awaitWriteFinish(path33, threshold, event, awfEmit) {
19763
19674
  const awf = this.options.awaitWriteFinish;
19764
19675
  if (typeof awf !== "object")
19765
19676
  return;
19766
19677
  const pollInterval = awf.pollInterval;
19767
19678
  let timeoutHandler;
19768
- let fullPath = path31;
19769
- if (this.options.cwd && !sp2.isAbsolute(path31)) {
19770
- fullPath = sp2.join(this.options.cwd, path31);
19679
+ let fullPath = path33;
19680
+ if (this.options.cwd && !sp2.isAbsolute(path33)) {
19681
+ fullPath = sp2.join(this.options.cwd, path33);
19771
19682
  }
19772
19683
  const now2 = /* @__PURE__ */ new Date();
19773
19684
  const writes = this._pendingWrites;
19774
19685
  function awaitWriteFinishFn(prevStat) {
19775
19686
  (0, import_node_fs2.stat)(fullPath, (err, curStat) => {
19776
- if (err || !writes.has(path31)) {
19687
+ if (err || !writes.has(path33)) {
19777
19688
  if (err && err.code !== "ENOENT")
19778
19689
  awfEmit(err);
19779
19690
  return;
19780
19691
  }
19781
19692
  const now3 = Number(/* @__PURE__ */ new Date());
19782
19693
  if (prevStat && curStat.size !== prevStat.size) {
19783
- writes.get(path31).lastChange = now3;
19694
+ writes.get(path33).lastChange = now3;
19784
19695
  }
19785
- const pw = writes.get(path31);
19696
+ const pw = writes.get(path33);
19786
19697
  const df = now3 - pw.lastChange;
19787
19698
  if (df >= threshold) {
19788
- writes.delete(path31);
19699
+ writes.delete(path33);
19789
19700
  awfEmit(void 0, curStat);
19790
19701
  } else {
19791
19702
  timeoutHandler = setTimeout(awaitWriteFinishFn, pollInterval, curStat);
19792
19703
  }
19793
19704
  });
19794
19705
  }
19795
- if (!writes.has(path31)) {
19796
- writes.set(path31, {
19706
+ if (!writes.has(path33)) {
19707
+ writes.set(path33, {
19797
19708
  lastChange: now2,
19798
19709
  cancelWait: () => {
19799
- writes.delete(path31);
19710
+ writes.delete(path33);
19800
19711
  clearTimeout(timeoutHandler);
19801
19712
  return event;
19802
19713
  }
@@ -19807,8 +19718,8 @@ var FSWatcher = class extends import_node_events.EventEmitter {
19807
19718
  /**
19808
19719
  * Determines whether user has asked to ignore this path.
19809
19720
  */
19810
- _isIgnored(path31, stats) {
19811
- if (this.options.atomic && DOT_RE.test(path31))
19721
+ _isIgnored(path33, stats) {
19722
+ if (this.options.atomic && DOT_RE.test(path33))
19812
19723
  return true;
19813
19724
  if (!this._userIgnored) {
19814
19725
  const { cwd } = this.options;
@@ -19818,17 +19729,17 @@ var FSWatcher = class extends import_node_events.EventEmitter {
19818
19729
  const list = [...ignoredPaths.map(normalizeIgnored(cwd)), ...ignored];
19819
19730
  this._userIgnored = anymatch(list, void 0);
19820
19731
  }
19821
- return this._userIgnored(path31, stats);
19732
+ return this._userIgnored(path33, stats);
19822
19733
  }
19823
- _isntIgnored(path31, stat4) {
19824
- return !this._isIgnored(path31, stat4);
19734
+ _isntIgnored(path33, stat5) {
19735
+ return !this._isIgnored(path33, stat5);
19825
19736
  }
19826
19737
  /**
19827
19738
  * Provides a set of common helpers and properties relating to symlink handling.
19828
19739
  * @param path file or directory pattern being watched
19829
19740
  */
19830
- _getWatchHelpers(path31) {
19831
- return new WatchHelper(path31, this.options.followSymlinks, this);
19741
+ _getWatchHelpers(path33) {
19742
+ return new WatchHelper(path33, this.options.followSymlinks, this);
19832
19743
  }
19833
19744
  // Directory helpers
19834
19745
  // -----------------
@@ -19860,63 +19771,63 @@ var FSWatcher = class extends import_node_events.EventEmitter {
19860
19771
  * @param item base path of item/directory
19861
19772
  */
19862
19773
  _remove(directory, item, isDirectory) {
19863
- const path31 = sp2.join(directory, item);
19864
- const fullPath = sp2.resolve(path31);
19865
- isDirectory = isDirectory != null ? isDirectory : this._watched.has(path31) || this._watched.has(fullPath);
19866
- if (!this._throttle("remove", path31, 100))
19774
+ const path33 = sp2.join(directory, item);
19775
+ const fullPath = sp2.resolve(path33);
19776
+ isDirectory = isDirectory != null ? isDirectory : this._watched.has(path33) || this._watched.has(fullPath);
19777
+ if (!this._throttle("remove", path33, 100))
19867
19778
  return;
19868
19779
  if (!isDirectory && this._watched.size === 1) {
19869
19780
  this.add(directory, item, true);
19870
19781
  }
19871
- const wp = this._getWatchedDir(path31);
19782
+ const wp = this._getWatchedDir(path33);
19872
19783
  const nestedDirectoryChildren = wp.getChildren();
19873
- nestedDirectoryChildren.forEach((nested) => this._remove(path31, nested));
19784
+ nestedDirectoryChildren.forEach((nested) => this._remove(path33, nested));
19874
19785
  const parent = this._getWatchedDir(directory);
19875
19786
  const wasTracked = parent.has(item);
19876
19787
  parent.remove(item);
19877
19788
  if (this._symlinkPaths.has(fullPath)) {
19878
19789
  this._symlinkPaths.delete(fullPath);
19879
19790
  }
19880
- let relPath = path31;
19791
+ let relPath = path33;
19881
19792
  if (this.options.cwd)
19882
- relPath = sp2.relative(this.options.cwd, path31);
19793
+ relPath = sp2.relative(this.options.cwd, path33);
19883
19794
  if (this.options.awaitWriteFinish && this._pendingWrites.has(relPath)) {
19884
19795
  const event = this._pendingWrites.get(relPath).cancelWait();
19885
19796
  if (event === EVENTS.ADD)
19886
19797
  return;
19887
19798
  }
19888
- this._watched.delete(path31);
19799
+ this._watched.delete(path33);
19889
19800
  this._watched.delete(fullPath);
19890
19801
  const eventName = isDirectory ? EVENTS.UNLINK_DIR : EVENTS.UNLINK;
19891
- if (wasTracked && !this._isIgnored(path31))
19892
- this._emit(eventName, path31);
19893
- this._closePath(path31);
19802
+ if (wasTracked && !this._isIgnored(path33))
19803
+ this._emit(eventName, path33);
19804
+ this._closePath(path33);
19894
19805
  }
19895
19806
  /**
19896
19807
  * Closes all watchers for a path
19897
19808
  */
19898
- _closePath(path31) {
19899
- this._closeFile(path31);
19900
- const dir = sp2.dirname(path31);
19901
- this._getWatchedDir(dir).remove(sp2.basename(path31));
19809
+ _closePath(path33) {
19810
+ this._closeFile(path33);
19811
+ const dir = sp2.dirname(path33);
19812
+ this._getWatchedDir(dir).remove(sp2.basename(path33));
19902
19813
  }
19903
19814
  /**
19904
19815
  * Closes only file-specific watchers
19905
19816
  */
19906
- _closeFile(path31) {
19907
- const closers = this._closers.get(path31);
19817
+ _closeFile(path33) {
19818
+ const closers = this._closers.get(path33);
19908
19819
  if (!closers)
19909
19820
  return;
19910
19821
  closers.forEach((closer) => closer());
19911
- this._closers.delete(path31);
19822
+ this._closers.delete(path33);
19912
19823
  }
19913
- _addPathCloser(path31, closer) {
19824
+ _addPathCloser(path33, closer) {
19914
19825
  if (!closer)
19915
19826
  return;
19916
- let list = this._closers.get(path31);
19827
+ let list = this._closers.get(path33);
19917
19828
  if (!list) {
19918
19829
  list = [];
19919
- this._closers.set(path31, list);
19830
+ this._closers.set(path33, list);
19920
19831
  }
19921
19832
  list.push(closer);
19922
19833
  }
@@ -19946,12 +19857,291 @@ function watch(paths, options = {}) {
19946
19857
  var chokidar_default = { watch, FSWatcher };
19947
19858
 
19948
19859
  // src/watcher/file-watcher.ts
19860
+ var path28 = __toESM(require("path"), 1);
19861
+
19862
+ // src/watcher/native-recursive-watcher.ts
19863
+ var import_node_fs3 = require("fs");
19949
19864
  var path26 = __toESM(require("path"), 1);
19865
+ var NativeRecursiveWatcher = class {
19866
+ constructor(root, onChange, options = {}) {
19867
+ this.root = root;
19868
+ this.onChange = onChange;
19869
+ this.watchFactory = options.watchFactory ?? this.defaultWatchFactory;
19870
+ this.onError = options.onError;
19871
+ }
19872
+ root;
19873
+ onChange;
19874
+ watcher = null;
19875
+ listenerToken = 0;
19876
+ watchFactory;
19877
+ onError;
19878
+ start() {
19879
+ if (this.watcher) return;
19880
+ const token = ++this.listenerToken;
19881
+ const listener = (_eventType, filename) => {
19882
+ if (this.watcher === null || this.listenerToken !== token) return;
19883
+ const absolutePath = this.toAbsolutePath(filename);
19884
+ const nextResult = this.onChange(absolutePath);
19885
+ if (nextResult instanceof Promise) {
19886
+ void nextResult.catch((error) => {
19887
+ console.error("[codebase-index] Error handling native watcher event:", error);
19888
+ });
19889
+ }
19890
+ };
19891
+ const watcher = this.watchFactory(this.root, listener, {
19892
+ persistent: true,
19893
+ recursive: true
19894
+ });
19895
+ watcher.on?.("error", (error) => {
19896
+ if (this.watcher === watcher && this.listenerToken === token) {
19897
+ this.onError?.(error);
19898
+ }
19899
+ });
19900
+ this.watcher = watcher;
19901
+ }
19902
+ async stop() {
19903
+ const watcher = this.watcher;
19904
+ this.watcher = null;
19905
+ this.listenerToken += 1;
19906
+ if (!watcher) return;
19907
+ await watcher.close();
19908
+ }
19909
+ toAbsolutePath(filename) {
19910
+ if (filename == null) return null;
19911
+ const normalizedFilename = typeof filename === "string" ? filename : filename.toString();
19912
+ const absolutePath = path26.resolve(this.root, normalizedFilename);
19913
+ const relativePath = path26.relative(this.root, absolutePath);
19914
+ const outsideRoot = relativePath === ".." || relativePath.startsWith(`..${path26.sep}`) || path26.isAbsolute(relativePath);
19915
+ return outsideRoot ? null : absolutePath;
19916
+ }
19917
+ defaultWatchFactory = (root, listener, options) => (0, import_node_fs3.watch)(root, options, listener);
19918
+ };
19919
+
19920
+ // src/watcher/snapshot.ts
19921
+ var fsPromises4 = __toESM(require("fs/promises"), 1);
19922
+ var path27 = __toESM(require("path"), 1);
19923
+ async function buildFileSnapshotScan(projectRoot, config, configPaths = []) {
19924
+ const normalizedProjectRoot = path27.resolve(projectRoot);
19925
+ const ignoreFilter = createIgnoreFilter(normalizedProjectRoot);
19926
+ const includePatterns = [...config.include, ...config.additionalInclude ?? []];
19927
+ const maxDepth = config.indexing?.maxDepth ?? -1;
19928
+ const snapshot = /* @__PURE__ */ new Map();
19929
+ const unreadablePrefixes = /* @__PURE__ */ new Set();
19930
+ const includeFile = async (filePath) => {
19931
+ const normalizedPath3 = path27.resolve(filePath);
19932
+ if (!shouldIncludeFile(normalizedPath3, normalizedProjectRoot, includePatterns, config.exclude, ignoreFilter)) return;
19933
+ const stat5 = await readStatIfFile(normalizedPath3, unreadablePrefixes);
19934
+ if (stat5) snapshot.set(normalizedPath3, { size: stat5.size, mtimeMs: stat5.mtimeMs });
19935
+ };
19936
+ const walk = async (directoryPath, depth) => {
19937
+ let entries;
19938
+ try {
19939
+ entries = await fsPromises4.readdir(directoryPath, { withFileTypes: true });
19940
+ } catch (error) {
19941
+ if (isMissingFsError(error)) return;
19942
+ if (isPermissionFsError(error)) {
19943
+ unreadablePrefixes.add(path27.resolve(directoryPath));
19944
+ return;
19945
+ }
19946
+ throw error;
19947
+ }
19948
+ for (const entry of entries) {
19949
+ const fullPath = path27.join(directoryPath, entry.name);
19950
+ const relativePath = path27.relative(normalizedProjectRoot, fullPath);
19951
+ if (entry.isDirectory()) {
19952
+ if (hasFilteredPathSegment(relativePath, path27.sep) || isRestrictedDirectory(relativePath, path27.sep)) continue;
19953
+ if (ignoreFilter.ignores(relativePath)) continue;
19954
+ if (maxDepth === -1 || depth < maxDepth) await walk(fullPath, depth + 1);
19955
+ } else if (entry.isFile()) {
19956
+ await includeFile(fullPath);
19957
+ }
19958
+ }
19959
+ };
19960
+ await walk(normalizedProjectRoot, 0);
19961
+ await includeExplicitConfigPaths(snapshot, unreadablePrefixes, configPaths);
19962
+ return { entries: snapshot, unreadablePrefixes };
19963
+ }
19964
+ async function buildFileSnapshotForPathScan(projectRoot, config, configPaths, targetPath) {
19965
+ const normalizedProjectRoot = path27.resolve(projectRoot);
19966
+ const normalizedTargetPath = path27.resolve(targetPath);
19967
+ if (!isWithinPath(normalizedProjectRoot, normalizedTargetPath)) {
19968
+ return { entries: /* @__PURE__ */ new Map(), unreadablePrefixes: /* @__PURE__ */ new Set() };
19969
+ }
19970
+ const ignoreFilter = createIgnoreFilter(normalizedProjectRoot);
19971
+ const includePatterns = [...config.include, ...config.additionalInclude ?? []];
19972
+ const maxDepth = config.indexing?.maxDepth ?? -1;
19973
+ const explicitConfigPaths = new Set(configPaths.map((configPath) => path27.resolve(configPath)));
19974
+ const snapshot = /* @__PURE__ */ new Map();
19975
+ const unreadablePrefixes = /* @__PURE__ */ new Set();
19976
+ const includeFile = async (filePath) => {
19977
+ const normalizedPath3 = path27.resolve(filePath);
19978
+ if (!explicitConfigPaths.has(normalizedPath3) && !shouldIncludeFile(
19979
+ normalizedPath3,
19980
+ normalizedProjectRoot,
19981
+ includePatterns,
19982
+ config.exclude,
19983
+ ignoreFilter
19984
+ )) return;
19985
+ const stat5 = await readStatIfFile(normalizedPath3, unreadablePrefixes);
19986
+ if (stat5) snapshot.set(normalizedPath3, { size: stat5.size, mtimeMs: stat5.mtimeMs });
19987
+ };
19988
+ const walk = async (directoryPath, depth) => {
19989
+ let entries;
19990
+ try {
19991
+ entries = await fsPromises4.readdir(directoryPath, { withFileTypes: true });
19992
+ } catch (error) {
19993
+ if (isMissingFsError(error)) return;
19994
+ if (isPermissionFsError(error)) {
19995
+ unreadablePrefixes.add(path27.resolve(directoryPath));
19996
+ return;
19997
+ }
19998
+ throw error;
19999
+ }
20000
+ for (const entry of entries) {
20001
+ const fullPath = path27.join(directoryPath, entry.name);
20002
+ const relativePath = path27.relative(normalizedProjectRoot, fullPath);
20003
+ if (entry.isDirectory()) {
20004
+ if (hasFilteredPathSegment(relativePath, path27.sep) || isRestrictedDirectory(relativePath, path27.sep)) continue;
20005
+ if (ignoreFilter.ignores(relativePath)) continue;
20006
+ if (maxDepth === -1 || depth < maxDepth) await walk(fullPath, depth + 1);
20007
+ } else if (entry.isFile()) {
20008
+ await includeFile(fullPath);
20009
+ }
20010
+ }
20011
+ };
20012
+ const targetStat = await readStatIfFile(normalizedTargetPath, unreadablePrefixes);
20013
+ if (targetStat) await includeFile(normalizedTargetPath);
20014
+ else await walk(normalizedTargetPath, 0);
20015
+ await includeExplicitConfigPathsInPath(snapshot, unreadablePrefixes, configPaths, normalizedTargetPath);
20016
+ return { entries: snapshot, unreadablePrefixes };
20017
+ }
20018
+ function completeFileSnapshot(previous, scan) {
20019
+ const completed = new Map(scan.entries);
20020
+ for (const unreadablePrefix of scan.unreadablePrefixes) {
20021
+ for (const [entryPath, entry] of previous) {
20022
+ if (isWithinPath(unreadablePrefix, entryPath) && !completed.has(entryPath)) completed.set(entryPath, entry);
20023
+ }
20024
+ }
20025
+ return completed;
20026
+ }
20027
+ async function includeExplicitConfigPaths(snapshot, unreadablePrefixes, configPaths) {
20028
+ for (const configPath of [...new Set(configPaths.map((value) => path27.resolve(value)))]) {
20029
+ if (snapshot.has(configPath)) continue;
20030
+ const stat5 = await readStatIfFile(configPath, unreadablePrefixes);
20031
+ if (stat5) snapshot.set(configPath, { size: stat5.size, mtimeMs: stat5.mtimeMs });
20032
+ }
20033
+ }
20034
+ async function includeExplicitConfigPathsInPath(snapshot, unreadablePrefixes, configPaths, targetPath) {
20035
+ await includeExplicitConfigPaths(
20036
+ snapshot,
20037
+ unreadablePrefixes,
20038
+ configPaths.filter((configPath) => isWithinPath(targetPath, path27.resolve(configPath)))
20039
+ );
20040
+ }
20041
+ function isWithinPath(parentPath, childPath) {
20042
+ const relativePath = path27.relative(parentPath, childPath);
20043
+ return relativePath === "" || !relativePath.startsWith(`..${path27.sep}`) && relativePath !== ".." && !path27.isAbsolute(relativePath);
20044
+ }
20045
+ async function readStatIfFile(filePath, unreadablePrefixes) {
20046
+ try {
20047
+ const stat5 = await fsPromises4.stat(filePath);
20048
+ return stat5.isFile() ? stat5 : null;
20049
+ } catch (error) {
20050
+ if (isMissingFsError(error)) return null;
20051
+ if (isPermissionFsError(error)) {
20052
+ unreadablePrefixes.add(path27.resolve(filePath));
20053
+ return null;
20054
+ }
20055
+ throw error;
20056
+ }
20057
+ }
20058
+ function isMissingFsError(error) {
20059
+ return error instanceof Error && ["ENOENT", "ENOTDIR"].includes(error.code ?? "");
20060
+ }
20061
+ function isPermissionFsError(error) {
20062
+ return error instanceof Error && ["EACCES", "EPERM"].includes(error.code ?? "");
20063
+ }
20064
+ var diffTypeOrder = { add: 0, change: 1, unlink: 2 };
20065
+ function diffFileSnapshots(previous, current, forcedChanges = /* @__PURE__ */ new Set()) {
20066
+ const changes = [];
20067
+ for (const [filePath, previousEntry] of previous) {
20068
+ const currentEntry = current.get(filePath);
20069
+ if (!currentEntry) changes.push({ type: "unlink", path: filePath });
20070
+ else if (forcedChanges.has(filePath) || currentEntry.size !== previousEntry.size || currentEntry.mtimeMs !== previousEntry.mtimeMs) {
20071
+ changes.push({ type: "change", path: filePath });
20072
+ }
20073
+ }
20074
+ for (const [filePath] of current) {
20075
+ if (!previous.has(filePath)) changes.push({ type: "add", path: filePath });
20076
+ }
20077
+ return changes.sort((left, right) => left.path.localeCompare(right.path) || diffTypeOrder[left.type] - diffTypeOrder[right.type]);
20078
+ }
20079
+
20080
+ // src/watcher/snapshot-reconciler.ts
20081
+ var FileSnapshotReconciler = class {
20082
+ constructor(projectRoot, config, configPaths) {
20083
+ this.projectRoot = projectRoot;
20084
+ this.config = config;
20085
+ this.configPaths = configPaths;
20086
+ }
20087
+ projectRoot;
20088
+ config;
20089
+ configPaths;
20090
+ snapshot = null;
20091
+ reconciliationTail = Promise.resolve();
20092
+ async initialize() {
20093
+ this.snapshot = (await buildFileSnapshotScan(this.projectRoot, this.config, this.configPaths)).entries;
20094
+ }
20095
+ async reconcile(invalidations = []) {
20096
+ if (this.snapshot === null) {
20097
+ throw new Error("FileSnapshotReconciler is not initialized. Call initialize() before reconcile().");
20098
+ }
20099
+ const reconciliation = this.reconciliationTail.then(async () => {
20100
+ const previousSnapshot = this.snapshot;
20101
+ if (previousSnapshot === null) {
20102
+ throw new Error("FileSnapshotReconciler is not initialized. Call initialize() before reconcile().");
20103
+ }
20104
+ const normalizedInvalidations = invalidations.map((invalidation) => typeof invalidation === "string" || invalidation === null ? { path: invalidation, forceChange: false } : { path: invalidation.path, forceChange: invalidation.forceChange === true });
20105
+ const scopedPaths = normalizedInvalidations.map((invalidation) => invalidation.path).filter((filePath) => filePath !== null);
20106
+ const scan = scopedPaths.length === 0 || scopedPaths.length !== normalizedInvalidations.length ? await buildFileSnapshotScan(this.projectRoot, this.config, this.configPaths) : await this.reconcilePaths(previousSnapshot, scopedPaths);
20107
+ const nextSnapshot = completeFileSnapshot(previousSnapshot, scan);
20108
+ const forcedChanges = new Set(normalizedInvalidations.filter((invalidation) => invalidation.path !== null && invalidation.forceChange).map((invalidation) => invalidation.path));
20109
+ const changes = diffFileSnapshots(previousSnapshot, nextSnapshot, forcedChanges);
20110
+ this.snapshot = nextSnapshot;
20111
+ return changes;
20112
+ });
20113
+ this.reconciliationTail = reconciliation.then(() => void 0, () => void 0);
20114
+ return reconciliation;
20115
+ }
20116
+ async reconcilePaths(previousSnapshot, invalidatedPaths) {
20117
+ const scopes = this.getScopes(invalidatedPaths);
20118
+ const entries = new Map(previousSnapshot);
20119
+ const unreadablePrefixes = /* @__PURE__ */ new Set();
20120
+ for (const scope of scopes) {
20121
+ for (const previousPath of entries.keys()) {
20122
+ if (isWithinPath(scope, previousPath)) entries.delete(previousPath);
20123
+ }
20124
+ const scopedScan = await buildFileSnapshotForPathScan(this.projectRoot, this.config, this.configPaths, scope);
20125
+ for (const [filePath, entry] of scopedScan.entries) entries.set(filePath, entry);
20126
+ for (const unreadablePrefix of scopedScan.unreadablePrefixes) unreadablePrefixes.add(unreadablePrefix);
20127
+ }
20128
+ return { entries, unreadablePrefixes };
20129
+ }
20130
+ getScopes(invalidatedPaths) {
20131
+ const uniquePaths = [...new Set(invalidatedPaths)].sort((left, right) => left.length - right.length);
20132
+ return uniquePaths.filter((candidate, index) => !uniquePaths.slice(0, index).some(
20133
+ (ancestor) => isWithinPath(ancestor, candidate)
20134
+ ));
20135
+ }
20136
+ };
20137
+
20138
+ // src/watcher/file-watcher.ts
19950
20139
  var FileWatcher = class {
19951
20140
  watcher = null;
19952
20141
  projectRoot;
19953
20142
  config;
19954
20143
  configPath;
20144
+ backend;
19955
20145
  projectConfigPaths;
19956
20146
  pendingChanges = /* @__PURE__ */ new Map();
19957
20147
  debounceTimer = null;
@@ -19961,44 +20151,74 @@ var FileWatcher = class {
19961
20151
  resolveReady = null;
19962
20152
  pollingFallbackAttempted = false;
19963
20153
  pendingClose = null;
20154
+ startupReadySignals = 1;
20155
+ nativeWatcher = null;
20156
+ nativeReconciler = null;
20157
+ nativeSetupGeneration = 0;
20158
+ nativeStarting = false;
20159
+ nativeInitializing = false;
20160
+ nativeReconcileTimer = null;
20161
+ nativeInvalidatedPaths = /* @__PURE__ */ new Map();
20162
+ configPathStates = /* @__PURE__ */ new Map();
19964
20163
  constructor(projectRoot, config, host, options = {}) {
19965
20164
  this.projectRoot = projectRoot;
19966
20165
  this.config = config;
20166
+ this.backend = options.backend ?? "auto";
19967
20167
  this.configPath = options.configPath;
19968
20168
  this.projectConfigPaths = options.configPath ? [options.configPath] : getProjectConfigCandidatePaths(projectRoot, host);
19969
20169
  }
19970
20170
  start(handler) {
19971
- if (this.watcher) {
20171
+ if (this.watcher || this.nativeWatcher || this.nativeStarting) {
19972
20172
  return;
19973
20173
  }
19974
20174
  this.onChanges = handler;
19975
20175
  this.pollingFallbackAttempted = false;
19976
20176
  this.resetReady();
20177
+ if (this.shouldUseNativeWatcher()) {
20178
+ if (this.hasExternalConfigWatchTarget()) {
20179
+ this.setStartupReadySignals(2);
20180
+ this.startExternalConfigWatcher();
20181
+ }
20182
+ this.nativeStarting = true;
20183
+ void this.createNativeWatcher();
20184
+ return;
20185
+ }
19977
20186
  this.createWatcher();
19978
20187
  }
19979
20188
  resetReady() {
19980
- this.readyPromise = new Promise((resolve18) => {
19981
- this.resolveReady = resolve18;
20189
+ this.readyPromise = new Promise((resolve20) => {
20190
+ this.resolveReady = resolve20;
19982
20191
  });
20192
+ this.startupReadySignals = 1;
19983
20193
  }
19984
- createWatcher(usePolling = false) {
19985
- const ignoreFilter = createIgnoreFilter(this.projectRoot);
19986
- let watchTargets = this.projectRoot;
19987
- if (this.configPath) {
19988
- watchTargets = [this.projectRoot, this.configPath];
19989
- } else {
19990
- const externalConfigTargets = this.projectConfigPaths.filter((projectConfigPath) => {
19991
- const relativeConfigPath = path26.relative(this.projectRoot, projectConfigPath);
19992
- return this.isOutsideProjectPath(relativeConfigPath);
19993
- }).map((projectConfigPath) => (0, import_fs19.existsSync)(projectConfigPath) ? projectConfigPath : this.getNearestExistingDirectory(path26.dirname(projectConfigPath)));
19994
- const uniqueExternalConfigTargets = [...new Set(externalConfigTargets)];
19995
- if (uniqueExternalConfigTargets.length > 0) {
19996
- watchTargets = [this.projectRoot, ...uniqueExternalConfigTargets];
19997
- }
20194
+ setStartupReadySignals(expectedSignals) {
20195
+ if (!this.readyPromise) {
20196
+ return;
20197
+ }
20198
+ this.startupReadySignals = Math.max(0, expectedSignals);
20199
+ }
20200
+ reportStartupReadySignal() {
20201
+ if (!this.readyPromise || !this.resolveReady) {
20202
+ return;
20203
+ }
20204
+ if (this.startupReadySignals <= 0) {
20205
+ return;
19998
20206
  }
20207
+ this.startupReadySignals -= 1;
20208
+ if (this.startupReadySignals !== 0) {
20209
+ return;
20210
+ }
20211
+ this.resolveReady();
20212
+ this.resolveReady = null;
20213
+ }
20214
+ createWatcher(watchTargets, usePolling = false, reportsStartupReady = true) {
20215
+ let reportedStartupReady = false;
20216
+ this.configPathStates = this.getConfigPathStates();
20217
+ const ignoreFilter = createIgnoreFilter(this.projectRoot);
20218
+ const resolvedWatchTargets = watchTargets ?? this.getFullChokidarWatchTargets();
19999
20219
  const watcherOptions = {
20000
20220
  ignored: (filePath) => {
20001
- const relativePath = path26.relative(this.projectRoot, filePath);
20221
+ const relativePath = path28.relative(this.projectRoot, filePath);
20002
20222
  if (!relativePath) return false;
20003
20223
  if (this.isProjectConfigPathOrAncestor(relativePath)) {
20004
20224
  return false;
@@ -20006,10 +20226,10 @@ var FileWatcher = class {
20006
20226
  if (this.isOutsideProjectPath(relativePath)) {
20007
20227
  return true;
20008
20228
  }
20009
- if (hasFilteredPathSegment(relativePath, path26.sep)) {
20229
+ if (hasFilteredPathSegment(relativePath, path28.sep)) {
20010
20230
  return true;
20011
20231
  }
20012
- if (isRestrictedDirectory(relativePath, path26.sep)) {
20232
+ if (isRestrictedDirectory(relativePath, path28.sep)) {
20013
20233
  return true;
20014
20234
  }
20015
20235
  if (ignoreFilter.ignores(relativePath)) {
@@ -20042,10 +20262,13 @@ var FileWatcher = class {
20042
20262
  watcher = new FSWatcher(watcherOptions);
20043
20263
  }
20044
20264
  this.watcher = watcher;
20045
- watcher.once("ready", () => {
20265
+ watcher.on("ready", () => {
20046
20266
  if (this.watcher !== watcher) return;
20047
- this.resolveReady?.();
20048
- this.resolveReady = null;
20267
+ this.reconcileConfigPathStates();
20268
+ if (reportsStartupReady) {
20269
+ this.reportStartupReadySignal();
20270
+ reportedStartupReady = true;
20271
+ }
20049
20272
  });
20050
20273
  watcher.on("error", (error) => {
20051
20274
  const err = error instanceof Error ? error : null;
@@ -20059,10 +20282,13 @@ var FileWatcher = class {
20059
20282
  console.error("[codebase-index] Failed to close exhausted file watcher:", closeError);
20060
20283
  });
20061
20284
  if (this.onChanges) {
20285
+ const replacementReportsStartupReady = reportsStartupReady || reportedStartupReady;
20062
20286
  if (!this.resolveReady) {
20063
20287
  this.resetReady();
20288
+ } else if (reportedStartupReady) {
20289
+ this.startupReadySignals += 1;
20064
20290
  }
20065
- this.createWatcher(true);
20291
+ this.createWatcher(resolvedWatchTargets, true, replacementReportsStartupReady);
20066
20292
  } else {
20067
20293
  this.watcher = null;
20068
20294
  }
@@ -20073,13 +20299,166 @@ var FileWatcher = class {
20073
20299
  watcher.on("add", (filePath) => this.handleChange(watcher, "add", filePath));
20074
20300
  watcher.on("change", (filePath) => this.handleChange(watcher, "change", filePath));
20075
20301
  watcher.on("unlink", (filePath) => this.handleChange(watcher, "unlink", filePath));
20076
- watcher.add(watchTargets);
20302
+ watcher.add(resolvedWatchTargets);
20303
+ }
20304
+ shouldUseNativeWatcher() {
20305
+ if (this.backend === "chokidar") {
20306
+ return false;
20307
+ }
20308
+ return true;
20309
+ }
20310
+ getFullChokidarWatchTargets() {
20311
+ if (this.configPath) {
20312
+ return [this.projectRoot, this.configPath];
20313
+ }
20314
+ const externalConfigTargets = this.getExternalConfigWatchTargets();
20315
+ if (externalConfigTargets.length === 0) {
20316
+ return this.projectRoot;
20317
+ }
20318
+ return [this.projectRoot, ...externalConfigTargets];
20319
+ }
20320
+ getExternalConfigWatchTargets() {
20321
+ return [...new Set(
20322
+ this.projectConfigPaths.filter((projectConfigPath) => {
20323
+ const relativeConfigPath = path28.relative(this.projectRoot, projectConfigPath);
20324
+ return this.isOutsideProjectPath(relativeConfigPath);
20325
+ }).map((projectConfigPath) => {
20326
+ if ((0, import_fs19.existsSync)(projectConfigPath)) {
20327
+ return projectConfigPath;
20328
+ }
20329
+ return this.getNearestExistingDirectory(path28.dirname(projectConfigPath));
20330
+ })
20331
+ )];
20332
+ }
20333
+ hasExternalConfigWatchTarget() {
20334
+ return this.getExternalConfigWatchTargets().length > 0;
20335
+ }
20336
+ startExternalConfigWatcher(usePolling = false) {
20337
+ const externalTargets = this.getExternalConfigWatchTargets();
20338
+ if (externalTargets.length === 0) {
20339
+ return;
20340
+ }
20341
+ this.createWatcher(externalTargets, usePolling);
20342
+ }
20343
+ async createNativeWatcher() {
20344
+ const generation = ++this.nativeSetupGeneration;
20345
+ const reconciler = new FileSnapshotReconciler(this.projectRoot, this.config, this.projectConfigPaths);
20346
+ const watcher = new NativeRecursiveWatcher(
20347
+ this.projectRoot,
20348
+ (filePath) => this.scheduleNativeReconciliation(generation, filePath),
20349
+ { onError: (error) => void this.fallbackFromNativeWatcher(generation, error) }
20350
+ );
20351
+ this.nativeReconciler = reconciler;
20352
+ this.nativeWatcher = watcher;
20353
+ this.nativeInitializing = true;
20354
+ try {
20355
+ watcher.start();
20356
+ if (!this.isCurrentNativeSetup(generation)) {
20357
+ await watcher.stop();
20358
+ return;
20359
+ }
20360
+ await reconciler.initialize();
20361
+ if (!this.isCurrentNativeSetup(generation) || this.nativeWatcher !== watcher) {
20362
+ await watcher.stop();
20363
+ return;
20364
+ }
20365
+ this.nativeStarting = false;
20366
+ this.nativeInitializing = false;
20367
+ await this.reconcileNativeWatcherWithPendingInvalidations(generation);
20368
+ this.reportStartupReadySignal();
20369
+ } catch (error) {
20370
+ if (!this.isCurrentNativeSetup(generation)) return;
20371
+ this.nativeInitializing = false;
20372
+ if (this.nativeWatcher) {
20373
+ await this.fallbackFromNativeWatcher(generation, error);
20374
+ return;
20375
+ }
20376
+ this.nativeStarting = false;
20377
+ const externalWatcher = this.watcher;
20378
+ this.watcher = null;
20379
+ this.nativeReconciler = null;
20380
+ await externalWatcher?.close();
20381
+ console.warn("[codebase-index] Native recursive watcher unavailable; using Chokidar fallback.", error);
20382
+ this.setStartupReadySignals(1);
20383
+ this.createWatcher();
20384
+ }
20385
+ }
20386
+ isCurrentNativeSetup(generation) {
20387
+ return this.nativeSetupGeneration === generation && this.onChanges !== null;
20388
+ }
20389
+ scheduleNativeReconciliation(generation, filePath) {
20390
+ if (!this.isCurrentNativeSetup(generation)) return;
20391
+ const requiresFullReconciliation = filePath === path28.join(this.projectRoot, ".gitignore");
20392
+ const invalidatedPath = requiresFullReconciliation ? null : filePath;
20393
+ this.nativeInvalidatedPaths.set(invalidatedPath, invalidatedPath !== null);
20394
+ if (this.nativeReconcileTimer) {
20395
+ clearTimeout(this.nativeReconcileTimer);
20396
+ }
20397
+ this.nativeReconcileTimer = setTimeout(() => {
20398
+ this.nativeReconcileTimer = null;
20399
+ void this.reconcileNativeWatcherFromQueue(generation);
20400
+ }, 100);
20401
+ }
20402
+ reconcileNativeWatcherFromQueue(generation) {
20403
+ if (!this.isCurrentNativeSetup(generation) || this.nativeInitializing) return;
20404
+ const invalidatedPaths = this.popNativeInvalidations();
20405
+ if (invalidatedPaths.length === 0) return;
20406
+ void this.reconcileNativeWatcher(generation, invalidatedPaths);
20407
+ }
20408
+ async reconcileNativeWatcher(generation, invalidatedPaths) {
20409
+ if (!this.isCurrentNativeSetup(generation) || !this.nativeReconciler) return;
20410
+ try {
20411
+ const reconciler = this.nativeReconciler;
20412
+ const changes = await reconciler.reconcile(invalidatedPaths);
20413
+ if (!this.isCurrentNativeSetup(generation) || this.nativeReconciler !== reconciler) return;
20414
+ this.recordChanges(changes);
20415
+ } catch (error) {
20416
+ await this.fallbackFromNativeWatcher(generation, error);
20417
+ }
20418
+ }
20419
+ async reconcileNativeWatcherWithPendingInvalidations(generation) {
20420
+ const invalidatedPaths = this.popNativeInvalidations();
20421
+ if (invalidatedPaths.length === 0) return;
20422
+ await this.reconcileNativeWatcher(generation, invalidatedPaths);
20423
+ }
20424
+ popNativeInvalidations() {
20425
+ if (this.nativeInvalidatedPaths.size === 0) return [];
20426
+ const invalidations = [...this.nativeInvalidatedPaths].map(([invalidatedPath, forceChange]) => ({
20427
+ path: invalidatedPath,
20428
+ forceChange
20429
+ }));
20430
+ this.nativeInvalidatedPaths.clear();
20431
+ return invalidations;
20432
+ }
20433
+ async fallbackFromNativeWatcher(generation, error) {
20434
+ if (!this.isCurrentNativeSetup(generation)) return;
20435
+ const watcher = this.nativeWatcher;
20436
+ const externalWatcher = this.watcher;
20437
+ this.nativeWatcher = null;
20438
+ this.watcher = null;
20439
+ this.nativeReconciler = null;
20440
+ this.nativeStarting = false;
20441
+ this.nativeInitializing = false;
20442
+ this.nativeSetupGeneration += 1;
20443
+ if (this.nativeReconcileTimer) {
20444
+ clearTimeout(this.nativeReconcileTimer);
20445
+ this.nativeReconcileTimer = null;
20446
+ }
20447
+ this.nativeInvalidatedPaths.clear();
20448
+ this.setStartupReadySignals(1);
20449
+ console.warn("[codebase-index] Native recursive watcher failed; using Chokidar fallback.", error);
20450
+ await watcher?.stop();
20451
+ await externalWatcher?.close();
20452
+ if (this.onChanges) {
20453
+ this.createWatcher();
20454
+ }
20077
20455
  }
20078
20456
  handleChange(watcher, type, filePath) {
20079
20457
  if (this.watcher !== watcher) {
20080
20458
  return;
20081
20459
  }
20082
20460
  if (this.isProjectConfigPath(filePath)) {
20461
+ this.updateConfigPathState(filePath);
20083
20462
  this.pendingChanges.set(filePath, type);
20084
20463
  this.scheduleFlush();
20085
20464
  return;
@@ -20094,27 +20473,33 @@ var FileWatcher = class {
20094
20473
  )) {
20095
20474
  return;
20096
20475
  }
20097
- this.pendingChanges.set(filePath, type);
20476
+ this.recordChanges([{ path: filePath, type }]);
20477
+ }
20478
+ recordChanges(changes) {
20479
+ if (changes.length === 0) return;
20480
+ for (const change of changes) {
20481
+ this.pendingChanges.set(change.path, change.type);
20482
+ }
20098
20483
  this.scheduleFlush();
20099
20484
  }
20100
20485
  isProjectConfigPath(filePath) {
20101
- const relativePath = path26.relative(this.projectRoot, filePath);
20102
- const normalizedRelativePath = path26.normalize(relativePath);
20486
+ const relativePath = path28.relative(this.projectRoot, filePath);
20487
+ const normalizedRelativePath = path28.normalize(relativePath);
20103
20488
  return this.getProjectConfigRelativePaths().some((configPath) => configPath === normalizedRelativePath);
20104
20489
  }
20105
20490
  isProjectConfigPathOrAncestor(relativePath) {
20106
- const normalizedRelativePath = path26.normalize(relativePath);
20491
+ const normalizedRelativePath = path28.normalize(relativePath);
20107
20492
  return this.getProjectConfigRelativePaths().some(
20108
- (configPath) => configPath === normalizedRelativePath || configPath.startsWith(`${normalizedRelativePath}${path26.sep}`)
20493
+ (configPath) => configPath === normalizedRelativePath || configPath.startsWith(`${normalizedRelativePath}${path28.sep}`)
20109
20494
  );
20110
20495
  }
20111
20496
  isOutsideProjectPath(relativePath) {
20112
- return relativePath === ".." || relativePath.startsWith(`..${path26.sep}`) || path26.isAbsolute(relativePath);
20497
+ return relativePath === ".." || relativePath.startsWith(`..${path28.sep}`) || path28.isAbsolute(relativePath);
20113
20498
  }
20114
20499
  getNearestExistingDirectory(directoryPath) {
20115
20500
  let candidate = directoryPath;
20116
20501
  while (!(0, import_fs19.existsSync)(candidate)) {
20117
- const parent = path26.dirname(candidate);
20502
+ const parent = path28.dirname(candidate);
20118
20503
  if (parent === candidate) break;
20119
20504
  candidate = parent;
20120
20505
  }
@@ -20122,9 +20507,51 @@ var FileWatcher = class {
20122
20507
  }
20123
20508
  getProjectConfigRelativePaths() {
20124
20509
  return this.projectConfigPaths.map(
20125
- (configPath) => path26.normalize(path26.relative(this.projectRoot, configPath))
20510
+ (configPath) => path28.normalize(path28.relative(this.projectRoot, configPath))
20126
20511
  );
20127
20512
  }
20513
+ getConfigPathStates() {
20514
+ const states = /* @__PURE__ */ new Map();
20515
+ for (const configPath of this.projectConfigPaths) {
20516
+ const state = this.getConfigPathState(configPath);
20517
+ if (state) states.set(configPath, state);
20518
+ }
20519
+ return states;
20520
+ }
20521
+ getConfigPathState(configPath) {
20522
+ try {
20523
+ const stats = (0, import_fs19.statSync)(configPath);
20524
+ return stats.isFile() ? { mtimeMs: stats.mtimeMs, size: stats.size } : void 0;
20525
+ } catch (error) {
20526
+ void error;
20527
+ return void 0;
20528
+ }
20529
+ }
20530
+ updateConfigPathState(configPath) {
20531
+ const state = this.getConfigPathState(configPath);
20532
+ if (state) {
20533
+ this.configPathStates.set(configPath, state);
20534
+ } else {
20535
+ this.configPathStates.delete(configPath);
20536
+ }
20537
+ }
20538
+ reconcileConfigPathStates() {
20539
+ const nextStates = this.getConfigPathStates();
20540
+ const changes = [];
20541
+ for (const configPath of this.projectConfigPaths) {
20542
+ const previous = this.configPathStates.get(configPath);
20543
+ const next = nextStates.get(configPath);
20544
+ if (!previous && next) {
20545
+ changes.push({ path: configPath, type: "add" });
20546
+ } else if (previous && !next) {
20547
+ changes.push({ path: configPath, type: "unlink" });
20548
+ } else if (previous && next && (previous.size !== next.size || previous.mtimeMs !== next.mtimeMs)) {
20549
+ changes.push({ path: configPath, type: "change" });
20550
+ }
20551
+ }
20552
+ this.configPathStates = nextStates;
20553
+ this.recordChanges(changes);
20554
+ }
20128
20555
  scheduleFlush() {
20129
20556
  if (this.debounceTimer) {
20130
20557
  clearTimeout(this.debounceTimer);
@@ -20138,7 +20565,7 @@ var FileWatcher = class {
20138
20565
  return;
20139
20566
  }
20140
20567
  const changes = Array.from(this.pendingChanges.entries()).map(
20141
- ([path31, type]) => ({ path: path31, type })
20568
+ ([path33, type]) => ({ path: path33, type })
20142
20569
  );
20143
20570
  this.pendingChanges.clear();
20144
20571
  try {
@@ -20152,20 +20579,31 @@ var FileWatcher = class {
20152
20579
  clearTimeout(this.debounceTimer);
20153
20580
  this.debounceTimer = null;
20154
20581
  }
20582
+ if (this.nativeReconcileTimer) {
20583
+ clearTimeout(this.nativeReconcileTimer);
20584
+ this.nativeReconcileTimer = null;
20585
+ }
20586
+ this.nativeInvalidatedPaths.clear();
20155
20587
  const watcher = this.watcher;
20588
+ const nativeWatcher = this.nativeWatcher;
20156
20589
  const pendingClose = this.pendingClose;
20157
20590
  const resolveReady = this.resolveReady;
20158
20591
  this.watcher = null;
20592
+ this.nativeWatcher = null;
20593
+ this.nativeReconciler = null;
20594
+ this.nativeStarting = false;
20595
+ this.nativeInitializing = false;
20596
+ this.nativeSetupGeneration += 1;
20159
20597
  this.pendingClose = null;
20160
20598
  this.resolveReady = null;
20161
20599
  this.readyPromise = null;
20162
20600
  this.pendingChanges.clear();
20163
20601
  this.onChanges = null;
20164
- await Promise.all([watcher?.close(), pendingClose]);
20602
+ await Promise.all([watcher?.close(), nativeWatcher?.stop(), pendingClose]);
20165
20603
  resolveReady?.();
20166
20604
  }
20167
20605
  isRunning() {
20168
- return this.watcher !== null;
20606
+ return this.watcher !== null || this.nativeWatcher !== null || this.nativeStarting;
20169
20607
  }
20170
20608
  async waitUntilReady() {
20171
20609
  await (this.readyPromise ?? Promise.resolve());
@@ -20173,7 +20611,7 @@ var FileWatcher = class {
20173
20611
  };
20174
20612
 
20175
20613
  // src/watcher/git-head-watcher.ts
20176
- var path27 = __toESM(require("path"), 1);
20614
+ var path29 = __toESM(require("path"), 1);
20177
20615
  var GitHeadWatcher = class {
20178
20616
  watcher = null;
20179
20617
  projectRoot;
@@ -20195,13 +20633,13 @@ var GitHeadWatcher = class {
20195
20633
  this.readyPromise = Promise.resolve();
20196
20634
  return;
20197
20635
  }
20198
- this.readyPromise = new Promise((resolve18) => {
20199
- this.resolveReady = resolve18;
20636
+ this.readyPromise = new Promise((resolve20) => {
20637
+ this.resolveReady = resolve20;
20200
20638
  });
20201
20639
  this.onBranchChange = handler;
20202
20640
  this.currentBranch = getCurrentBranch(this.projectRoot);
20203
20641
  const headPath = getHeadPath(this.projectRoot);
20204
- const refsPath = path27.join(this.projectRoot, ".git", "refs", "heads");
20642
+ const refsPath = path29.join(this.projectRoot, ".git", "refs", "heads");
20205
20643
  this.watcher = chokidar_default.watch([headPath, refsPath], {
20206
20644
  persistent: true,
20207
20645
  ignoreInitial: true,
@@ -20337,7 +20775,7 @@ function getConfigPaths(projectRoot, host, options) {
20337
20775
 
20338
20776
  // src/tools/visualize/activity.ts
20339
20777
  var import_child_process5 = require("child_process");
20340
- var path28 = __toESM(require("path"), 1);
20778
+ var path30 = __toESM(require("path"), 1);
20341
20779
  function attachRecentActivity(data, projectRoot) {
20342
20780
  const activity = readGitActivity(projectRoot);
20343
20781
  const changes = activity.size > 0 ? buildGitChanges(data, activity, projectRoot) : buildGraphChanges(data);
@@ -20499,7 +20937,7 @@ function normalizePath4(filePath) {
20499
20937
  return filePath.replace(/\\/g, "/");
20500
20938
  }
20501
20939
  function toGitRelativePath(projectRoot, filePath) {
20502
- const relativePath = path28.isAbsolute(filePath) ? path28.relative(projectRoot, filePath) : filePath;
20940
+ const relativePath = path30.isAbsolute(filePath) ? path30.relative(projectRoot, filePath) : filePath;
20503
20941
  return normalizePath4(relativePath);
20504
20942
  }
20505
20943
 
@@ -20757,7 +21195,7 @@ render();
20757
21195
  }
20758
21196
 
20759
21197
  // src/tools/visualize/transform.ts
20760
- var path29 = __toESM(require("path"), 1);
21198
+ var path31 = __toESM(require("path"), 1);
20761
21199
 
20762
21200
  // src/tools/visualize/modules.ts
20763
21201
  var MAX_MODULES = 18;
@@ -20890,8 +21328,8 @@ function compactModules(prefixToNodes) {
20890
21328
  function deriveModules(nodes) {
20891
21329
  const initial = /* @__PURE__ */ new Map();
20892
21330
  for (const node of nodes) {
20893
- const relative12 = stripToProjectRelative(node.filePath);
20894
- const prefix = modulePrefixFromRelativePath(relative12);
21331
+ const relative14 = stripToProjectRelative(node.filePath);
21332
+ const prefix = modulePrefixFromRelativePath(relative14);
20895
21333
  if (!initial.has(prefix)) initial.set(prefix, []);
20896
21334
  initial.get(prefix)?.push(node);
20897
21335
  }
@@ -21017,7 +21455,7 @@ function transformForVisualization(symbols, edges, options = {}) {
21017
21455
  filePath: s.filePath,
21018
21456
  kind: s.kind,
21019
21457
  line: s.startLine,
21020
- directory: path29.dirname(s.filePath),
21458
+ directory: path31.dirname(s.filePath),
21021
21459
  moduleId: "",
21022
21460
  moduleLabel: ""
21023
21461
  }));
@@ -21045,9 +21483,9 @@ function parseArgs(argv) {
21045
21483
  let host = "opencode";
21046
21484
  for (let i = 2; i < argv.length; i++) {
21047
21485
  if (argv[i] === "--project" && argv[i + 1]) {
21048
- project = path30.resolve(argv[++i]);
21486
+ project = path32.resolve(argv[++i]);
21049
21487
  } else if (argv[i] === "--config" && argv[i + 1]) {
21050
- config = path30.resolve(argv[++i]);
21488
+ config = path32.resolve(argv[++i]);
21051
21489
  } else if (argv[i] === "--host" && argv[i + 1]) {
21052
21490
  host = parseHostMode(argv[++i]);
21053
21491
  } else if (argv[i] === "--host") {
@@ -21074,7 +21512,7 @@ function parseIndexArgs(argv, cwd) {
21074
21512
  if (!arg.startsWith("--project=")) {
21075
21513
  i += 1;
21076
21514
  }
21077
- project = path30.resolve(cwd, value);
21515
+ project = path32.resolve(cwd, value);
21078
21516
  continue;
21079
21517
  }
21080
21518
  if (arg === "--config" || arg.startsWith("--config=")) {
@@ -21085,7 +21523,7 @@ function parseIndexArgs(argv, cwd) {
21085
21523
  if (!arg.startsWith("--config=")) {
21086
21524
  i += 1;
21087
21525
  }
21088
- config = path30.resolve(cwd, value);
21526
+ config = path32.resolve(cwd, value);
21089
21527
  continue;
21090
21528
  }
21091
21529
  if (arg === "--host" || arg.startsWith("--host=")) {
@@ -21151,7 +21589,7 @@ function parseVisualizeArgs(argv, cwd) {
21151
21589
  for (let i = 0; i < argv.length; i++) {
21152
21590
  const arg = argv[i];
21153
21591
  if (arg === "--project" && argv[i + 1]) {
21154
- project = path30.resolve(argv[++i]);
21592
+ project = path32.resolve(argv[++i]);
21155
21593
  } else if (arg === "--max" && argv[i + 1]) {
21156
21594
  maxNodes = Number(argv[++i]);
21157
21595
  } else if (arg.startsWith("--max=") || arg.startsWith("max=")) {
@@ -21188,7 +21626,7 @@ async function handleVisualizeCommand(argv, cwd) {
21188
21626
  console.error("No connected symbols found. Retry with: npm run visualize -- orphans");
21189
21627
  return 1;
21190
21628
  }
21191
- const outputPath = path30.join(os8.tmpdir(), `call-graph-${Date.now()}.html`);
21629
+ const outputPath = path32.join(os8.tmpdir(), `call-graph-${Date.now()}.html`);
21192
21630
  (0, import_fs20.writeFileSync)(outputPath, generateVisualizationHtml(vizData), "utf-8");
21193
21631
  console.log(`Temporal call graph visualization generated: ${outputPath}`);
21194
21632
  console.log(`Nodes: ${vizData.nodes.length} | Edges: ${vizData.edges.length}`);
@@ -21220,8 +21658,60 @@ async function runMcpCli(argv) {
21220
21658
  const config = parseConfig(rawConfig);
21221
21659
  const server = createMcpServer(args.project, config, args.host);
21222
21660
  const transport = new import_stdio.StdioServerTransport();
21223
- await server.connect(transport);
21224
21661
  let watcher = null;
21662
+ let shutdownPromise;
21663
+ const onServerClose = server.server.onclose;
21664
+ const shutdown = () => {
21665
+ if (shutdownPromise) return shutdownPromise;
21666
+ process.stdin.removeListener("end", requestShutdown);
21667
+ process.stdin.removeListener("close", requestShutdown);
21668
+ process.removeListener("SIGHUP", requestShutdown);
21669
+ process.removeListener("SIGINT", requestShutdown);
21670
+ process.removeListener("SIGTERM", requestShutdown);
21671
+ server.server.onclose = onServerClose;
21672
+ shutdownPromise = (async () => {
21673
+ let exitCode = 0;
21674
+ try {
21675
+ await watcher?.stop();
21676
+ } catch (error) {
21677
+ exitCode = 1;
21678
+ console.error("Failed to stop MCP file watcher cleanly:", error);
21679
+ }
21680
+ try {
21681
+ await stopAutoIndex(args.project, args.host);
21682
+ } catch (error) {
21683
+ exitCode = 1;
21684
+ console.error("Failed to stop automatic indexing cleanly:", error);
21685
+ }
21686
+ try {
21687
+ await server.close();
21688
+ } catch (error) {
21689
+ exitCode = 1;
21690
+ console.error("Failed to close MCP server cleanly:", error);
21691
+ }
21692
+ process.exit(exitCode);
21693
+ })();
21694
+ return shutdownPromise;
21695
+ };
21696
+ const requestShutdown = () => {
21697
+ void shutdown();
21698
+ };
21699
+ server.server.onclose = () => {
21700
+ try {
21701
+ onServerClose?.();
21702
+ } finally {
21703
+ requestShutdown();
21704
+ }
21705
+ };
21706
+ process.stdin.once("end", requestShutdown);
21707
+ process.stdin.once("close", requestShutdown);
21708
+ process.once("SIGINT", requestShutdown);
21709
+ if (process.platform !== "win32") {
21710
+ process.once("SIGHUP", requestShutdown);
21711
+ process.once("SIGTERM", requestShutdown);
21712
+ }
21713
+ await server.connect(transport);
21714
+ if (shutdownPromise) return;
21225
21715
  const isHomeDir = isHomeDirectory(args.project);
21226
21716
  const isValidProject = !isHomeDir && (!config.indexing.requireProjectMarker || hasProjectMarker(args.project));
21227
21717
  if (config.indexing.watchFiles && isValidProject) {
@@ -21233,26 +21723,6 @@ async function runMcpCli(argv) {
21233
21723
  args.config ? { configPath: args.config } : {}
21234
21724
  );
21235
21725
  }
21236
- let shuttingDown = false;
21237
- const shutdown = async () => {
21238
- if (shuttingDown) return;
21239
- shuttingDown = true;
21240
- try {
21241
- await watcher?.stop();
21242
- await stopAutoIndex(args.project, args.host);
21243
- await server.close();
21244
- process.exit(0);
21245
- } catch (error) {
21246
- console.error("Failed to stop MCP server cleanly:", error);
21247
- process.exit(1);
21248
- }
21249
- };
21250
- process.on("SIGINT", () => {
21251
- void shutdown();
21252
- });
21253
- process.on("SIGTERM", () => {
21254
- void shutdown();
21255
- });
21256
21726
  }
21257
21727
  function printIndexProgress(onProgress, title, metadata) {
21258
21728
  const details = Object.entries(metadata).filter(([, value]) => value !== void 0 && value !== null).map(([key, value]) => `${key}=${isSensitiveKey(key) ? "[REDACTED]" : String(value)}`).join(" ");