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.js CHANGED
@@ -491,7 +491,7 @@ var require_ignore = __commonJS({
491
491
  // path matching.
492
492
  // - check `string` either `MODE_IGNORE` or `MODE_CHECK_IGNORE`
493
493
  // @returns {TestResult} true if a file is ignored
494
- test(path31, checkUnignored, mode) {
494
+ test(path33, checkUnignored, mode) {
495
495
  let ignored = false;
496
496
  let unignored = false;
497
497
  let matchedRule;
@@ -500,7 +500,7 @@ var require_ignore = __commonJS({
500
500
  if (unignored === negative && ignored !== unignored || negative && !ignored && !unignored && !checkUnignored) {
501
501
  return;
502
502
  }
503
- const matched = rule[mode].test(path31);
503
+ const matched = rule[mode].test(path33);
504
504
  if (!matched) {
505
505
  return;
506
506
  }
@@ -521,17 +521,17 @@ var require_ignore = __commonJS({
521
521
  var throwError = (message, Ctor) => {
522
522
  throw new Ctor(message);
523
523
  };
524
- var checkPath = (path31, originalPath, doThrow) => {
525
- if (!isString(path31)) {
524
+ var checkPath = (path33, originalPath, doThrow) => {
525
+ if (!isString(path33)) {
526
526
  return doThrow(
527
527
  `path must be a string, but got \`${originalPath}\``,
528
528
  TypeError
529
529
  );
530
530
  }
531
- if (!path31) {
531
+ if (!path33) {
532
532
  return doThrow(`path must not be empty`, TypeError);
533
533
  }
534
- if (checkPath.isNotRelative(path31)) {
534
+ if (checkPath.isNotRelative(path33)) {
535
535
  const r = "`path.relative()`d";
536
536
  return doThrow(
537
537
  `path should be a ${r} string, but got "${originalPath}"`,
@@ -540,7 +540,7 @@ var require_ignore = __commonJS({
540
540
  }
541
541
  return true;
542
542
  };
543
- var isNotRelative = (path31) => REGEX_TEST_INVALID_PATH.test(path31);
543
+ var isNotRelative = (path33) => REGEX_TEST_INVALID_PATH.test(path33);
544
544
  checkPath.isNotRelative = isNotRelative;
545
545
  checkPath.convert = (p) => p;
546
546
  var Ignore2 = class {
@@ -570,19 +570,19 @@ var require_ignore = __commonJS({
570
570
  }
571
571
  // @returns {TestResult}
572
572
  _test(originalPath, cache, checkUnignored, slices) {
573
- const path31 = originalPath && checkPath.convert(originalPath);
573
+ const path33 = originalPath && checkPath.convert(originalPath);
574
574
  checkPath(
575
- path31,
575
+ path33,
576
576
  originalPath,
577
577
  this._strictPathCheck ? throwError : RETURN_FALSE
578
578
  );
579
- return this._t(path31, cache, checkUnignored, slices);
579
+ return this._t(path33, cache, checkUnignored, slices);
580
580
  }
581
- checkIgnore(path31) {
582
- if (!REGEX_TEST_TRAILING_SLASH.test(path31)) {
583
- return this.test(path31);
581
+ checkIgnore(path33) {
582
+ if (!REGEX_TEST_TRAILING_SLASH.test(path33)) {
583
+ return this.test(path33);
584
584
  }
585
- const slices = path31.split(SLASH2).filter(Boolean);
585
+ const slices = path33.split(SLASH2).filter(Boolean);
586
586
  slices.pop();
587
587
  if (slices.length) {
588
588
  const parent = this._t(
@@ -595,18 +595,18 @@ var require_ignore = __commonJS({
595
595
  return parent;
596
596
  }
597
597
  }
598
- return this._rules.test(path31, false, MODE_CHECK_IGNORE);
598
+ return this._rules.test(path33, false, MODE_CHECK_IGNORE);
599
599
  }
600
- _t(path31, cache, checkUnignored, slices) {
601
- if (path31 in cache) {
602
- return cache[path31];
600
+ _t(path33, cache, checkUnignored, slices) {
601
+ if (path33 in cache) {
602
+ return cache[path33];
603
603
  }
604
604
  if (!slices) {
605
- slices = path31.split(SLASH2).filter(Boolean);
605
+ slices = path33.split(SLASH2).filter(Boolean);
606
606
  }
607
607
  slices.pop();
608
608
  if (!slices.length) {
609
- return cache[path31] = this._rules.test(path31, checkUnignored, MODE_IGNORE);
609
+ return cache[path33] = this._rules.test(path33, checkUnignored, MODE_IGNORE);
610
610
  }
611
611
  const parent = this._t(
612
612
  slices.join(SLASH2) + SLASH2,
@@ -614,29 +614,29 @@ var require_ignore = __commonJS({
614
614
  checkUnignored,
615
615
  slices
616
616
  );
617
- return cache[path31] = parent.ignored ? parent : this._rules.test(path31, checkUnignored, MODE_IGNORE);
617
+ return cache[path33] = parent.ignored ? parent : this._rules.test(path33, checkUnignored, MODE_IGNORE);
618
618
  }
619
- ignores(path31) {
620
- return this._test(path31, this._ignoreCache, false).ignored;
619
+ ignores(path33) {
620
+ return this._test(path33, this._ignoreCache, false).ignored;
621
621
  }
622
622
  createFilter() {
623
- return (path31) => !this.ignores(path31);
623
+ return (path33) => !this.ignores(path33);
624
624
  }
625
625
  filter(paths) {
626
626
  return makeArray(paths).filter(this.createFilter());
627
627
  }
628
628
  // @returns {TestResult}
629
- test(path31) {
630
- return this._test(path31, this._testCache, true);
629
+ test(path33) {
630
+ return this._test(path33, this._testCache, true);
631
631
  }
632
632
  };
633
633
  var factory = (options) => new Ignore2(options);
634
- var isPathValid = (path31) => checkPath(path31 && checkPath.convert(path31), path31, RETURN_FALSE);
634
+ var isPathValid = (path33) => checkPath(path33 && checkPath.convert(path33), path33, RETURN_FALSE);
635
635
  var setupWindows = () => {
636
636
  const makePosix = (str) => /^\\\\\?\\/.test(str) || /["<>|\u0000-\u001F]+/u.test(str) ? str : str.replace(/\\/g, "/");
637
637
  checkPath.convert = makePosix;
638
638
  const REGEX_TEST_WINDOWS_PATH_ABSOLUTE = /^[a-z]:\//i;
639
- checkPath.isNotRelative = (path31) => REGEX_TEST_WINDOWS_PATH_ABSOLUTE.test(path31) || isNotRelative(path31);
639
+ checkPath.isNotRelative = (path33) => REGEX_TEST_WINDOWS_PATH_ABSOLUTE.test(path33) || isNotRelative(path33);
640
640
  };
641
641
  if (
642
642
  // Detect `process` so that it can run in browsers.
@@ -655,7 +655,7 @@ var require_ignore = __commonJS({
655
655
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
656
656
  import { realpathSync as realpathSync6, writeFileSync as writeFileSync6 } from "fs";
657
657
  import * as os8 from "os";
658
- import * as path30 from "path";
658
+ import * as path32 from "path";
659
659
  import { fileURLToPath as fileURLToPath2 } from "url";
660
660
 
661
661
  // src/config/constants.ts
@@ -1165,9 +1165,9 @@ import { mkdirSync, readFileSync, writeFileSync } from "fs";
1165
1165
  import * as path from "path";
1166
1166
 
1167
1167
  // src/eval/report-formatters.ts
1168
- function assertFiniteNumber(value, path31) {
1168
+ function assertFiniteNumber(value, path33) {
1169
1169
  if (typeof value !== "number" || Number.isNaN(value) || !Number.isFinite(value)) {
1170
- throw new Error(`${path31} must be a finite number`);
1170
+ throw new Error(`${path33} must be a finite number`);
1171
1171
  }
1172
1172
  return value;
1173
1173
  }
@@ -1439,7 +1439,7 @@ function pTimeout(promise, options) {
1439
1439
  } = options;
1440
1440
  let timer;
1441
1441
  let abortHandler;
1442
- const wrappedPromise = new Promise((resolve18, reject) => {
1442
+ const wrappedPromise = new Promise((resolve20, reject) => {
1443
1443
  if (typeof milliseconds !== "number" || Math.sign(milliseconds) !== 1) {
1444
1444
  throw new TypeError(`Expected \`milliseconds\` to be a positive number, got \`${milliseconds}\``);
1445
1445
  }
@@ -1453,7 +1453,7 @@ function pTimeout(promise, options) {
1453
1453
  };
1454
1454
  signal.addEventListener("abort", abortHandler, { once: true });
1455
1455
  }
1456
- promise.then(resolve18, reject);
1456
+ promise.then(resolve20, reject);
1457
1457
  if (milliseconds === Number.POSITIVE_INFINITY) {
1458
1458
  return;
1459
1459
  }
@@ -1461,7 +1461,7 @@ function pTimeout(promise, options) {
1461
1461
  timer = customTimers.setTimeout.call(void 0, () => {
1462
1462
  if (fallback) {
1463
1463
  try {
1464
- resolve18(fallback());
1464
+ resolve20(fallback());
1465
1465
  } catch (error) {
1466
1466
  reject(error);
1467
1467
  }
@@ -1471,7 +1471,7 @@ function pTimeout(promise, options) {
1471
1471
  promise.cancel();
1472
1472
  }
1473
1473
  if (message === false) {
1474
- resolve18();
1474
+ resolve20();
1475
1475
  } else if (message instanceof Error) {
1476
1476
  reject(message);
1477
1477
  } else {
@@ -1873,7 +1873,7 @@ var PQueue = class extends import_index.default {
1873
1873
  // Assign unique ID if not provided
1874
1874
  id: options.id ?? (this.#idAssigner++).toString()
1875
1875
  };
1876
- return new Promise((resolve18, reject) => {
1876
+ return new Promise((resolve20, reject) => {
1877
1877
  const taskSymbol = /* @__PURE__ */ Symbol(`task-${options.id}`);
1878
1878
  let cleanupQueueAbortHandler = () => void 0;
1879
1879
  const run = async () => {
@@ -1913,7 +1913,7 @@ var PQueue = class extends import_index.default {
1913
1913
  })]);
1914
1914
  }
1915
1915
  const result = await operation;
1916
- resolve18(result);
1916
+ resolve20(result);
1917
1917
  this.emit("completed", result);
1918
1918
  } catch (error) {
1919
1919
  reject(error);
@@ -2101,13 +2101,13 @@ var PQueue = class extends import_index.default {
2101
2101
  });
2102
2102
  }
2103
2103
  async #onEvent(event, filter) {
2104
- return new Promise((resolve18) => {
2104
+ return new Promise((resolve20) => {
2105
2105
  const listener = () => {
2106
2106
  if (filter && !filter()) {
2107
2107
  return;
2108
2108
  }
2109
2109
  this.off(event, listener);
2110
- resolve18();
2110
+ resolve20();
2111
2111
  };
2112
2112
  this.on(event, listener);
2113
2113
  });
@@ -2393,7 +2393,7 @@ async function onAttemptFailure({ error, attemptNumber, retriesConsumed, startTi
2393
2393
  const finalDelay = Math.min(delayTime, remainingTime);
2394
2394
  options.signal?.throwIfAborted();
2395
2395
  if (finalDelay > 0) {
2396
- await new Promise((resolve18, reject) => {
2396
+ await new Promise((resolve20, reject) => {
2397
2397
  const onAbort = () => {
2398
2398
  clearTimeout(timeoutToken);
2399
2399
  options.signal?.removeEventListener("abort", onAbort);
@@ -2401,7 +2401,7 @@ async function onAttemptFailure({ error, attemptNumber, retriesConsumed, startTi
2401
2401
  };
2402
2402
  const timeoutToken = setTimeout(() => {
2403
2403
  options.signal?.removeEventListener("abort", onAbort);
2404
- resolve18();
2404
+ resolve20();
2405
2405
  }, finalDelay);
2406
2406
  if (options.unref) {
2407
2407
  timeoutToken.unref?.();
@@ -3207,85 +3207,6 @@ function createEmbeddingProvider(configuredProviderInfo) {
3207
3207
  }
3208
3208
  }
3209
3209
 
3210
- // src/rerank/index.ts
3211
- function createReranker(config) {
3212
- if (!config.enabled) {
3213
- return new NoOpReranker();
3214
- }
3215
- return new SiliconFlowReranker(config);
3216
- }
3217
- var NoOpReranker = class {
3218
- isAvailable() {
3219
- return false;
3220
- }
3221
- async rerank(_query, documents, _topN) {
3222
- return {
3223
- results: documents.map((_, index) => ({ index, relevanceScore: 0 }))
3224
- };
3225
- }
3226
- };
3227
- var SiliconFlowReranker = class {
3228
- config;
3229
- constructor(config) {
3230
- this.config = config;
3231
- }
3232
- isAvailable() {
3233
- return this.config.enabled && !!this.config.baseUrl && !!this.config.model;
3234
- }
3235
- async rerank(query, documents, topN) {
3236
- if (documents.length === 0) {
3237
- return { results: [] };
3238
- }
3239
- const headers = {
3240
- "Content-Type": "application/json"
3241
- };
3242
- if (this.config.apiKey) {
3243
- headers["Authorization"] = `Bearer ${this.config.apiKey}`;
3244
- }
3245
- const baseUrl = this.config.baseUrl;
3246
- if (!baseUrl) {
3247
- throw new Error("Reranker baseUrl is required. Configure reranker.baseUrl in your codebase-index.json.");
3248
- }
3249
- const timeoutMs = this.config.timeoutMs ?? 3e4;
3250
- const controller = new AbortController();
3251
- const timeout = setTimeout(() => controller.abort(), timeoutMs);
3252
- try {
3253
- const response = await fetch(`${baseUrl}/rerank`, {
3254
- method: "POST",
3255
- headers,
3256
- body: JSON.stringify({
3257
- model: this.config.model,
3258
- query,
3259
- documents,
3260
- top_n: topN ?? this.config.topN ?? 20,
3261
- return_documents: false
3262
- }),
3263
- signal: controller.signal
3264
- });
3265
- clearTimeout(timeout);
3266
- if (!response.ok) {
3267
- const errorText = await response.text();
3268
- throw new Error(`Rerank API error: ${response.status} - ${errorText}`);
3269
- }
3270
- const data = await response.json();
3271
- return {
3272
- results: data.results.map((r) => ({
3273
- index: r.index,
3274
- relevanceScore: r.relevance_score,
3275
- document: r.document?.text
3276
- })),
3277
- tokensUsed: data.meta?.tokens?.input_tokens
3278
- };
3279
- } catch (error) {
3280
- clearTimeout(timeout);
3281
- if (error instanceof Error && error.name === "AbortError") {
3282
- throw new Error(`Rerank API request timed out after ${timeoutMs}ms`);
3283
- }
3284
- throw error;
3285
- }
3286
- }
3287
- };
3288
-
3289
3210
  // src/utils/files.ts
3290
3211
  var import_ignore = __toESM(require_ignore(), 1);
3291
3212
  import { existsSync as existsSync2, readFileSync as readFileSync3, promises as fsPromises } from "fs";
@@ -3447,8 +3368,8 @@ async function* walkDirectory(dir, projectRoot, includePatterns, excludePatterns
3447
3368
  if (entry.isDirectory()) {
3448
3369
  subdirs.push({ fullPath, relativePath });
3449
3370
  } else if (entry.isFile()) {
3450
- const stat4 = await fsPromises.stat(fullPath);
3451
- if (stat4.size > maxFileSize) {
3371
+ const stat5 = await fsPromises.stat(fullPath);
3372
+ if (stat5.size > maxFileSize) {
3452
3373
  skipped.push({ path: relativePath, reason: "too_large" });
3453
3374
  continue;
3454
3375
  }
@@ -3466,7 +3387,7 @@ async function* walkDirectory(dir, projectRoot, includePatterns, excludePatterns
3466
3387
  }
3467
3388
  }
3468
3389
  if (matched) {
3469
- filesInDir.push({ path: fullPath, size: stat4.size });
3390
+ filesInDir.push({ path: fullPath, size: stat5.size });
3470
3391
  }
3471
3392
  }
3472
3393
  }
@@ -3523,8 +3444,8 @@ async function collectFiles(projectRoot, includePatterns, excludePatterns, maxFi
3523
3444
  }
3524
3445
  for (const resolvedKbRoot of normalizedRoots) {
3525
3446
  try {
3526
- const stat4 = await fsPromises.stat(resolvedKbRoot);
3527
- if (!stat4.isDirectory()) {
3447
+ const stat5 = await fsPromises.stat(resolvedKbRoot);
3448
+ if (!stat5.isDirectory()) {
3528
3449
  skipped.push({ path: resolvedKbRoot, reason: "excluded" });
3529
3450
  continue;
3530
3451
  }
@@ -4850,11 +4771,11 @@ function resolveGitDir(repoRoot) {
4850
4771
  return null;
4851
4772
  }
4852
4773
  try {
4853
- const stat4 = statSync2(gitPath);
4854
- if (stat4.isDirectory()) {
4774
+ const stat5 = statSync2(gitPath);
4775
+ if (stat5.isDirectory()) {
4855
4776
  return gitPath;
4856
4777
  }
4857
- if (stat4.isFile()) {
4778
+ if (stat5.isFile()) {
4858
4779
  const content = readFileSync5(gitPath, "utf-8").trim();
4859
4780
  const match = content.match(/^gitdir:\s*(.+)$/);
4860
4781
  if (match) {
@@ -5359,8 +5280,8 @@ async function isWorktreeRegistered(projectRoot, worktreePath) {
5359
5280
  return false;
5360
5281
  }
5361
5282
  function isPathWithinRoot(filePath, rootPath) {
5362
- const relative12 = path9.relative(path9.resolve(rootPath), path9.resolve(filePath));
5363
- return relative12 === "" || !relative12.startsWith(`..${path9.sep}`) && relative12 !== ".." && !path9.isAbsolute(relative12);
5283
+ const relative14 = path9.relative(path9.resolve(rootPath), path9.resolve(filePath));
5284
+ return relative14 === "" || !relative14.startsWith(`..${path9.sep}`) && relative14 !== ".." && !path9.isAbsolute(relative14);
5364
5285
  }
5365
5286
  async function pruneExactMissingWorktreeRegistration(projectRoot, worktreePath) {
5366
5287
  if (await pathExists(worktreePath)) return false;
@@ -5918,11 +5839,11 @@ function normalizeFiles(rawFiles, projectRoot) {
5918
5839
  for (const raw of rawFiles) {
5919
5840
  if (raw.length === 0) continue;
5920
5841
  const absolute = path11.resolve(root, raw);
5921
- const relative12 = path11.relative(root, absolute);
5922
- if (path11.isAbsolute(raw) || relative12 === ".." || relative12.startsWith(`..${path11.sep}`) || path11.isAbsolute(relative12)) {
5842
+ const relative14 = path11.relative(root, absolute);
5843
+ if (path11.isAbsolute(raw) || relative14 === ".." || relative14.startsWith(`..${path11.sep}`) || path11.isAbsolute(relative14)) {
5923
5844
  throw new Error(`Changed file escapes the project root: ${JSON.stringify(raw)}`);
5924
5845
  }
5925
- const cleaned = relative12.startsWith(`.${path11.sep}`) ? relative12.slice(2) : relative12;
5846
+ const cleaned = relative14.startsWith(`.${path11.sep}`) ? relative14.slice(2) : relative14;
5926
5847
  if (!seen.has(cleaned)) {
5927
5848
  seen.add(cleaned);
5928
5849
  result.push(cleaned);
@@ -6259,7 +6180,7 @@ function analyzeQueryIntent(query) {
6259
6180
  }
6260
6181
  function isTestPath(filePath) {
6261
6182
  const normalized = normalizePath(filePath);
6262
- return /(?:^|\/)(?:test|tests|__tests__|spec|specs)(?:\/|$)/u.test(normalized) || /\.(?:test|spec)\.[^/]+$/u.test(normalized);
6183
+ return /(?:^|\/)(?:test|tests|__tests__|spec|specs)(?:\/|$)/u.test(normalized) || /(?:\.(?:test|spec)|_(?:test|spec))\.[^/]+$/u.test(normalized) || /(?:^|\/)(?:test|spec)_[^/]+\.[^/]+$/u.test(normalized);
6263
6184
  }
6264
6185
  function isFixturePath(filePath) {
6265
6186
  const normalized = normalizePath(filePath);
@@ -6666,7 +6587,8 @@ function rankHybridResults(query, semanticResults, keywordResults, options) {
6666
6587
  return cached;
6667
6588
  }
6668
6589
  }
6669
- const overfetchLimit = Math.max(options.limit * 4, options.limit);
6590
+ const overfetchFactor = prioritizeSourcePaths ? 12 : 4;
6591
+ const overfetchLimit = Math.max(options.limit * overfetchFactor, options.limit);
6670
6592
  const fused = options.fusionStrategy === "rrf" ? fuseResultsRrf(semanticResults, keywordResults, options.rrfK, overfetchLimit) : fuseResultsWeighted(semanticResults, keywordResults, options.hybridWeight, overfetchLimit);
6671
6593
  const rerankPoolLimit = Math.max(overfetchLimit, options.rerankTopN * 3, options.limit * 6);
6672
6594
  const rerankPool = fused.slice(0, rerankPoolLimit);
@@ -8578,7 +8500,6 @@ var Indexer = class _Indexer {
8578
8500
  database = null;
8579
8501
  provider = null;
8580
8502
  configuredProviderInfo = null;
8581
- reranker = null;
8582
8503
  fileHashCache = /* @__PURE__ */ new Map();
8583
8504
  fileHashCachePath = "";
8584
8505
  failedBatchesPath = "";
@@ -8738,7 +8659,6 @@ var Indexer = class _Indexer {
8738
8659
  this.database = null;
8739
8660
  this.provider = null;
8740
8661
  this.configuredProviderInfo = null;
8741
- this.reranker = null;
8742
8662
  this.indexCompatibility = null;
8743
8663
  this.initializationMode = "none";
8744
8664
  this.readIssues = [];
@@ -9468,7 +9388,7 @@ var Indexer = class _Indexer {
9468
9388
  await options.queue.onSizeLessThan(Math.max(1, options.providerRateLimits.concurrency));
9469
9389
  const task = options.queue.add(async () => {
9470
9390
  if (options.rateLimitState.backoffMs > 0) {
9471
- await new Promise((resolve18) => setTimeout(resolve18, options.rateLimitState.backoffMs));
9391
+ await new Promise((resolve20) => setTimeout(resolve20, options.rateLimitState.backoffMs));
9472
9392
  }
9473
9393
  try {
9474
9394
  const embeddingResult = await pRetry(
@@ -10035,15 +9955,6 @@ var Indexer = class _Indexer {
10035
9955
  rerankerEnabled: this.config.reranker?.enabled ?? false
10036
9956
  });
10037
9957
  this.provider = createEmbeddingProvider(this.configuredProviderInfo);
10038
- if (this.config.reranker?.enabled) {
10039
- this.reranker = createReranker(this.config.reranker);
10040
- if (this.reranker.isAvailable()) {
10041
- this.logger.info("Reranker initialized", {
10042
- model: this.config.reranker.model,
10043
- baseUrl: this.config.reranker.baseUrl
10044
- });
10045
- }
10046
- }
10047
9958
  const dimensions = this.configuredProviderInfo.modelInfo.dimensions;
10048
9959
  const storePath = path15.join(this.indexPath, "vectors");
10049
9960
  const vectorMetadataPath = `${storePath}.meta.json`;
@@ -11448,6 +11359,7 @@ var Indexer = class _Indexer {
11448
11359
  const filterByBranch = options?.filterByBranch ?? true;
11449
11360
  const sourceIntent = options?.definitionIntent === true || classifyQueryIntentRaw(query) === "source";
11450
11361
  const identifierHints = extractIdentifierHints(query);
11362
+ const candidateLimit = maxResults * (sourceIntent ? 12 : 4);
11451
11363
  this.logger.search("debug", "Starting search", {
11452
11364
  query,
11453
11365
  maxResults,
@@ -11484,7 +11396,7 @@ var Indexer = class _Indexer {
11484
11396
  const semanticCandidates = embedding ? this.searchSemanticCandidates(
11485
11397
  store,
11486
11398
  embedding,
11487
- maxResults * 4,
11399
+ candidateLimit,
11488
11400
  branchChunkIds,
11489
11401
  shouldPrefilterByBranch
11490
11402
  ) : [];
@@ -11492,7 +11404,7 @@ var Indexer = class _Indexer {
11492
11404
  const keywordStartTime = performance2.now();
11493
11405
  const keywordCandidates = await this.keywordSearch(
11494
11406
  query,
11495
- maxResults * 4,
11407
+ candidateLimit,
11496
11408
  store,
11497
11409
  invertedIndex,
11498
11410
  branchChunkIds,
@@ -12250,9 +12162,9 @@ var Indexer = class _Indexer {
12250
12162
  this.requireReadableComponents(readIssues, "database");
12251
12163
  let shortest = [];
12252
12164
  for (const branchKey of this.getBranchCatalogKeys()) {
12253
- const path31 = database.findShortestPath(fromName, toName, branchKey, maxDepth);
12254
- if (path31.length > 0 && (shortest.length === 0 || path31.length < shortest.length)) {
12255
- shortest = path31;
12165
+ const path33 = database.findShortestPath(fromName, toName, branchKey, maxDepth);
12166
+ if (path33.length > 0 && (shortest.length === 0 || path33.length < shortest.length)) {
12167
+ shortest = path33;
12256
12168
  }
12257
12169
  }
12258
12170
  return shortest.map((hop) => this.resolveFilePathRecord(hop));
@@ -12300,13 +12212,13 @@ var Indexer = class _Indexer {
12300
12212
  }
12301
12213
  }
12302
12214
  if (!found) continue;
12303
- const path31 = [];
12215
+ const path33 = [];
12304
12216
  let currentSymbolId = toSymbolId;
12305
12217
  while (true) {
12306
12218
  const symbol = symbolsById.get(currentSymbolId);
12307
12219
  if (!symbol) break;
12308
12220
  const parent = parentBySymbolId.get(currentSymbolId);
12309
- path31.push({
12221
+ path33.push({
12310
12222
  symbolId: symbol.id,
12311
12223
  symbolName: symbol.name,
12312
12224
  filePath: symbol.filePath,
@@ -12316,9 +12228,9 @@ var Indexer = class _Indexer {
12316
12228
  if (!parent) break;
12317
12229
  currentSymbolId = parent.parentId;
12318
12230
  }
12319
- path31.reverse();
12320
- if (path31.length > 0 && (shortest.length === 0 || path31.length < shortest.length)) {
12321
- shortest = path31;
12231
+ path33.reverse();
12232
+ if (path33.length > 0 && (shortest.length === 0 || path33.length < shortest.length)) {
12233
+ shortest = path33;
12322
12234
  }
12323
12235
  }
12324
12236
  return shortest.map((hop) => this.resolveFilePathRecord(hop));
@@ -12654,7 +12566,6 @@ var Indexer = class _Indexer {
12654
12566
  this.store = null;
12655
12567
  this.invertedIndex = null;
12656
12568
  this.provider = null;
12657
- this.reranker = null;
12658
12569
  this.configuredProviderInfo = null;
12659
12570
  this.indexCompatibility = null;
12660
12571
  this.initializationMode = "none";
@@ -12968,8 +12879,8 @@ function formatExactSearchHandoff(results) {
12968
12879
  }
12969
12880
  function formatContextEvidence(result, index) {
12970
12881
  const symbol = result.name ? ` ${JSON.stringify(compactEvidenceValue(result.name, 80))}` : "";
12971
- const path31 = compactEvidenceValue(result.filePath, 120);
12972
- return `[${index}] ${result.chunkType}${symbol} in ${path31}:${result.startLine}-${result.endLine} (score ${result.score.toFixed(2)})`;
12882
+ const path33 = compactEvidenceValue(result.filePath, 120);
12883
+ return `[${index}] ${result.chunkType}${symbol} in ${path33}:${result.startLine}-${result.endLine} (score ${result.score.toFixed(2)})`;
12973
12884
  }
12974
12885
  function formatContextPack(heading, selected, candidateCount, duplicateCount, limitOmittedCount, budgetOmittedCount, includeExactSearchHandoff) {
12975
12886
  const lines = selected.map((result, index) => formatContextEvidence(result, index + 1));
@@ -13623,7 +13534,7 @@ function getErrorMessage4(error) {
13623
13534
  return error instanceof Error ? error.message : String(error);
13624
13535
  }
13625
13536
  function runCommand(file, args, options) {
13626
- return new Promise((resolve18, reject) => {
13537
+ return new Promise((resolve20, reject) => {
13627
13538
  childProcess.execFile(
13628
13539
  file,
13629
13540
  args,
@@ -13633,7 +13544,7 @@ function runCommand(file, args, options) {
13633
13544
  reject(error);
13634
13545
  return;
13635
13546
  }
13636
- resolve18(stdout);
13547
+ resolve20(stdout);
13637
13548
  }
13638
13549
  );
13639
13550
  });
@@ -13778,10 +13689,10 @@ function safeFailureMessage(error) {
13778
13689
  }
13779
13690
  function cancellableDelay(delayMs, signal) {
13780
13691
  if (signal.aborted) return Promise.reject(new AutoIndexCancelledError());
13781
- return new Promise((resolve18, reject) => {
13692
+ return new Promise((resolve20, reject) => {
13782
13693
  const timer = setTimeout(() => {
13783
13694
  signal.removeEventListener("abort", onAbort);
13784
- resolve18();
13695
+ resolve20();
13785
13696
  }, delayMs);
13786
13697
  timer.unref?.();
13787
13698
  const onAbort = () => {
@@ -13793,15 +13704,15 @@ function cancellableDelay(delayMs, signal) {
13793
13704
  }
13794
13705
  function withTimeout(promise, timeoutMs) {
13795
13706
  if (timeoutMs <= 0) return Promise.resolve(void 0);
13796
- return new Promise((resolve18) => {
13797
- const timer = setTimeout(() => resolve18(void 0), timeoutMs);
13707
+ return new Promise((resolve20) => {
13708
+ const timer = setTimeout(() => resolve20(void 0), timeoutMs);
13798
13709
  timer.unref?.();
13799
13710
  void promise.then((value) => {
13800
13711
  clearTimeout(timer);
13801
- resolve18(value);
13712
+ resolve20(value);
13802
13713
  }, () => {
13803
13714
  clearTimeout(timer);
13804
- resolve18(void 0);
13715
+ resolve20(void 0);
13805
13716
  });
13806
13717
  });
13807
13718
  }
@@ -14183,17 +14094,17 @@ var AutoIndexCoordinator = class {
14183
14094
  }
14184
14095
  }
14185
14096
  waitForBatteryRetry(delayMs) {
14186
- return new Promise((resolve18) => {
14097
+ return new Promise((resolve20) => {
14187
14098
  const timer = setTimeout(() => {
14188
14099
  if (this.batteryRetryTimer === timer) {
14189
14100
  this.batteryRetryTimer = null;
14190
14101
  this.resolveBatteryRetry = null;
14191
14102
  }
14192
- resolve18();
14103
+ resolve20();
14193
14104
  }, delayMs);
14194
14105
  timer.unref?.();
14195
14106
  this.batteryRetryTimer = timer;
14196
- this.resolveBatteryRetry = resolve18;
14107
+ this.resolveBatteryRetry = resolve20;
14197
14108
  });
14198
14109
  }
14199
14110
  cancelBatteryRetry() {
@@ -14201,9 +14112,9 @@ var AutoIndexCoordinator = class {
14201
14112
  clearTimeout(this.batteryRetryTimer);
14202
14113
  this.batteryRetryTimer = null;
14203
14114
  }
14204
- const resolve18 = this.resolveBatteryRetry;
14115
+ const resolve20 = this.resolveBatteryRetry;
14205
14116
  this.resolveBatteryRetry = null;
14206
- resolve18?.();
14117
+ resolve20?.();
14207
14118
  }
14208
14119
  finishBatteryCheck(batteryCheck) {
14209
14120
  if (this.batteryCheck !== batteryCheck) return;
@@ -14878,12 +14789,12 @@ async function getCallGraphPath(projectRoot, host, from, to, maxDepth, fromFileP
14878
14789
  if (fromResolution.status !== "resolved" || toResolution.status !== "resolved") {
14879
14790
  return { from: fromResolution, to: toResolution, path: [] };
14880
14791
  }
14881
- const path31 = await indexer.findCallPathBySymbolIds(
14792
+ const path33 = await indexer.findCallPathBySymbolIds(
14882
14793
  fromResolution.symbolId,
14883
14794
  toResolution.symbolId,
14884
14795
  maxDepth
14885
14796
  );
14886
- return { from: fromResolution, to: toResolution, path: path31 };
14797
+ return { from: fromResolution, to: toResolution, path: path33 };
14887
14798
  }
14888
14799
  async function runIndexCodebase(projectRoot, host, args, onProgress) {
14889
14800
  const root = getProjectRoot(projectRoot, host);
@@ -15508,7 +15419,7 @@ async function resolveCodebaseContextUnmeasured(projectRoot, host, input) {
15508
15419
  const directory = input.directory ?? void 0;
15509
15420
  const tokenBudget = input.tokenBudget ?? void 0;
15510
15421
  if (from && to) {
15511
- const path31 = await getCallGraphPath(
15422
+ const path33 = await getCallGraphPath(
15512
15423
  projectRoot,
15513
15424
  host,
15514
15425
  from,
@@ -15517,25 +15428,25 @@ async function resolveCodebaseContextUnmeasured(projectRoot, host, input) {
15517
15428
  fromFilePath,
15518
15429
  toFilePath
15519
15430
  );
15520
- const pathText = formatCallGraphPathResult(path31);
15521
- if (path31.path.length > 0) {
15431
+ const pathText = formatCallGraphPathResult(path33);
15432
+ if (path33.path.length > 0) {
15522
15433
  const fitted2 = fitTextToContextBudget(
15523
15434
  pathText,
15524
15435
  tokenBudget
15525
15436
  );
15526
15437
  return {
15527
15438
  text: fitted2.text,
15528
- details: fittedDetails("path", fitted2, path31.path.length)
15439
+ details: fittedDetails("path", fitted2, path33.path.length)
15529
15440
  };
15530
15441
  }
15531
- if (path31.from.status !== "resolved" || path31.to.status !== "resolved") {
15442
+ if (path33.from.status !== "resolved" || path33.to.status !== "resolved") {
15532
15443
  const fitted2 = fitTextToContextBudget(pathText, tokenBudget);
15533
15444
  return {
15534
15445
  text: fitted2.text,
15535
15446
  details: fittedDetails("path", fitted2, 0)
15536
15447
  };
15537
15448
  }
15538
- const resolvedFrom = path31.from;
15449
+ const resolvedFrom = path33.from;
15539
15450
  const { callers } = await getCallGraphData(projectRoot, host, {
15540
15451
  name: to,
15541
15452
  direction: "callers",
@@ -16012,9 +15923,9 @@ function getRelevantEvidence(query) {
16012
15923
  });
16013
15924
  }
16014
15925
  if (query.expected.acceptableFiles) {
16015
- for (const path31 of query.expected.acceptableFiles) {
15926
+ for (const path33 of query.expected.acceptableFiles) {
16016
15927
  legacyEvidence.push({
16017
- path: path31,
15928
+ path: path33,
16018
15929
  ...query.expected.symbol !== void 0 ? { symbol: query.expected.symbol } : {},
16019
15930
  relevance: 1
16020
15931
  });
@@ -16511,68 +16422,68 @@ function isStringArray4(value) {
16511
16422
  function isNonEmptyString(value) {
16512
16423
  return typeof value === "string" && value.trim().length > 0;
16513
16424
  }
16514
- function asPositiveNumber(value, path31) {
16425
+ function asPositiveNumber(value, path33) {
16515
16426
  if (typeof value !== "number" || Number.isNaN(value) || value < 0) {
16516
- throw new Error(`${path31} must be a non-negative number`);
16427
+ throw new Error(`${path33} must be a non-negative number`);
16517
16428
  }
16518
16429
  return value;
16519
16430
  }
16520
- function parseQueryType(value, path31) {
16431
+ function parseQueryType(value, path33) {
16521
16432
  if (value === "definition" || value === "implementation-intent" || value === "similarity" || value === "keyword-heavy" || value === "conceptual") {
16522
16433
  return value;
16523
16434
  }
16524
16435
  throw new Error(
16525
- `${path31} must be one of: definition, implementation-intent, similarity, keyword-heavy, conceptual`
16436
+ `${path33} must be one of: definition, implementation-intent, similarity, keyword-heavy, conceptual`
16526
16437
  );
16527
16438
  }
16528
- function parseExpectedRoute(value, path31) {
16439
+ function parseExpectedRoute(value, path33) {
16529
16440
  if (value === void 0) return void 0;
16530
16441
  if (value === "search" || value === "definition") return value;
16531
- throw new Error(`${path31} must be one of: search, definition`);
16442
+ throw new Error(`${path33} must be one of: search, definition`);
16532
16443
  }
16533
- function parseExpectedOutcome(value, path31) {
16444
+ function parseExpectedOutcome(value, path33) {
16534
16445
  if (value === void 0) return void 0;
16535
16446
  if (value === "results" || value === "no-results") {
16536
16447
  return value;
16537
16448
  }
16538
- throw new Error(`${path31} must be one of: results, no-results`);
16449
+ throw new Error(`${path33} must be one of: results, no-results`);
16539
16450
  }
16540
- function parseRecoveryExpectation(value, path31) {
16451
+ function parseRecoveryExpectation(value, path33) {
16541
16452
  if (value === void 0) return void 0;
16542
16453
  if (value === "none" || value === "filter-relaxed") {
16543
16454
  return value;
16544
16455
  }
16545
- throw new Error(`${path31} must be one of: none, filter-relaxed`);
16456
+ throw new Error(`${path33} must be one of: none, filter-relaxed`);
16546
16457
  }
16547
- function parseQueryDifficulty(value, path31) {
16458
+ function parseQueryDifficulty(value, path33) {
16548
16459
  if (value === void 0) return void 0;
16549
16460
  if (value === "easy" || value === "medium" || value === "hard") {
16550
16461
  return value;
16551
16462
  }
16552
- throw new Error(`${path31} must be one of: easy, medium, hard`);
16463
+ throw new Error(`${path33} must be one of: easy, medium, hard`);
16553
16464
  }
16554
- function parseQueryTags(value, path31) {
16465
+ function parseQueryTags(value, path33) {
16555
16466
  if (value === void 0) return void 0;
16556
16467
  if (!isStringArray4(value) || value.some((tag) => tag.trim().length === 0)) {
16557
- throw new Error(`${path31} must be an array of non-empty strings`);
16468
+ throw new Error(`${path33} must be an array of non-empty strings`);
16558
16469
  }
16559
16470
  if (value.length > 16) {
16560
- throw new Error(`${path31} must contain at most 16 tags`);
16471
+ throw new Error(`${path33} must contain at most 16 tags`);
16561
16472
  }
16562
16473
  return value;
16563
16474
  }
16564
- function parseQueryArgs(value, path31) {
16475
+ function parseQueryArgs(value, path33) {
16565
16476
  if (value === void 0) return void 0;
16566
16477
  if (!isRecord3(value)) {
16567
- throw new Error(`${path31} must be an object`);
16568
- }
16569
- const symbol = parseStringOrUndefined(value.symbol, `${path31}.symbol`);
16570
- const filePath = parseStringOrUndefined(value.filePath, `${path31}.filePath`);
16571
- const fileType = parseStringOrUndefined(value.fileType, `${path31}.fileType`);
16572
- const directory = parseStringOrUndefined(value.directory, `${path31}.directory`);
16573
- const callerLimit = parsePositiveIntegerOrUndefined(value.callerLimit, `${path31}.callerLimit`);
16574
- const calleeLimit = parsePositiveIntegerOrUndefined(value.calleeLimit, `${path31}.calleeLimit`);
16575
- const tokenBudget = parsePositiveIntegerOrUndefined(value.tokenBudget, `${path31}.tokenBudget`);
16478
+ throw new Error(`${path33} must be an object`);
16479
+ }
16480
+ const symbol = parseStringOrUndefined(value.symbol, `${path33}.symbol`);
16481
+ const filePath = parseStringOrUndefined(value.filePath, `${path33}.filePath`);
16482
+ const fileType = parseStringOrUndefined(value.fileType, `${path33}.fileType`);
16483
+ const directory = parseStringOrUndefined(value.directory, `${path33}.directory`);
16484
+ const callerLimit = parsePositiveIntegerOrUndefined(value.callerLimit, `${path33}.callerLimit`);
16485
+ const calleeLimit = parsePositiveIntegerOrUndefined(value.calleeLimit, `${path33}.calleeLimit`);
16486
+ const tokenBudget = parsePositiveIntegerOrUndefined(value.tokenBudget, `${path33}.tokenBudget`);
16576
16487
  return {
16577
16488
  ...symbol !== void 0 ? { symbol } : {},
16578
16489
  ...filePath !== void 0 ? { filePath } : {},
@@ -16583,50 +16494,50 @@ function parseQueryArgs(value, path31) {
16583
16494
  ...tokenBudget !== void 0 ? { tokenBudget } : {}
16584
16495
  };
16585
16496
  }
16586
- function parsePositiveIntegerOrUndefined(value, path31) {
16497
+ function parsePositiveIntegerOrUndefined(value, path33) {
16587
16498
  if (value === void 0 || value === null) return void 0;
16588
16499
  if (typeof value !== "number" || !Number.isInteger(value) || value <= 0) {
16589
- throw new Error(`${path31} must be a positive integer`);
16500
+ throw new Error(`${path33} must be a positive integer`);
16590
16501
  }
16591
16502
  return value;
16592
16503
  }
16593
16504
  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-]+)*)?$/;
16594
- function parseSemanticVersion(value, path31) {
16505
+ function parseSemanticVersion(value, path33) {
16595
16506
  if (!isNonEmptyString(value)) {
16596
- throw new Error(`${path31} must be a non-empty string`);
16507
+ throw new Error(`${path33} must be a non-empty string`);
16597
16508
  }
16598
16509
  if (!SEMVER_VERSION_PATTERN.test(value)) {
16599
- throw new Error(`${path31} must be a valid semantic version (MAJOR.MINOR.PATCH)`);
16510
+ throw new Error(`${path33} must be a valid semantic version (MAJOR.MINOR.PATCH)`);
16600
16511
  }
16601
16512
  return value;
16602
16513
  }
16603
- function parseRetrievalMode(value, path31) {
16514
+ function parseRetrievalMode(value, path33) {
16604
16515
  if (value === void 0 || value === "search") return "search";
16605
16516
  if (value === "context" || value === "edit-context") return value;
16606
- throw new Error(`${path31} must be one of: search, context, edit-context`);
16517
+ throw new Error(`${path33} must be one of: search, context, edit-context`);
16607
16518
  }
16608
- function parseStringOrUndefined(value, path31) {
16519
+ function parseStringOrUndefined(value, path33) {
16609
16520
  if (value === void 0 || value === null) return void 0;
16610
16521
  if (!isNonEmptyString(value)) {
16611
- throw new Error(`${path31} must be a non-empty string`);
16522
+ throw new Error(`${path33} must be a non-empty string`);
16612
16523
  }
16613
16524
  return value;
16614
16525
  }
16615
- function parseGradedEvidence(value, path31) {
16526
+ function parseGradedEvidence(value, path33) {
16616
16527
  if (value === void 0) return [];
16617
16528
  if (!Array.isArray(value)) {
16618
- throw new Error(`${path31} must be an array`);
16529
+ throw new Error(`${path33} must be an array`);
16619
16530
  }
16620
16531
  return value.map((entry, index) => {
16621
16532
  if (!isRecord3(entry)) {
16622
- throw new Error(`${path31}[${index}] must be an object`);
16533
+ throw new Error(`${path33}[${index}] must be an object`);
16623
16534
  }
16624
- const evidencePath = parseStringOrUndefined(entry.path, `${path31}[${index}].path`);
16535
+ const evidencePath = parseStringOrUndefined(entry.path, `${path33}[${index}].path`);
16625
16536
  if (evidencePath === void 0) {
16626
- throw new Error(`${path31}[${index}].path is required`);
16537
+ throw new Error(`${path33}[${index}].path is required`);
16627
16538
  }
16628
- const symbol = parseStringOrUndefined(entry.symbol, `${path31}[${index}].symbol`);
16629
- const relevance = parseEvidenceRelevance(entry.relevance, `${path31}[${index}].relevance`);
16539
+ const symbol = parseStringOrUndefined(entry.symbol, `${path33}[${index}].symbol`);
16540
+ const relevance = parseEvidenceRelevance(entry.relevance, `${path33}[${index}].relevance`);
16630
16541
  return {
16631
16542
  path: evidencePath,
16632
16543
  ...symbol !== void 0 ? { symbol } : {},
@@ -16634,27 +16545,27 @@ function parseGradedEvidence(value, path31) {
16634
16545
  };
16635
16546
  });
16636
16547
  }
16637
- function parseEvidenceRelevance(value, path31) {
16548
+ function parseEvidenceRelevance(value, path33) {
16638
16549
  if (value === void 0) {
16639
- throw new Error(`${path31} is required`);
16550
+ throw new Error(`${path33} is required`);
16640
16551
  }
16641
16552
  if (value !== 1 && value !== 2 && value !== 3) {
16642
- throw new Error(`${path31} must be 1, 2, or 3`);
16553
+ throw new Error(`${path33} must be 1, 2, or 3`);
16643
16554
  }
16644
16555
  return value;
16645
16556
  }
16646
- function parseExpectedGraphNeighbor(value, path31) {
16557
+ function parseExpectedGraphNeighbor(value, path33) {
16647
16558
  if (value === void 0) return void 0;
16648
16559
  if (!isRecord3(value)) {
16649
- throw new Error(`${path31} must be an object`);
16560
+ throw new Error(`${path33} must be an object`);
16650
16561
  }
16651
16562
  if (value.direction !== "caller" && value.direction !== "callee") {
16652
- throw new Error(`${path31}.direction must be one of: caller, callee`);
16563
+ throw new Error(`${path33}.direction must be one of: caller, callee`);
16653
16564
  }
16654
- const filePath = parseStringOrUndefined(value.filePath, `${path31}.filePath`);
16655
- const symbol = parseStringOrUndefined(value.symbol, `${path31}.symbol`);
16565
+ const filePath = parseStringOrUndefined(value.filePath, `${path33}.filePath`);
16566
+ const symbol = parseStringOrUndefined(value.symbol, `${path33}.symbol`);
16656
16567
  if (filePath === void 0 && symbol === void 0) {
16657
- throw new Error(`${path31} must include filePath or symbol`);
16568
+ throw new Error(`${path33} must include filePath or symbol`);
16658
16569
  }
16659
16570
  return {
16660
16571
  direction: value.direction,
@@ -16662,9 +16573,9 @@ function parseExpectedGraphNeighbor(value, path31) {
16662
16573
  ...symbol !== void 0 ? { symbol } : {}
16663
16574
  };
16664
16575
  }
16665
- function parseExpected(input, path31) {
16576
+ function parseExpected(input, path33) {
16666
16577
  if (!isRecord3(input)) {
16667
- throw new Error(`${path31} must be an object`);
16578
+ throw new Error(`${path33} must be an object`);
16668
16579
  }
16669
16580
  const filePathRaw = input.filePath;
16670
16581
  const acceptableFilesRaw = input.acceptableFiles;
@@ -16675,29 +16586,29 @@ function parseExpected(input, path31) {
16675
16586
  const recoveryExpectationRaw = input.recoveryExpectation;
16676
16587
  const gradedEvidenceRaw = input.gradedEvidence;
16677
16588
  const graphNeighborRaw = input.graphNeighbor;
16678
- const filePath = parseStringOrUndefined(filePathRaw, `${path31}.filePath`);
16589
+ const filePath = parseStringOrUndefined(filePathRaw, `${path33}.filePath`);
16679
16590
  const acceptableFiles = isStringArray4(acceptableFilesRaw) ? acceptableFilesRaw : void 0;
16680
- const gradedEvidence = parseGradedEvidence(gradedEvidenceRaw, `${path31}.gradedEvidence`);
16681
- const graphNeighbor = parseExpectedGraphNeighbor(graphNeighborRaw, `${path31}.graphNeighbor`);
16682
- const expectedOutcome = parseExpectedOutcome(expectedOutcomeRaw, `${path31}.expectedOutcome`);
16591
+ const gradedEvidence = parseGradedEvidence(gradedEvidenceRaw, `${path33}.gradedEvidence`);
16592
+ const graphNeighbor = parseExpectedGraphNeighbor(graphNeighborRaw, `${path33}.graphNeighbor`);
16593
+ const expectedOutcome = parseExpectedOutcome(expectedOutcomeRaw, `${path33}.expectedOutcome`);
16683
16594
  if (expectedOutcome !== "no-results" && !filePath && (!acceptableFiles || acceptableFiles.length === 0) && gradedEvidence.length === 0) {
16684
16595
  throw new Error(
16685
- `${path31} must include expected.filePath, expected.acceptableFiles, or expected.gradedEvidence`
16596
+ `${path33} must include expected.filePath, expected.acceptableFiles, or expected.gradedEvidence`
16686
16597
  );
16687
16598
  }
16688
16599
  if (acceptableFilesRaw !== void 0 && !isStringArray4(acceptableFilesRaw)) {
16689
- throw new Error(`${path31}.acceptableFiles must be an array of strings`);
16600
+ throw new Error(`${path33}.acceptableFiles must be an array of strings`);
16690
16601
  }
16691
16602
  if (symbolRaw !== void 0 && typeof symbolRaw !== "string") {
16692
- throw new Error(`${path31}.symbol must be a string when provided`);
16603
+ throw new Error(`${path33}.symbol must be a string when provided`);
16693
16604
  }
16694
16605
  if (branchRaw !== void 0 && typeof branchRaw !== "string") {
16695
- throw new Error(`${path31}.branch must be a string when provided`);
16606
+ throw new Error(`${path33}.branch must be a string when provided`);
16696
16607
  }
16697
- const expectedRoute = parseExpectedRoute(expectedRouteRaw, `${path31}.expectedRoute`);
16608
+ const expectedRoute = parseExpectedRoute(expectedRouteRaw, `${path33}.expectedRoute`);
16698
16609
  const recoveryExpectation = parseRecoveryExpectation(
16699
16610
  recoveryExpectationRaw,
16700
- `${path31}.recoveryExpectation`
16611
+ `${path33}.recoveryExpectation`
16701
16612
  );
16702
16613
  return {
16703
16614
  filePath,
@@ -16711,13 +16622,13 @@ function parseExpected(input, path31) {
16711
16622
  ...graphNeighbor !== void 0 ? { graphNeighbor } : {}
16712
16623
  };
16713
16624
  }
16714
- function parseQueryLanguage(value, path31) {
16715
- return parseStringOrUndefined(value, path31);
16625
+ function parseQueryLanguage(value, path33) {
16626
+ return parseStringOrUndefined(value, path33);
16716
16627
  }
16717
16628
  function parseQuery(input, index) {
16718
- const path31 = `queries[${index}]`;
16629
+ const path33 = `queries[${index}]`;
16719
16630
  if (!isRecord3(input)) {
16720
- throw new Error(`${path31} must be an object`);
16631
+ throw new Error(`${path33} must be an object`);
16721
16632
  }
16722
16633
  const id = input.id;
16723
16634
  const query = input.query;
@@ -16729,21 +16640,21 @@ function parseQuery(input, index) {
16729
16640
  const tags = input.tags;
16730
16641
  const args = input.args;
16731
16642
  if (typeof id !== "string" || id.trim().length === 0) {
16732
- throw new Error(`${path31}.id must be a non-empty string`);
16643
+ throw new Error(`${path33}.id must be a non-empty string`);
16733
16644
  }
16734
16645
  if (typeof query !== "string" || query.trim().length === 0) {
16735
- throw new Error(`${path31}.query must be a non-empty string`);
16646
+ throw new Error(`${path33}.query must be a non-empty string`);
16736
16647
  }
16737
16648
  return {
16738
16649
  id,
16739
16650
  query,
16740
- queryType: parseQueryType(queryType, `${path31}.queryType`),
16741
- retrievalMode: parseRetrievalMode(retrievalMode, `${path31}.retrievalMode`),
16742
- language: parseQueryLanguage(language, `${path31}.language`),
16743
- difficulty: parseQueryDifficulty(difficulty, `${path31}.difficulty`),
16744
- args: parseQueryArgs(args, `${path31}.args`),
16745
- tags: parseQueryTags(tags, `${path31}.tags`),
16746
- expected: parseExpected(expected, `${path31}.expected`)
16651
+ queryType: parseQueryType(queryType, `${path33}.queryType`),
16652
+ retrievalMode: parseRetrievalMode(retrievalMode, `${path33}.retrievalMode`),
16653
+ language: parseQueryLanguage(language, `${path33}.language`),
16654
+ difficulty: parseQueryDifficulty(difficulty, `${path33}.difficulty`),
16655
+ args: parseQueryArgs(args, `${path33}.args`),
16656
+ tags: parseQueryTags(tags, `${path33}.tags`),
16657
+ expected: parseExpected(expected, `${path33}.expected`)
16747
16658
  };
16748
16659
  }
16749
16660
  function parseGoldenDataset(raw, sourceLabel) {
@@ -17709,7 +17620,7 @@ async function executeCallGraph(projectRoot, host, args) {
17709
17620
  return { text: formatCallGraphResult(await getCallGraphData(projectRoot, host, args)) };
17710
17621
  }
17711
17622
  async function executeCallGraphPath(projectRoot, host, args) {
17712
- const path31 = await getCallGraphPath(
17623
+ const path33 = await getCallGraphPath(
17713
17624
  projectRoot,
17714
17625
  host,
17715
17626
  args.from,
@@ -17718,7 +17629,7 @@ async function executeCallGraphPath(projectRoot, host, args) {
17718
17629
  args.fromFilePath,
17719
17630
  args.toFilePath
17720
17631
  );
17721
- return { text: formatCallGraphPathResult(path31) };
17632
+ return { text: formatCallGraphPathResult(path33) };
17722
17633
  }
17723
17634
  async function executeCodeCommunities(projectRoot, host, args) {
17724
17635
  const result = await getCodeCommunities(projectRoot, host, args);
@@ -18205,7 +18116,7 @@ function createMcpServer(projectRoot, config, host) {
18205
18116
  }
18206
18117
 
18207
18118
  // src/watcher/file-watcher.ts
18208
- import { existsSync as existsSync15 } from "fs";
18119
+ import { existsSync as existsSync15, statSync as statSync6 } from "fs";
18209
18120
 
18210
18121
  // node_modules/chokidar/index.js
18211
18122
  import { EventEmitter as EventEmitter2 } from "events";
@@ -18297,7 +18208,7 @@ var ReaddirpStream = class extends Readable {
18297
18208
  this._directoryFilter = normalizeFilter(opts.directoryFilter);
18298
18209
  const statMethod = opts.lstat ? lstat : stat;
18299
18210
  if (wantBigintFsStats) {
18300
- this._stat = (path31) => statMethod(path31, { bigint: true });
18211
+ this._stat = (path33) => statMethod(path33, { bigint: true });
18301
18212
  } else {
18302
18213
  this._stat = statMethod;
18303
18214
  }
@@ -18322,8 +18233,8 @@ var ReaddirpStream = class extends Readable {
18322
18233
  const par = this.parent;
18323
18234
  const fil = par && par.files;
18324
18235
  if (fil && fil.length > 0) {
18325
- const { path: path31, depth } = par;
18326
- const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path31));
18236
+ const { path: path33, depth } = par;
18237
+ const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path33));
18327
18238
  const awaited = await Promise.all(slice);
18328
18239
  for (const entry of awaited) {
18329
18240
  if (!entry)
@@ -18363,20 +18274,20 @@ var ReaddirpStream = class extends Readable {
18363
18274
  this.reading = false;
18364
18275
  }
18365
18276
  }
18366
- async _exploreDir(path31, depth) {
18277
+ async _exploreDir(path33, depth) {
18367
18278
  let files;
18368
18279
  try {
18369
- files = await readdir(path31, this._rdOptions);
18280
+ files = await readdir(path33, this._rdOptions);
18370
18281
  } catch (error) {
18371
18282
  this._onError(error);
18372
18283
  }
18373
- return { files, depth, path: path31 };
18284
+ return { files, depth, path: path33 };
18374
18285
  }
18375
- async _formatEntry(dirent, path31) {
18286
+ async _formatEntry(dirent, path33) {
18376
18287
  let entry;
18377
18288
  const basename8 = this._isDirent ? dirent.name : dirent;
18378
18289
  try {
18379
- const fullPath = presolve(pjoin(path31, basename8));
18290
+ const fullPath = presolve(pjoin(path33, basename8));
18380
18291
  entry = { path: prelative(this._root, fullPath), fullPath, basename: basename8 };
18381
18292
  entry[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath);
18382
18293
  } catch (err) {
@@ -18776,16 +18687,16 @@ var delFromSet = (main, prop, item) => {
18776
18687
  };
18777
18688
  var isEmptySet = (val) => val instanceof Set ? val.size === 0 : !val;
18778
18689
  var FsWatchInstances = /* @__PURE__ */ new Map();
18779
- function createFsWatchInstance(path31, options, listener, errHandler, emitRaw) {
18690
+ function createFsWatchInstance(path33, options, listener, errHandler, emitRaw) {
18780
18691
  const handleEvent = (rawEvent, evPath) => {
18781
- listener(path31);
18782
- emitRaw(rawEvent, evPath, { watchedPath: path31 });
18783
- if (evPath && path31 !== evPath) {
18784
- fsWatchBroadcast(sp.resolve(path31, evPath), KEY_LISTENERS, sp.join(path31, evPath));
18692
+ listener(path33);
18693
+ emitRaw(rawEvent, evPath, { watchedPath: path33 });
18694
+ if (evPath && path33 !== evPath) {
18695
+ fsWatchBroadcast(sp.resolve(path33, evPath), KEY_LISTENERS, sp.join(path33, evPath));
18785
18696
  }
18786
18697
  };
18787
18698
  try {
18788
- return fs_watch(path31, {
18699
+ return fs_watch(path33, {
18789
18700
  persistent: options.persistent
18790
18701
  }, handleEvent);
18791
18702
  } catch (error) {
@@ -18801,12 +18712,12 @@ var fsWatchBroadcast = (fullPath, listenerType, val1, val2, val3) => {
18801
18712
  listener(val1, val2, val3);
18802
18713
  });
18803
18714
  };
18804
- var setFsWatchListener = (path31, fullPath, options, handlers) => {
18715
+ var setFsWatchListener = (path33, fullPath, options, handlers) => {
18805
18716
  const { listener, errHandler, rawEmitter } = handlers;
18806
18717
  let cont = FsWatchInstances.get(fullPath);
18807
18718
  let watcher;
18808
18719
  if (!options.persistent) {
18809
- watcher = createFsWatchInstance(path31, options, listener, errHandler, rawEmitter);
18720
+ watcher = createFsWatchInstance(path33, options, listener, errHandler, rawEmitter);
18810
18721
  if (!watcher)
18811
18722
  return;
18812
18723
  return watcher.close.bind(watcher);
@@ -18817,7 +18728,7 @@ var setFsWatchListener = (path31, fullPath, options, handlers) => {
18817
18728
  addAndConvert(cont, KEY_RAW, rawEmitter);
18818
18729
  } else {
18819
18730
  watcher = createFsWatchInstance(
18820
- path31,
18731
+ path33,
18821
18732
  options,
18822
18733
  fsWatchBroadcast.bind(null, fullPath, KEY_LISTENERS),
18823
18734
  errHandler,
@@ -18832,7 +18743,7 @@ var setFsWatchListener = (path31, fullPath, options, handlers) => {
18832
18743
  cont.watcherUnusable = true;
18833
18744
  if (isWindows && error.code === "EPERM") {
18834
18745
  try {
18835
- const fd = await open(path31, "r");
18746
+ const fd = await open(path33, "r");
18836
18747
  await fd.close();
18837
18748
  broadcastErr(error);
18838
18749
  } catch (err) {
@@ -18863,7 +18774,7 @@ var setFsWatchListener = (path31, fullPath, options, handlers) => {
18863
18774
  };
18864
18775
  };
18865
18776
  var FsWatchFileInstances = /* @__PURE__ */ new Map();
18866
- var setFsWatchFileListener = (path31, fullPath, options, handlers) => {
18777
+ var setFsWatchFileListener = (path33, fullPath, options, handlers) => {
18867
18778
  const { listener, rawEmitter } = handlers;
18868
18779
  let cont = FsWatchFileInstances.get(fullPath);
18869
18780
  const copts = cont && cont.options;
@@ -18885,7 +18796,7 @@ var setFsWatchFileListener = (path31, fullPath, options, handlers) => {
18885
18796
  });
18886
18797
  const currmtime = curr.mtimeMs;
18887
18798
  if (curr.size !== prev.size || currmtime > prev.mtimeMs || currmtime === 0) {
18888
- foreach(cont.listeners, (listener2) => listener2(path31, curr));
18799
+ foreach(cont.listeners, (listener2) => listener2(path33, curr));
18889
18800
  }
18890
18801
  })
18891
18802
  };
@@ -18915,13 +18826,13 @@ var NodeFsHandler = class {
18915
18826
  * @param listener on fs change
18916
18827
  * @returns closer for the watcher instance
18917
18828
  */
18918
- _watchWithNodeFs(path31, listener) {
18829
+ _watchWithNodeFs(path33, listener) {
18919
18830
  const opts = this.fsw.options;
18920
- const directory = sp.dirname(path31);
18921
- const basename8 = sp.basename(path31);
18831
+ const directory = sp.dirname(path33);
18832
+ const basename8 = sp.basename(path33);
18922
18833
  const parent = this.fsw._getWatchedDir(directory);
18923
18834
  parent.add(basename8);
18924
- const absolutePath = sp.resolve(path31);
18835
+ const absolutePath = sp.resolve(path33);
18925
18836
  const options = {
18926
18837
  persistent: opts.persistent
18927
18838
  };
@@ -18931,12 +18842,12 @@ var NodeFsHandler = class {
18931
18842
  if (opts.usePolling) {
18932
18843
  const enableBin = opts.interval !== opts.binaryInterval;
18933
18844
  options.interval = enableBin && isBinaryPath(basename8) ? opts.binaryInterval : opts.interval;
18934
- closer = setFsWatchFileListener(path31, absolutePath, options, {
18845
+ closer = setFsWatchFileListener(path33, absolutePath, options, {
18935
18846
  listener,
18936
18847
  rawEmitter: this.fsw._emitRaw
18937
18848
  });
18938
18849
  } else {
18939
- closer = setFsWatchListener(path31, absolutePath, options, {
18850
+ closer = setFsWatchListener(path33, absolutePath, options, {
18940
18851
  listener,
18941
18852
  errHandler: this._boundHandleError,
18942
18853
  rawEmitter: this.fsw._emitRaw
@@ -18958,7 +18869,7 @@ var NodeFsHandler = class {
18958
18869
  let prevStats = stats;
18959
18870
  if (parent.has(basename8))
18960
18871
  return;
18961
- const listener = async (path31, newStats) => {
18872
+ const listener = async (path33, newStats) => {
18962
18873
  if (!this.fsw._throttle(THROTTLE_MODE_WATCH, file, 5))
18963
18874
  return;
18964
18875
  if (!newStats || newStats.mtimeMs === 0) {
@@ -18972,11 +18883,11 @@ var NodeFsHandler = class {
18972
18883
  this.fsw._emit(EV.CHANGE, file, newStats2);
18973
18884
  }
18974
18885
  if ((isMacos || isLinux || isFreeBSD) && prevStats.ino !== newStats2.ino) {
18975
- this.fsw._closeFile(path31);
18886
+ this.fsw._closeFile(path33);
18976
18887
  prevStats = newStats2;
18977
18888
  const closer2 = this._watchWithNodeFs(file, listener);
18978
18889
  if (closer2)
18979
- this.fsw._addPathCloser(path31, closer2);
18890
+ this.fsw._addPathCloser(path33, closer2);
18980
18891
  } else {
18981
18892
  prevStats = newStats2;
18982
18893
  }
@@ -19008,7 +18919,7 @@ var NodeFsHandler = class {
19008
18919
  * @param item basename of this item
19009
18920
  * @returns true if no more processing is needed for this entry.
19010
18921
  */
19011
- async _handleSymlink(entry, directory, path31, item) {
18922
+ async _handleSymlink(entry, directory, path33, item) {
19012
18923
  if (this.fsw.closed) {
19013
18924
  return;
19014
18925
  }
@@ -19018,7 +18929,7 @@ var NodeFsHandler = class {
19018
18929
  this.fsw._incrReadyCount();
19019
18930
  let linkPath;
19020
18931
  try {
19021
- linkPath = await fsrealpath(path31);
18932
+ linkPath = await fsrealpath(path33);
19022
18933
  } catch (e) {
19023
18934
  this.fsw._emitReady();
19024
18935
  return true;
@@ -19028,12 +18939,12 @@ var NodeFsHandler = class {
19028
18939
  if (dir.has(item)) {
19029
18940
  if (this.fsw._symlinkPaths.get(full) !== linkPath) {
19030
18941
  this.fsw._symlinkPaths.set(full, linkPath);
19031
- this.fsw._emit(EV.CHANGE, path31, entry.stats);
18942
+ this.fsw._emit(EV.CHANGE, path33, entry.stats);
19032
18943
  }
19033
18944
  } else {
19034
18945
  dir.add(item);
19035
18946
  this.fsw._symlinkPaths.set(full, linkPath);
19036
- this.fsw._emit(EV.ADD, path31, entry.stats);
18947
+ this.fsw._emit(EV.ADD, path33, entry.stats);
19037
18948
  }
19038
18949
  this.fsw._emitReady();
19039
18950
  return true;
@@ -19063,9 +18974,9 @@ var NodeFsHandler = class {
19063
18974
  return;
19064
18975
  }
19065
18976
  const item = entry.path;
19066
- let path31 = sp.join(directory, item);
18977
+ let path33 = sp.join(directory, item);
19067
18978
  current.add(item);
19068
- if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path31, item)) {
18979
+ if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path33, item)) {
19069
18980
  return;
19070
18981
  }
19071
18982
  if (this.fsw.closed) {
@@ -19074,11 +18985,11 @@ var NodeFsHandler = class {
19074
18985
  }
19075
18986
  if (item === target || !target && !previous.has(item)) {
19076
18987
  this.fsw._incrReadyCount();
19077
- path31 = sp.join(dir, sp.relative(dir, path31));
19078
- this._addToNodeFs(path31, initialAdd, wh, depth + 1);
18988
+ path33 = sp.join(dir, sp.relative(dir, path33));
18989
+ this._addToNodeFs(path33, initialAdd, wh, depth + 1);
19079
18990
  }
19080
18991
  }).on(EV.ERROR, this._boundHandleError);
19081
- return new Promise((resolve18, reject) => {
18992
+ return new Promise((resolve20, reject) => {
19082
18993
  if (!stream)
19083
18994
  return reject();
19084
18995
  stream.once(STR_END, () => {
@@ -19087,7 +18998,7 @@ var NodeFsHandler = class {
19087
18998
  return;
19088
18999
  }
19089
19000
  const wasThrottled = throttler ? throttler.clear() : false;
19090
- resolve18(void 0);
19001
+ resolve20(void 0);
19091
19002
  previous.getChildren().filter((item) => {
19092
19003
  return item !== directory && !current.has(item);
19093
19004
  }).forEach((item) => {
@@ -19144,13 +19055,13 @@ var NodeFsHandler = class {
19144
19055
  * @param depth Child path actually targeted for watch
19145
19056
  * @param target Child path actually targeted for watch
19146
19057
  */
19147
- async _addToNodeFs(path31, initialAdd, priorWh, depth, target) {
19058
+ async _addToNodeFs(path33, initialAdd, priorWh, depth, target) {
19148
19059
  const ready = this.fsw._emitReady;
19149
- if (this.fsw._isIgnored(path31) || this.fsw.closed) {
19060
+ if (this.fsw._isIgnored(path33) || this.fsw.closed) {
19150
19061
  ready();
19151
19062
  return false;
19152
19063
  }
19153
- const wh = this.fsw._getWatchHelpers(path31);
19064
+ const wh = this.fsw._getWatchHelpers(path33);
19154
19065
  if (priorWh) {
19155
19066
  wh.filterPath = (entry) => priorWh.filterPath(entry);
19156
19067
  wh.filterDir = (entry) => priorWh.filterDir(entry);
@@ -19166,8 +19077,8 @@ var NodeFsHandler = class {
19166
19077
  const follow = this.fsw.options.followSymlinks;
19167
19078
  let closer;
19168
19079
  if (stats.isDirectory()) {
19169
- const absPath = sp.resolve(path31);
19170
- const targetPath = follow ? await fsrealpath(path31) : path31;
19080
+ const absPath = sp.resolve(path33);
19081
+ const targetPath = follow ? await fsrealpath(path33) : path33;
19171
19082
  if (this.fsw.closed)
19172
19083
  return;
19173
19084
  closer = await this._handleDir(wh.watchPath, stats, initialAdd, depth, target, wh, targetPath);
@@ -19177,29 +19088,29 @@ var NodeFsHandler = class {
19177
19088
  this.fsw._symlinkPaths.set(absPath, targetPath);
19178
19089
  }
19179
19090
  } else if (stats.isSymbolicLink()) {
19180
- const targetPath = follow ? await fsrealpath(path31) : path31;
19091
+ const targetPath = follow ? await fsrealpath(path33) : path33;
19181
19092
  if (this.fsw.closed)
19182
19093
  return;
19183
19094
  const parent = sp.dirname(wh.watchPath);
19184
19095
  this.fsw._getWatchedDir(parent).add(wh.watchPath);
19185
19096
  this.fsw._emit(EV.ADD, wh.watchPath, stats);
19186
- closer = await this._handleDir(parent, stats, initialAdd, depth, path31, wh, targetPath);
19097
+ closer = await this._handleDir(parent, stats, initialAdd, depth, path33, wh, targetPath);
19187
19098
  if (this.fsw.closed)
19188
19099
  return;
19189
19100
  if (targetPath !== void 0) {
19190
- this.fsw._symlinkPaths.set(sp.resolve(path31), targetPath);
19101
+ this.fsw._symlinkPaths.set(sp.resolve(path33), targetPath);
19191
19102
  }
19192
19103
  } else {
19193
19104
  closer = this._handleFile(wh.watchPath, stats, initialAdd);
19194
19105
  }
19195
19106
  ready();
19196
19107
  if (closer)
19197
- this.fsw._addPathCloser(path31, closer);
19108
+ this.fsw._addPathCloser(path33, closer);
19198
19109
  return false;
19199
19110
  } catch (error) {
19200
19111
  if (this.fsw._handleError(error)) {
19201
19112
  ready();
19202
- return path31;
19113
+ return path33;
19203
19114
  }
19204
19115
  }
19205
19116
  }
@@ -19231,35 +19142,35 @@ function createPattern(matcher) {
19231
19142
  if (matcher.path === string)
19232
19143
  return true;
19233
19144
  if (matcher.recursive) {
19234
- const relative12 = sp2.relative(matcher.path, string);
19235
- if (!relative12) {
19145
+ const relative14 = sp2.relative(matcher.path, string);
19146
+ if (!relative14) {
19236
19147
  return false;
19237
19148
  }
19238
- return !relative12.startsWith("..") && !sp2.isAbsolute(relative12);
19149
+ return !relative14.startsWith("..") && !sp2.isAbsolute(relative14);
19239
19150
  }
19240
19151
  return false;
19241
19152
  };
19242
19153
  }
19243
19154
  return () => false;
19244
19155
  }
19245
- function normalizePath3(path31) {
19246
- if (typeof path31 !== "string")
19156
+ function normalizePath3(path33) {
19157
+ if (typeof path33 !== "string")
19247
19158
  throw new Error("string expected");
19248
- path31 = sp2.normalize(path31);
19249
- path31 = path31.replace(/\\/g, "/");
19159
+ path33 = sp2.normalize(path33);
19160
+ path33 = path33.replace(/\\/g, "/");
19250
19161
  let prepend = false;
19251
- if (path31.startsWith("//"))
19162
+ if (path33.startsWith("//"))
19252
19163
  prepend = true;
19253
- path31 = path31.replace(DOUBLE_SLASH_RE, "/");
19164
+ path33 = path33.replace(DOUBLE_SLASH_RE, "/");
19254
19165
  if (prepend)
19255
- path31 = "/" + path31;
19256
- return path31;
19166
+ path33 = "/" + path33;
19167
+ return path33;
19257
19168
  }
19258
19169
  function matchPatterns(patterns, testString, stats) {
19259
- const path31 = normalizePath3(testString);
19170
+ const path33 = normalizePath3(testString);
19260
19171
  for (let index = 0; index < patterns.length; index++) {
19261
19172
  const pattern = patterns[index];
19262
- if (pattern(path31, stats)) {
19173
+ if (pattern(path33, stats)) {
19263
19174
  return true;
19264
19175
  }
19265
19176
  }
@@ -19297,19 +19208,19 @@ var toUnix = (string) => {
19297
19208
  }
19298
19209
  return str;
19299
19210
  };
19300
- var normalizePathToUnix = (path31) => toUnix(sp2.normalize(toUnix(path31)));
19301
- var normalizeIgnored = (cwd = "") => (path31) => {
19302
- if (typeof path31 === "string") {
19303
- return normalizePathToUnix(sp2.isAbsolute(path31) ? path31 : sp2.join(cwd, path31));
19211
+ var normalizePathToUnix = (path33) => toUnix(sp2.normalize(toUnix(path33)));
19212
+ var normalizeIgnored = (cwd = "") => (path33) => {
19213
+ if (typeof path33 === "string") {
19214
+ return normalizePathToUnix(sp2.isAbsolute(path33) ? path33 : sp2.join(cwd, path33));
19304
19215
  } else {
19305
- return path31;
19216
+ return path33;
19306
19217
  }
19307
19218
  };
19308
- var getAbsolutePath = (path31, cwd) => {
19309
- if (sp2.isAbsolute(path31)) {
19310
- return path31;
19219
+ var getAbsolutePath = (path33, cwd) => {
19220
+ if (sp2.isAbsolute(path33)) {
19221
+ return path33;
19311
19222
  }
19312
- return sp2.join(cwd, path31);
19223
+ return sp2.join(cwd, path33);
19313
19224
  };
19314
19225
  var EMPTY_SET = Object.freeze(/* @__PURE__ */ new Set());
19315
19226
  var DirEntry = class {
@@ -19374,10 +19285,10 @@ var WatchHelper = class {
19374
19285
  dirParts;
19375
19286
  followSymlinks;
19376
19287
  statMethod;
19377
- constructor(path31, follow, fsw) {
19288
+ constructor(path33, follow, fsw) {
19378
19289
  this.fsw = fsw;
19379
- const watchPath = path31;
19380
- this.path = path31 = path31.replace(REPLACER_RE, "");
19290
+ const watchPath = path33;
19291
+ this.path = path33 = path33.replace(REPLACER_RE, "");
19381
19292
  this.watchPath = watchPath;
19382
19293
  this.fullWatchPath = sp2.resolve(watchPath);
19383
19294
  this.dirParts = [];
@@ -19517,20 +19428,20 @@ var FSWatcher = class extends EventEmitter2 {
19517
19428
  this._closePromise = void 0;
19518
19429
  let paths = unifyPaths(paths_);
19519
19430
  if (cwd) {
19520
- paths = paths.map((path31) => {
19521
- const absPath = getAbsolutePath(path31, cwd);
19431
+ paths = paths.map((path33) => {
19432
+ const absPath = getAbsolutePath(path33, cwd);
19522
19433
  return absPath;
19523
19434
  });
19524
19435
  }
19525
- paths.forEach((path31) => {
19526
- this._removeIgnoredPath(path31);
19436
+ paths.forEach((path33) => {
19437
+ this._removeIgnoredPath(path33);
19527
19438
  });
19528
19439
  this._userIgnored = void 0;
19529
19440
  if (!this._readyCount)
19530
19441
  this._readyCount = 0;
19531
19442
  this._readyCount += paths.length;
19532
- Promise.all(paths.map(async (path31) => {
19533
- const res = await this._nodeFsHandler._addToNodeFs(path31, !_internal, void 0, 0, _origAdd);
19443
+ Promise.all(paths.map(async (path33) => {
19444
+ const res = await this._nodeFsHandler._addToNodeFs(path33, !_internal, void 0, 0, _origAdd);
19534
19445
  if (res)
19535
19446
  this._emitReady();
19536
19447
  return res;
@@ -19552,17 +19463,17 @@ var FSWatcher = class extends EventEmitter2 {
19552
19463
  return this;
19553
19464
  const paths = unifyPaths(paths_);
19554
19465
  const { cwd } = this.options;
19555
- paths.forEach((path31) => {
19556
- if (!sp2.isAbsolute(path31) && !this._closers.has(path31)) {
19466
+ paths.forEach((path33) => {
19467
+ if (!sp2.isAbsolute(path33) && !this._closers.has(path33)) {
19557
19468
  if (cwd)
19558
- path31 = sp2.join(cwd, path31);
19559
- path31 = sp2.resolve(path31);
19469
+ path33 = sp2.join(cwd, path33);
19470
+ path33 = sp2.resolve(path33);
19560
19471
  }
19561
- this._closePath(path31);
19562
- this._addIgnoredPath(path31);
19563
- if (this._watched.has(path31)) {
19472
+ this._closePath(path33);
19473
+ this._addIgnoredPath(path33);
19474
+ if (this._watched.has(path33)) {
19564
19475
  this._addIgnoredPath({
19565
- path: path31,
19476
+ path: path33,
19566
19477
  recursive: true
19567
19478
  });
19568
19479
  }
@@ -19626,38 +19537,38 @@ var FSWatcher = class extends EventEmitter2 {
19626
19537
  * @param stats arguments to be passed with event
19627
19538
  * @returns the error if defined, otherwise the value of the FSWatcher instance's `closed` flag
19628
19539
  */
19629
- async _emit(event, path31, stats) {
19540
+ async _emit(event, path33, stats) {
19630
19541
  if (this.closed)
19631
19542
  return;
19632
19543
  const opts = this.options;
19633
19544
  if (isWindows)
19634
- path31 = sp2.normalize(path31);
19545
+ path33 = sp2.normalize(path33);
19635
19546
  if (opts.cwd)
19636
- path31 = sp2.relative(opts.cwd, path31);
19637
- const args = [path31];
19547
+ path33 = sp2.relative(opts.cwd, path33);
19548
+ const args = [path33];
19638
19549
  if (stats != null)
19639
19550
  args.push(stats);
19640
19551
  const awf = opts.awaitWriteFinish;
19641
19552
  let pw;
19642
- if (awf && (pw = this._pendingWrites.get(path31))) {
19553
+ if (awf && (pw = this._pendingWrites.get(path33))) {
19643
19554
  pw.lastChange = /* @__PURE__ */ new Date();
19644
19555
  return this;
19645
19556
  }
19646
19557
  if (opts.atomic) {
19647
19558
  if (event === EVENTS.UNLINK) {
19648
- this._pendingUnlinks.set(path31, [event, ...args]);
19559
+ this._pendingUnlinks.set(path33, [event, ...args]);
19649
19560
  setTimeout(() => {
19650
- this._pendingUnlinks.forEach((entry, path32) => {
19561
+ this._pendingUnlinks.forEach((entry, path34) => {
19651
19562
  this.emit(...entry);
19652
19563
  this.emit(EVENTS.ALL, ...entry);
19653
- this._pendingUnlinks.delete(path32);
19564
+ this._pendingUnlinks.delete(path34);
19654
19565
  });
19655
19566
  }, typeof opts.atomic === "number" ? opts.atomic : 100);
19656
19567
  return this;
19657
19568
  }
19658
- if (event === EVENTS.ADD && this._pendingUnlinks.has(path31)) {
19569
+ if (event === EVENTS.ADD && this._pendingUnlinks.has(path33)) {
19659
19570
  event = EVENTS.CHANGE;
19660
- this._pendingUnlinks.delete(path31);
19571
+ this._pendingUnlinks.delete(path33);
19661
19572
  }
19662
19573
  }
19663
19574
  if (awf && (event === EVENTS.ADD || event === EVENTS.CHANGE) && this._readyEmitted) {
@@ -19675,16 +19586,16 @@ var FSWatcher = class extends EventEmitter2 {
19675
19586
  this.emitWithAll(event, args);
19676
19587
  }
19677
19588
  };
19678
- this._awaitWriteFinish(path31, awf.stabilityThreshold, event, awfEmit);
19589
+ this._awaitWriteFinish(path33, awf.stabilityThreshold, event, awfEmit);
19679
19590
  return this;
19680
19591
  }
19681
19592
  if (event === EVENTS.CHANGE) {
19682
- const isThrottled = !this._throttle(EVENTS.CHANGE, path31, 50);
19593
+ const isThrottled = !this._throttle(EVENTS.CHANGE, path33, 50);
19683
19594
  if (isThrottled)
19684
19595
  return this;
19685
19596
  }
19686
19597
  if (opts.alwaysStat && stats === void 0 && (event === EVENTS.ADD || event === EVENTS.ADD_DIR || event === EVENTS.CHANGE)) {
19687
- const fullPath = opts.cwd ? sp2.join(opts.cwd, path31) : path31;
19598
+ const fullPath = opts.cwd ? sp2.join(opts.cwd, path33) : path33;
19688
19599
  let stats2;
19689
19600
  try {
19690
19601
  stats2 = await stat3(fullPath);
@@ -19715,23 +19626,23 @@ var FSWatcher = class extends EventEmitter2 {
19715
19626
  * @param timeout duration of time to suppress duplicate actions
19716
19627
  * @returns tracking object or false if action should be suppressed
19717
19628
  */
19718
- _throttle(actionType, path31, timeout) {
19629
+ _throttle(actionType, path33, timeout) {
19719
19630
  if (!this._throttled.has(actionType)) {
19720
19631
  this._throttled.set(actionType, /* @__PURE__ */ new Map());
19721
19632
  }
19722
19633
  const action = this._throttled.get(actionType);
19723
19634
  if (!action)
19724
19635
  throw new Error("invalid throttle");
19725
- const actionPath = action.get(path31);
19636
+ const actionPath = action.get(path33);
19726
19637
  if (actionPath) {
19727
19638
  actionPath.count++;
19728
19639
  return false;
19729
19640
  }
19730
19641
  let timeoutObject;
19731
19642
  const clear = () => {
19732
- const item = action.get(path31);
19643
+ const item = action.get(path33);
19733
19644
  const count = item ? item.count : 0;
19734
- action.delete(path31);
19645
+ action.delete(path33);
19735
19646
  clearTimeout(timeoutObject);
19736
19647
  if (item)
19737
19648
  clearTimeout(item.timeoutObject);
@@ -19739,7 +19650,7 @@ var FSWatcher = class extends EventEmitter2 {
19739
19650
  };
19740
19651
  timeoutObject = setTimeout(clear, timeout);
19741
19652
  const thr = { timeoutObject, clear, count: 0 };
19742
- action.set(path31, thr);
19653
+ action.set(path33, thr);
19743
19654
  return thr;
19744
19655
  }
19745
19656
  _incrReadyCount() {
@@ -19753,44 +19664,44 @@ var FSWatcher = class extends EventEmitter2 {
19753
19664
  * @param event
19754
19665
  * @param awfEmit Callback to be called when ready for event to be emitted.
19755
19666
  */
19756
- _awaitWriteFinish(path31, threshold, event, awfEmit) {
19667
+ _awaitWriteFinish(path33, threshold, event, awfEmit) {
19757
19668
  const awf = this.options.awaitWriteFinish;
19758
19669
  if (typeof awf !== "object")
19759
19670
  return;
19760
19671
  const pollInterval = awf.pollInterval;
19761
19672
  let timeoutHandler;
19762
- let fullPath = path31;
19763
- if (this.options.cwd && !sp2.isAbsolute(path31)) {
19764
- fullPath = sp2.join(this.options.cwd, path31);
19673
+ let fullPath = path33;
19674
+ if (this.options.cwd && !sp2.isAbsolute(path33)) {
19675
+ fullPath = sp2.join(this.options.cwd, path33);
19765
19676
  }
19766
19677
  const now2 = /* @__PURE__ */ new Date();
19767
19678
  const writes = this._pendingWrites;
19768
19679
  function awaitWriteFinishFn(prevStat) {
19769
19680
  statcb(fullPath, (err, curStat) => {
19770
- if (err || !writes.has(path31)) {
19681
+ if (err || !writes.has(path33)) {
19771
19682
  if (err && err.code !== "ENOENT")
19772
19683
  awfEmit(err);
19773
19684
  return;
19774
19685
  }
19775
19686
  const now3 = Number(/* @__PURE__ */ new Date());
19776
19687
  if (prevStat && curStat.size !== prevStat.size) {
19777
- writes.get(path31).lastChange = now3;
19688
+ writes.get(path33).lastChange = now3;
19778
19689
  }
19779
- const pw = writes.get(path31);
19690
+ const pw = writes.get(path33);
19780
19691
  const df = now3 - pw.lastChange;
19781
19692
  if (df >= threshold) {
19782
- writes.delete(path31);
19693
+ writes.delete(path33);
19783
19694
  awfEmit(void 0, curStat);
19784
19695
  } else {
19785
19696
  timeoutHandler = setTimeout(awaitWriteFinishFn, pollInterval, curStat);
19786
19697
  }
19787
19698
  });
19788
19699
  }
19789
- if (!writes.has(path31)) {
19790
- writes.set(path31, {
19700
+ if (!writes.has(path33)) {
19701
+ writes.set(path33, {
19791
19702
  lastChange: now2,
19792
19703
  cancelWait: () => {
19793
- writes.delete(path31);
19704
+ writes.delete(path33);
19794
19705
  clearTimeout(timeoutHandler);
19795
19706
  return event;
19796
19707
  }
@@ -19801,8 +19712,8 @@ var FSWatcher = class extends EventEmitter2 {
19801
19712
  /**
19802
19713
  * Determines whether user has asked to ignore this path.
19803
19714
  */
19804
- _isIgnored(path31, stats) {
19805
- if (this.options.atomic && DOT_RE.test(path31))
19715
+ _isIgnored(path33, stats) {
19716
+ if (this.options.atomic && DOT_RE.test(path33))
19806
19717
  return true;
19807
19718
  if (!this._userIgnored) {
19808
19719
  const { cwd } = this.options;
@@ -19812,17 +19723,17 @@ var FSWatcher = class extends EventEmitter2 {
19812
19723
  const list = [...ignoredPaths.map(normalizeIgnored(cwd)), ...ignored];
19813
19724
  this._userIgnored = anymatch(list, void 0);
19814
19725
  }
19815
- return this._userIgnored(path31, stats);
19726
+ return this._userIgnored(path33, stats);
19816
19727
  }
19817
- _isntIgnored(path31, stat4) {
19818
- return !this._isIgnored(path31, stat4);
19728
+ _isntIgnored(path33, stat5) {
19729
+ return !this._isIgnored(path33, stat5);
19819
19730
  }
19820
19731
  /**
19821
19732
  * Provides a set of common helpers and properties relating to symlink handling.
19822
19733
  * @param path file or directory pattern being watched
19823
19734
  */
19824
- _getWatchHelpers(path31) {
19825
- return new WatchHelper(path31, this.options.followSymlinks, this);
19735
+ _getWatchHelpers(path33) {
19736
+ return new WatchHelper(path33, this.options.followSymlinks, this);
19826
19737
  }
19827
19738
  // Directory helpers
19828
19739
  // -----------------
@@ -19854,63 +19765,63 @@ var FSWatcher = class extends EventEmitter2 {
19854
19765
  * @param item base path of item/directory
19855
19766
  */
19856
19767
  _remove(directory, item, isDirectory) {
19857
- const path31 = sp2.join(directory, item);
19858
- const fullPath = sp2.resolve(path31);
19859
- isDirectory = isDirectory != null ? isDirectory : this._watched.has(path31) || this._watched.has(fullPath);
19860
- if (!this._throttle("remove", path31, 100))
19768
+ const path33 = sp2.join(directory, item);
19769
+ const fullPath = sp2.resolve(path33);
19770
+ isDirectory = isDirectory != null ? isDirectory : this._watched.has(path33) || this._watched.has(fullPath);
19771
+ if (!this._throttle("remove", path33, 100))
19861
19772
  return;
19862
19773
  if (!isDirectory && this._watched.size === 1) {
19863
19774
  this.add(directory, item, true);
19864
19775
  }
19865
- const wp = this._getWatchedDir(path31);
19776
+ const wp = this._getWatchedDir(path33);
19866
19777
  const nestedDirectoryChildren = wp.getChildren();
19867
- nestedDirectoryChildren.forEach((nested) => this._remove(path31, nested));
19778
+ nestedDirectoryChildren.forEach((nested) => this._remove(path33, nested));
19868
19779
  const parent = this._getWatchedDir(directory);
19869
19780
  const wasTracked = parent.has(item);
19870
19781
  parent.remove(item);
19871
19782
  if (this._symlinkPaths.has(fullPath)) {
19872
19783
  this._symlinkPaths.delete(fullPath);
19873
19784
  }
19874
- let relPath = path31;
19785
+ let relPath = path33;
19875
19786
  if (this.options.cwd)
19876
- relPath = sp2.relative(this.options.cwd, path31);
19787
+ relPath = sp2.relative(this.options.cwd, path33);
19877
19788
  if (this.options.awaitWriteFinish && this._pendingWrites.has(relPath)) {
19878
19789
  const event = this._pendingWrites.get(relPath).cancelWait();
19879
19790
  if (event === EVENTS.ADD)
19880
19791
  return;
19881
19792
  }
19882
- this._watched.delete(path31);
19793
+ this._watched.delete(path33);
19883
19794
  this._watched.delete(fullPath);
19884
19795
  const eventName = isDirectory ? EVENTS.UNLINK_DIR : EVENTS.UNLINK;
19885
- if (wasTracked && !this._isIgnored(path31))
19886
- this._emit(eventName, path31);
19887
- this._closePath(path31);
19796
+ if (wasTracked && !this._isIgnored(path33))
19797
+ this._emit(eventName, path33);
19798
+ this._closePath(path33);
19888
19799
  }
19889
19800
  /**
19890
19801
  * Closes all watchers for a path
19891
19802
  */
19892
- _closePath(path31) {
19893
- this._closeFile(path31);
19894
- const dir = sp2.dirname(path31);
19895
- this._getWatchedDir(dir).remove(sp2.basename(path31));
19803
+ _closePath(path33) {
19804
+ this._closeFile(path33);
19805
+ const dir = sp2.dirname(path33);
19806
+ this._getWatchedDir(dir).remove(sp2.basename(path33));
19896
19807
  }
19897
19808
  /**
19898
19809
  * Closes only file-specific watchers
19899
19810
  */
19900
- _closeFile(path31) {
19901
- const closers = this._closers.get(path31);
19811
+ _closeFile(path33) {
19812
+ const closers = this._closers.get(path33);
19902
19813
  if (!closers)
19903
19814
  return;
19904
19815
  closers.forEach((closer) => closer());
19905
- this._closers.delete(path31);
19816
+ this._closers.delete(path33);
19906
19817
  }
19907
- _addPathCloser(path31, closer) {
19818
+ _addPathCloser(path33, closer) {
19908
19819
  if (!closer)
19909
19820
  return;
19910
- let list = this._closers.get(path31);
19821
+ let list = this._closers.get(path33);
19911
19822
  if (!list) {
19912
19823
  list = [];
19913
- this._closers.set(path31, list);
19824
+ this._closers.set(path33, list);
19914
19825
  }
19915
19826
  list.push(closer);
19916
19827
  }
@@ -19940,12 +19851,291 @@ function watch(paths, options = {}) {
19940
19851
  var chokidar_default = { watch, FSWatcher };
19941
19852
 
19942
19853
  // src/watcher/file-watcher.ts
19854
+ import * as path28 from "path";
19855
+
19856
+ // src/watcher/native-recursive-watcher.ts
19857
+ import { watch as watch2 } from "fs";
19943
19858
  import * as path26 from "path";
19859
+ var NativeRecursiveWatcher = class {
19860
+ constructor(root, onChange, options = {}) {
19861
+ this.root = root;
19862
+ this.onChange = onChange;
19863
+ this.watchFactory = options.watchFactory ?? this.defaultWatchFactory;
19864
+ this.onError = options.onError;
19865
+ }
19866
+ root;
19867
+ onChange;
19868
+ watcher = null;
19869
+ listenerToken = 0;
19870
+ watchFactory;
19871
+ onError;
19872
+ start() {
19873
+ if (this.watcher) return;
19874
+ const token = ++this.listenerToken;
19875
+ const listener = (_eventType, filename) => {
19876
+ if (this.watcher === null || this.listenerToken !== token) return;
19877
+ const absolutePath = this.toAbsolutePath(filename);
19878
+ const nextResult = this.onChange(absolutePath);
19879
+ if (nextResult instanceof Promise) {
19880
+ void nextResult.catch((error) => {
19881
+ console.error("[codebase-index] Error handling native watcher event:", error);
19882
+ });
19883
+ }
19884
+ };
19885
+ const watcher = this.watchFactory(this.root, listener, {
19886
+ persistent: true,
19887
+ recursive: true
19888
+ });
19889
+ watcher.on?.("error", (error) => {
19890
+ if (this.watcher === watcher && this.listenerToken === token) {
19891
+ this.onError?.(error);
19892
+ }
19893
+ });
19894
+ this.watcher = watcher;
19895
+ }
19896
+ async stop() {
19897
+ const watcher = this.watcher;
19898
+ this.watcher = null;
19899
+ this.listenerToken += 1;
19900
+ if (!watcher) return;
19901
+ await watcher.close();
19902
+ }
19903
+ toAbsolutePath(filename) {
19904
+ if (filename == null) return null;
19905
+ const normalizedFilename = typeof filename === "string" ? filename : filename.toString();
19906
+ const absolutePath = path26.resolve(this.root, normalizedFilename);
19907
+ const relativePath = path26.relative(this.root, absolutePath);
19908
+ const outsideRoot = relativePath === ".." || relativePath.startsWith(`..${path26.sep}`) || path26.isAbsolute(relativePath);
19909
+ return outsideRoot ? null : absolutePath;
19910
+ }
19911
+ defaultWatchFactory = (root, listener, options) => watch2(root, options, listener);
19912
+ };
19913
+
19914
+ // src/watcher/snapshot.ts
19915
+ import * as fsPromises4 from "fs/promises";
19916
+ import * as path27 from "path";
19917
+ async function buildFileSnapshotScan(projectRoot, config, configPaths = []) {
19918
+ const normalizedProjectRoot = path27.resolve(projectRoot);
19919
+ const ignoreFilter = createIgnoreFilter(normalizedProjectRoot);
19920
+ const includePatterns = [...config.include, ...config.additionalInclude ?? []];
19921
+ const maxDepth = config.indexing?.maxDepth ?? -1;
19922
+ const snapshot = /* @__PURE__ */ new Map();
19923
+ const unreadablePrefixes = /* @__PURE__ */ new Set();
19924
+ const includeFile = async (filePath) => {
19925
+ const normalizedPath3 = path27.resolve(filePath);
19926
+ if (!shouldIncludeFile(normalizedPath3, normalizedProjectRoot, includePatterns, config.exclude, ignoreFilter)) return;
19927
+ const stat5 = await readStatIfFile(normalizedPath3, unreadablePrefixes);
19928
+ if (stat5) snapshot.set(normalizedPath3, { size: stat5.size, mtimeMs: stat5.mtimeMs });
19929
+ };
19930
+ const walk = async (directoryPath, depth) => {
19931
+ let entries;
19932
+ try {
19933
+ entries = await fsPromises4.readdir(directoryPath, { withFileTypes: true });
19934
+ } catch (error) {
19935
+ if (isMissingFsError(error)) return;
19936
+ if (isPermissionFsError(error)) {
19937
+ unreadablePrefixes.add(path27.resolve(directoryPath));
19938
+ return;
19939
+ }
19940
+ throw error;
19941
+ }
19942
+ for (const entry of entries) {
19943
+ const fullPath = path27.join(directoryPath, entry.name);
19944
+ const relativePath = path27.relative(normalizedProjectRoot, fullPath);
19945
+ if (entry.isDirectory()) {
19946
+ if (hasFilteredPathSegment(relativePath, path27.sep) || isRestrictedDirectory(relativePath, path27.sep)) continue;
19947
+ if (ignoreFilter.ignores(relativePath)) continue;
19948
+ if (maxDepth === -1 || depth < maxDepth) await walk(fullPath, depth + 1);
19949
+ } else if (entry.isFile()) {
19950
+ await includeFile(fullPath);
19951
+ }
19952
+ }
19953
+ };
19954
+ await walk(normalizedProjectRoot, 0);
19955
+ await includeExplicitConfigPaths(snapshot, unreadablePrefixes, configPaths);
19956
+ return { entries: snapshot, unreadablePrefixes };
19957
+ }
19958
+ async function buildFileSnapshotForPathScan(projectRoot, config, configPaths, targetPath) {
19959
+ const normalizedProjectRoot = path27.resolve(projectRoot);
19960
+ const normalizedTargetPath = path27.resolve(targetPath);
19961
+ if (!isWithinPath(normalizedProjectRoot, normalizedTargetPath)) {
19962
+ return { entries: /* @__PURE__ */ new Map(), unreadablePrefixes: /* @__PURE__ */ new Set() };
19963
+ }
19964
+ const ignoreFilter = createIgnoreFilter(normalizedProjectRoot);
19965
+ const includePatterns = [...config.include, ...config.additionalInclude ?? []];
19966
+ const maxDepth = config.indexing?.maxDepth ?? -1;
19967
+ const explicitConfigPaths = new Set(configPaths.map((configPath) => path27.resolve(configPath)));
19968
+ const snapshot = /* @__PURE__ */ new Map();
19969
+ const unreadablePrefixes = /* @__PURE__ */ new Set();
19970
+ const includeFile = async (filePath) => {
19971
+ const normalizedPath3 = path27.resolve(filePath);
19972
+ if (!explicitConfigPaths.has(normalizedPath3) && !shouldIncludeFile(
19973
+ normalizedPath3,
19974
+ normalizedProjectRoot,
19975
+ includePatterns,
19976
+ config.exclude,
19977
+ ignoreFilter
19978
+ )) return;
19979
+ const stat5 = await readStatIfFile(normalizedPath3, unreadablePrefixes);
19980
+ if (stat5) snapshot.set(normalizedPath3, { size: stat5.size, mtimeMs: stat5.mtimeMs });
19981
+ };
19982
+ const walk = async (directoryPath, depth) => {
19983
+ let entries;
19984
+ try {
19985
+ entries = await fsPromises4.readdir(directoryPath, { withFileTypes: true });
19986
+ } catch (error) {
19987
+ if (isMissingFsError(error)) return;
19988
+ if (isPermissionFsError(error)) {
19989
+ unreadablePrefixes.add(path27.resolve(directoryPath));
19990
+ return;
19991
+ }
19992
+ throw error;
19993
+ }
19994
+ for (const entry of entries) {
19995
+ const fullPath = path27.join(directoryPath, entry.name);
19996
+ const relativePath = path27.relative(normalizedProjectRoot, fullPath);
19997
+ if (entry.isDirectory()) {
19998
+ if (hasFilteredPathSegment(relativePath, path27.sep) || isRestrictedDirectory(relativePath, path27.sep)) continue;
19999
+ if (ignoreFilter.ignores(relativePath)) continue;
20000
+ if (maxDepth === -1 || depth < maxDepth) await walk(fullPath, depth + 1);
20001
+ } else if (entry.isFile()) {
20002
+ await includeFile(fullPath);
20003
+ }
20004
+ }
20005
+ };
20006
+ const targetStat = await readStatIfFile(normalizedTargetPath, unreadablePrefixes);
20007
+ if (targetStat) await includeFile(normalizedTargetPath);
20008
+ else await walk(normalizedTargetPath, 0);
20009
+ await includeExplicitConfigPathsInPath(snapshot, unreadablePrefixes, configPaths, normalizedTargetPath);
20010
+ return { entries: snapshot, unreadablePrefixes };
20011
+ }
20012
+ function completeFileSnapshot(previous, scan) {
20013
+ const completed = new Map(scan.entries);
20014
+ for (const unreadablePrefix of scan.unreadablePrefixes) {
20015
+ for (const [entryPath, entry] of previous) {
20016
+ if (isWithinPath(unreadablePrefix, entryPath) && !completed.has(entryPath)) completed.set(entryPath, entry);
20017
+ }
20018
+ }
20019
+ return completed;
20020
+ }
20021
+ async function includeExplicitConfigPaths(snapshot, unreadablePrefixes, configPaths) {
20022
+ for (const configPath of [...new Set(configPaths.map((value) => path27.resolve(value)))]) {
20023
+ if (snapshot.has(configPath)) continue;
20024
+ const stat5 = await readStatIfFile(configPath, unreadablePrefixes);
20025
+ if (stat5) snapshot.set(configPath, { size: stat5.size, mtimeMs: stat5.mtimeMs });
20026
+ }
20027
+ }
20028
+ async function includeExplicitConfigPathsInPath(snapshot, unreadablePrefixes, configPaths, targetPath) {
20029
+ await includeExplicitConfigPaths(
20030
+ snapshot,
20031
+ unreadablePrefixes,
20032
+ configPaths.filter((configPath) => isWithinPath(targetPath, path27.resolve(configPath)))
20033
+ );
20034
+ }
20035
+ function isWithinPath(parentPath, childPath) {
20036
+ const relativePath = path27.relative(parentPath, childPath);
20037
+ return relativePath === "" || !relativePath.startsWith(`..${path27.sep}`) && relativePath !== ".." && !path27.isAbsolute(relativePath);
20038
+ }
20039
+ async function readStatIfFile(filePath, unreadablePrefixes) {
20040
+ try {
20041
+ const stat5 = await fsPromises4.stat(filePath);
20042
+ return stat5.isFile() ? stat5 : null;
20043
+ } catch (error) {
20044
+ if (isMissingFsError(error)) return null;
20045
+ if (isPermissionFsError(error)) {
20046
+ unreadablePrefixes.add(path27.resolve(filePath));
20047
+ return null;
20048
+ }
20049
+ throw error;
20050
+ }
20051
+ }
20052
+ function isMissingFsError(error) {
20053
+ return error instanceof Error && ["ENOENT", "ENOTDIR"].includes(error.code ?? "");
20054
+ }
20055
+ function isPermissionFsError(error) {
20056
+ return error instanceof Error && ["EACCES", "EPERM"].includes(error.code ?? "");
20057
+ }
20058
+ var diffTypeOrder = { add: 0, change: 1, unlink: 2 };
20059
+ function diffFileSnapshots(previous, current, forcedChanges = /* @__PURE__ */ new Set()) {
20060
+ const changes = [];
20061
+ for (const [filePath, previousEntry] of previous) {
20062
+ const currentEntry = current.get(filePath);
20063
+ if (!currentEntry) changes.push({ type: "unlink", path: filePath });
20064
+ else if (forcedChanges.has(filePath) || currentEntry.size !== previousEntry.size || currentEntry.mtimeMs !== previousEntry.mtimeMs) {
20065
+ changes.push({ type: "change", path: filePath });
20066
+ }
20067
+ }
20068
+ for (const [filePath] of current) {
20069
+ if (!previous.has(filePath)) changes.push({ type: "add", path: filePath });
20070
+ }
20071
+ return changes.sort((left, right) => left.path.localeCompare(right.path) || diffTypeOrder[left.type] - diffTypeOrder[right.type]);
20072
+ }
20073
+
20074
+ // src/watcher/snapshot-reconciler.ts
20075
+ var FileSnapshotReconciler = class {
20076
+ constructor(projectRoot, config, configPaths) {
20077
+ this.projectRoot = projectRoot;
20078
+ this.config = config;
20079
+ this.configPaths = configPaths;
20080
+ }
20081
+ projectRoot;
20082
+ config;
20083
+ configPaths;
20084
+ snapshot = null;
20085
+ reconciliationTail = Promise.resolve();
20086
+ async initialize() {
20087
+ this.snapshot = (await buildFileSnapshotScan(this.projectRoot, this.config, this.configPaths)).entries;
20088
+ }
20089
+ async reconcile(invalidations = []) {
20090
+ if (this.snapshot === null) {
20091
+ throw new Error("FileSnapshotReconciler is not initialized. Call initialize() before reconcile().");
20092
+ }
20093
+ const reconciliation = this.reconciliationTail.then(async () => {
20094
+ const previousSnapshot = this.snapshot;
20095
+ if (previousSnapshot === null) {
20096
+ throw new Error("FileSnapshotReconciler is not initialized. Call initialize() before reconcile().");
20097
+ }
20098
+ const normalizedInvalidations = invalidations.map((invalidation) => typeof invalidation === "string" || invalidation === null ? { path: invalidation, forceChange: false } : { path: invalidation.path, forceChange: invalidation.forceChange === true });
20099
+ const scopedPaths = normalizedInvalidations.map((invalidation) => invalidation.path).filter((filePath) => filePath !== null);
20100
+ const scan = scopedPaths.length === 0 || scopedPaths.length !== normalizedInvalidations.length ? await buildFileSnapshotScan(this.projectRoot, this.config, this.configPaths) : await this.reconcilePaths(previousSnapshot, scopedPaths);
20101
+ const nextSnapshot = completeFileSnapshot(previousSnapshot, scan);
20102
+ const forcedChanges = new Set(normalizedInvalidations.filter((invalidation) => invalidation.path !== null && invalidation.forceChange).map((invalidation) => invalidation.path));
20103
+ const changes = diffFileSnapshots(previousSnapshot, nextSnapshot, forcedChanges);
20104
+ this.snapshot = nextSnapshot;
20105
+ return changes;
20106
+ });
20107
+ this.reconciliationTail = reconciliation.then(() => void 0, () => void 0);
20108
+ return reconciliation;
20109
+ }
20110
+ async reconcilePaths(previousSnapshot, invalidatedPaths) {
20111
+ const scopes = this.getScopes(invalidatedPaths);
20112
+ const entries = new Map(previousSnapshot);
20113
+ const unreadablePrefixes = /* @__PURE__ */ new Set();
20114
+ for (const scope of scopes) {
20115
+ for (const previousPath of entries.keys()) {
20116
+ if (isWithinPath(scope, previousPath)) entries.delete(previousPath);
20117
+ }
20118
+ const scopedScan = await buildFileSnapshotForPathScan(this.projectRoot, this.config, this.configPaths, scope);
20119
+ for (const [filePath, entry] of scopedScan.entries) entries.set(filePath, entry);
20120
+ for (const unreadablePrefix of scopedScan.unreadablePrefixes) unreadablePrefixes.add(unreadablePrefix);
20121
+ }
20122
+ return { entries, unreadablePrefixes };
20123
+ }
20124
+ getScopes(invalidatedPaths) {
20125
+ const uniquePaths = [...new Set(invalidatedPaths)].sort((left, right) => left.length - right.length);
20126
+ return uniquePaths.filter((candidate, index) => !uniquePaths.slice(0, index).some(
20127
+ (ancestor) => isWithinPath(ancestor, candidate)
20128
+ ));
20129
+ }
20130
+ };
20131
+
20132
+ // src/watcher/file-watcher.ts
19944
20133
  var FileWatcher = class {
19945
20134
  watcher = null;
19946
20135
  projectRoot;
19947
20136
  config;
19948
20137
  configPath;
20138
+ backend;
19949
20139
  projectConfigPaths;
19950
20140
  pendingChanges = /* @__PURE__ */ new Map();
19951
20141
  debounceTimer = null;
@@ -19955,44 +20145,74 @@ var FileWatcher = class {
19955
20145
  resolveReady = null;
19956
20146
  pollingFallbackAttempted = false;
19957
20147
  pendingClose = null;
20148
+ startupReadySignals = 1;
20149
+ nativeWatcher = null;
20150
+ nativeReconciler = null;
20151
+ nativeSetupGeneration = 0;
20152
+ nativeStarting = false;
20153
+ nativeInitializing = false;
20154
+ nativeReconcileTimer = null;
20155
+ nativeInvalidatedPaths = /* @__PURE__ */ new Map();
20156
+ configPathStates = /* @__PURE__ */ new Map();
19958
20157
  constructor(projectRoot, config, host, options = {}) {
19959
20158
  this.projectRoot = projectRoot;
19960
20159
  this.config = config;
20160
+ this.backend = options.backend ?? "auto";
19961
20161
  this.configPath = options.configPath;
19962
20162
  this.projectConfigPaths = options.configPath ? [options.configPath] : getProjectConfigCandidatePaths(projectRoot, host);
19963
20163
  }
19964
20164
  start(handler) {
19965
- if (this.watcher) {
20165
+ if (this.watcher || this.nativeWatcher || this.nativeStarting) {
19966
20166
  return;
19967
20167
  }
19968
20168
  this.onChanges = handler;
19969
20169
  this.pollingFallbackAttempted = false;
19970
20170
  this.resetReady();
20171
+ if (this.shouldUseNativeWatcher()) {
20172
+ if (this.hasExternalConfigWatchTarget()) {
20173
+ this.setStartupReadySignals(2);
20174
+ this.startExternalConfigWatcher();
20175
+ }
20176
+ this.nativeStarting = true;
20177
+ void this.createNativeWatcher();
20178
+ return;
20179
+ }
19971
20180
  this.createWatcher();
19972
20181
  }
19973
20182
  resetReady() {
19974
- this.readyPromise = new Promise((resolve18) => {
19975
- this.resolveReady = resolve18;
20183
+ this.readyPromise = new Promise((resolve20) => {
20184
+ this.resolveReady = resolve20;
19976
20185
  });
20186
+ this.startupReadySignals = 1;
19977
20187
  }
19978
- createWatcher(usePolling = false) {
19979
- const ignoreFilter = createIgnoreFilter(this.projectRoot);
19980
- let watchTargets = this.projectRoot;
19981
- if (this.configPath) {
19982
- watchTargets = [this.projectRoot, this.configPath];
19983
- } else {
19984
- const externalConfigTargets = this.projectConfigPaths.filter((projectConfigPath) => {
19985
- const relativeConfigPath = path26.relative(this.projectRoot, projectConfigPath);
19986
- return this.isOutsideProjectPath(relativeConfigPath);
19987
- }).map((projectConfigPath) => existsSync15(projectConfigPath) ? projectConfigPath : this.getNearestExistingDirectory(path26.dirname(projectConfigPath)));
19988
- const uniqueExternalConfigTargets = [...new Set(externalConfigTargets)];
19989
- if (uniqueExternalConfigTargets.length > 0) {
19990
- watchTargets = [this.projectRoot, ...uniqueExternalConfigTargets];
19991
- }
20188
+ setStartupReadySignals(expectedSignals) {
20189
+ if (!this.readyPromise) {
20190
+ return;
20191
+ }
20192
+ this.startupReadySignals = Math.max(0, expectedSignals);
20193
+ }
20194
+ reportStartupReadySignal() {
20195
+ if (!this.readyPromise || !this.resolveReady) {
20196
+ return;
20197
+ }
20198
+ if (this.startupReadySignals <= 0) {
20199
+ return;
19992
20200
  }
20201
+ this.startupReadySignals -= 1;
20202
+ if (this.startupReadySignals !== 0) {
20203
+ return;
20204
+ }
20205
+ this.resolveReady();
20206
+ this.resolveReady = null;
20207
+ }
20208
+ createWatcher(watchTargets, usePolling = false, reportsStartupReady = true) {
20209
+ let reportedStartupReady = false;
20210
+ this.configPathStates = this.getConfigPathStates();
20211
+ const ignoreFilter = createIgnoreFilter(this.projectRoot);
20212
+ const resolvedWatchTargets = watchTargets ?? this.getFullChokidarWatchTargets();
19993
20213
  const watcherOptions = {
19994
20214
  ignored: (filePath) => {
19995
- const relativePath = path26.relative(this.projectRoot, filePath);
20215
+ const relativePath = path28.relative(this.projectRoot, filePath);
19996
20216
  if (!relativePath) return false;
19997
20217
  if (this.isProjectConfigPathOrAncestor(relativePath)) {
19998
20218
  return false;
@@ -20000,10 +20220,10 @@ var FileWatcher = class {
20000
20220
  if (this.isOutsideProjectPath(relativePath)) {
20001
20221
  return true;
20002
20222
  }
20003
- if (hasFilteredPathSegment(relativePath, path26.sep)) {
20223
+ if (hasFilteredPathSegment(relativePath, path28.sep)) {
20004
20224
  return true;
20005
20225
  }
20006
- if (isRestrictedDirectory(relativePath, path26.sep)) {
20226
+ if (isRestrictedDirectory(relativePath, path28.sep)) {
20007
20227
  return true;
20008
20228
  }
20009
20229
  if (ignoreFilter.ignores(relativePath)) {
@@ -20036,10 +20256,13 @@ var FileWatcher = class {
20036
20256
  watcher = new FSWatcher(watcherOptions);
20037
20257
  }
20038
20258
  this.watcher = watcher;
20039
- watcher.once("ready", () => {
20259
+ watcher.on("ready", () => {
20040
20260
  if (this.watcher !== watcher) return;
20041
- this.resolveReady?.();
20042
- this.resolveReady = null;
20261
+ this.reconcileConfigPathStates();
20262
+ if (reportsStartupReady) {
20263
+ this.reportStartupReadySignal();
20264
+ reportedStartupReady = true;
20265
+ }
20043
20266
  });
20044
20267
  watcher.on("error", (error) => {
20045
20268
  const err = error instanceof Error ? error : null;
@@ -20053,10 +20276,13 @@ var FileWatcher = class {
20053
20276
  console.error("[codebase-index] Failed to close exhausted file watcher:", closeError);
20054
20277
  });
20055
20278
  if (this.onChanges) {
20279
+ const replacementReportsStartupReady = reportsStartupReady || reportedStartupReady;
20056
20280
  if (!this.resolveReady) {
20057
20281
  this.resetReady();
20282
+ } else if (reportedStartupReady) {
20283
+ this.startupReadySignals += 1;
20058
20284
  }
20059
- this.createWatcher(true);
20285
+ this.createWatcher(resolvedWatchTargets, true, replacementReportsStartupReady);
20060
20286
  } else {
20061
20287
  this.watcher = null;
20062
20288
  }
@@ -20067,13 +20293,166 @@ var FileWatcher = class {
20067
20293
  watcher.on("add", (filePath) => this.handleChange(watcher, "add", filePath));
20068
20294
  watcher.on("change", (filePath) => this.handleChange(watcher, "change", filePath));
20069
20295
  watcher.on("unlink", (filePath) => this.handleChange(watcher, "unlink", filePath));
20070
- watcher.add(watchTargets);
20296
+ watcher.add(resolvedWatchTargets);
20297
+ }
20298
+ shouldUseNativeWatcher() {
20299
+ if (this.backend === "chokidar") {
20300
+ return false;
20301
+ }
20302
+ return true;
20303
+ }
20304
+ getFullChokidarWatchTargets() {
20305
+ if (this.configPath) {
20306
+ return [this.projectRoot, this.configPath];
20307
+ }
20308
+ const externalConfigTargets = this.getExternalConfigWatchTargets();
20309
+ if (externalConfigTargets.length === 0) {
20310
+ return this.projectRoot;
20311
+ }
20312
+ return [this.projectRoot, ...externalConfigTargets];
20313
+ }
20314
+ getExternalConfigWatchTargets() {
20315
+ return [...new Set(
20316
+ this.projectConfigPaths.filter((projectConfigPath) => {
20317
+ const relativeConfigPath = path28.relative(this.projectRoot, projectConfigPath);
20318
+ return this.isOutsideProjectPath(relativeConfigPath);
20319
+ }).map((projectConfigPath) => {
20320
+ if (existsSync15(projectConfigPath)) {
20321
+ return projectConfigPath;
20322
+ }
20323
+ return this.getNearestExistingDirectory(path28.dirname(projectConfigPath));
20324
+ })
20325
+ )];
20326
+ }
20327
+ hasExternalConfigWatchTarget() {
20328
+ return this.getExternalConfigWatchTargets().length > 0;
20329
+ }
20330
+ startExternalConfigWatcher(usePolling = false) {
20331
+ const externalTargets = this.getExternalConfigWatchTargets();
20332
+ if (externalTargets.length === 0) {
20333
+ return;
20334
+ }
20335
+ this.createWatcher(externalTargets, usePolling);
20336
+ }
20337
+ async createNativeWatcher() {
20338
+ const generation = ++this.nativeSetupGeneration;
20339
+ const reconciler = new FileSnapshotReconciler(this.projectRoot, this.config, this.projectConfigPaths);
20340
+ const watcher = new NativeRecursiveWatcher(
20341
+ this.projectRoot,
20342
+ (filePath) => this.scheduleNativeReconciliation(generation, filePath),
20343
+ { onError: (error) => void this.fallbackFromNativeWatcher(generation, error) }
20344
+ );
20345
+ this.nativeReconciler = reconciler;
20346
+ this.nativeWatcher = watcher;
20347
+ this.nativeInitializing = true;
20348
+ try {
20349
+ watcher.start();
20350
+ if (!this.isCurrentNativeSetup(generation)) {
20351
+ await watcher.stop();
20352
+ return;
20353
+ }
20354
+ await reconciler.initialize();
20355
+ if (!this.isCurrentNativeSetup(generation) || this.nativeWatcher !== watcher) {
20356
+ await watcher.stop();
20357
+ return;
20358
+ }
20359
+ this.nativeStarting = false;
20360
+ this.nativeInitializing = false;
20361
+ await this.reconcileNativeWatcherWithPendingInvalidations(generation);
20362
+ this.reportStartupReadySignal();
20363
+ } catch (error) {
20364
+ if (!this.isCurrentNativeSetup(generation)) return;
20365
+ this.nativeInitializing = false;
20366
+ if (this.nativeWatcher) {
20367
+ await this.fallbackFromNativeWatcher(generation, error);
20368
+ return;
20369
+ }
20370
+ this.nativeStarting = false;
20371
+ const externalWatcher = this.watcher;
20372
+ this.watcher = null;
20373
+ this.nativeReconciler = null;
20374
+ await externalWatcher?.close();
20375
+ console.warn("[codebase-index] Native recursive watcher unavailable; using Chokidar fallback.", error);
20376
+ this.setStartupReadySignals(1);
20377
+ this.createWatcher();
20378
+ }
20379
+ }
20380
+ isCurrentNativeSetup(generation) {
20381
+ return this.nativeSetupGeneration === generation && this.onChanges !== null;
20382
+ }
20383
+ scheduleNativeReconciliation(generation, filePath) {
20384
+ if (!this.isCurrentNativeSetup(generation)) return;
20385
+ const requiresFullReconciliation = filePath === path28.join(this.projectRoot, ".gitignore");
20386
+ const invalidatedPath = requiresFullReconciliation ? null : filePath;
20387
+ this.nativeInvalidatedPaths.set(invalidatedPath, invalidatedPath !== null);
20388
+ if (this.nativeReconcileTimer) {
20389
+ clearTimeout(this.nativeReconcileTimer);
20390
+ }
20391
+ this.nativeReconcileTimer = setTimeout(() => {
20392
+ this.nativeReconcileTimer = null;
20393
+ void this.reconcileNativeWatcherFromQueue(generation);
20394
+ }, 100);
20395
+ }
20396
+ reconcileNativeWatcherFromQueue(generation) {
20397
+ if (!this.isCurrentNativeSetup(generation) || this.nativeInitializing) return;
20398
+ const invalidatedPaths = this.popNativeInvalidations();
20399
+ if (invalidatedPaths.length === 0) return;
20400
+ void this.reconcileNativeWatcher(generation, invalidatedPaths);
20401
+ }
20402
+ async reconcileNativeWatcher(generation, invalidatedPaths) {
20403
+ if (!this.isCurrentNativeSetup(generation) || !this.nativeReconciler) return;
20404
+ try {
20405
+ const reconciler = this.nativeReconciler;
20406
+ const changes = await reconciler.reconcile(invalidatedPaths);
20407
+ if (!this.isCurrentNativeSetup(generation) || this.nativeReconciler !== reconciler) return;
20408
+ this.recordChanges(changes);
20409
+ } catch (error) {
20410
+ await this.fallbackFromNativeWatcher(generation, error);
20411
+ }
20412
+ }
20413
+ async reconcileNativeWatcherWithPendingInvalidations(generation) {
20414
+ const invalidatedPaths = this.popNativeInvalidations();
20415
+ if (invalidatedPaths.length === 0) return;
20416
+ await this.reconcileNativeWatcher(generation, invalidatedPaths);
20417
+ }
20418
+ popNativeInvalidations() {
20419
+ if (this.nativeInvalidatedPaths.size === 0) return [];
20420
+ const invalidations = [...this.nativeInvalidatedPaths].map(([invalidatedPath, forceChange]) => ({
20421
+ path: invalidatedPath,
20422
+ forceChange
20423
+ }));
20424
+ this.nativeInvalidatedPaths.clear();
20425
+ return invalidations;
20426
+ }
20427
+ async fallbackFromNativeWatcher(generation, error) {
20428
+ if (!this.isCurrentNativeSetup(generation)) return;
20429
+ const watcher = this.nativeWatcher;
20430
+ const externalWatcher = this.watcher;
20431
+ this.nativeWatcher = null;
20432
+ this.watcher = null;
20433
+ this.nativeReconciler = null;
20434
+ this.nativeStarting = false;
20435
+ this.nativeInitializing = false;
20436
+ this.nativeSetupGeneration += 1;
20437
+ if (this.nativeReconcileTimer) {
20438
+ clearTimeout(this.nativeReconcileTimer);
20439
+ this.nativeReconcileTimer = null;
20440
+ }
20441
+ this.nativeInvalidatedPaths.clear();
20442
+ this.setStartupReadySignals(1);
20443
+ console.warn("[codebase-index] Native recursive watcher failed; using Chokidar fallback.", error);
20444
+ await watcher?.stop();
20445
+ await externalWatcher?.close();
20446
+ if (this.onChanges) {
20447
+ this.createWatcher();
20448
+ }
20071
20449
  }
20072
20450
  handleChange(watcher, type, filePath) {
20073
20451
  if (this.watcher !== watcher) {
20074
20452
  return;
20075
20453
  }
20076
20454
  if (this.isProjectConfigPath(filePath)) {
20455
+ this.updateConfigPathState(filePath);
20077
20456
  this.pendingChanges.set(filePath, type);
20078
20457
  this.scheduleFlush();
20079
20458
  return;
@@ -20088,27 +20467,33 @@ var FileWatcher = class {
20088
20467
  )) {
20089
20468
  return;
20090
20469
  }
20091
- this.pendingChanges.set(filePath, type);
20470
+ this.recordChanges([{ path: filePath, type }]);
20471
+ }
20472
+ recordChanges(changes) {
20473
+ if (changes.length === 0) return;
20474
+ for (const change of changes) {
20475
+ this.pendingChanges.set(change.path, change.type);
20476
+ }
20092
20477
  this.scheduleFlush();
20093
20478
  }
20094
20479
  isProjectConfigPath(filePath) {
20095
- const relativePath = path26.relative(this.projectRoot, filePath);
20096
- const normalizedRelativePath = path26.normalize(relativePath);
20480
+ const relativePath = path28.relative(this.projectRoot, filePath);
20481
+ const normalizedRelativePath = path28.normalize(relativePath);
20097
20482
  return this.getProjectConfigRelativePaths().some((configPath) => configPath === normalizedRelativePath);
20098
20483
  }
20099
20484
  isProjectConfigPathOrAncestor(relativePath) {
20100
- const normalizedRelativePath = path26.normalize(relativePath);
20485
+ const normalizedRelativePath = path28.normalize(relativePath);
20101
20486
  return this.getProjectConfigRelativePaths().some(
20102
- (configPath) => configPath === normalizedRelativePath || configPath.startsWith(`${normalizedRelativePath}${path26.sep}`)
20487
+ (configPath) => configPath === normalizedRelativePath || configPath.startsWith(`${normalizedRelativePath}${path28.sep}`)
20103
20488
  );
20104
20489
  }
20105
20490
  isOutsideProjectPath(relativePath) {
20106
- return relativePath === ".." || relativePath.startsWith(`..${path26.sep}`) || path26.isAbsolute(relativePath);
20491
+ return relativePath === ".." || relativePath.startsWith(`..${path28.sep}`) || path28.isAbsolute(relativePath);
20107
20492
  }
20108
20493
  getNearestExistingDirectory(directoryPath) {
20109
20494
  let candidate = directoryPath;
20110
20495
  while (!existsSync15(candidate)) {
20111
- const parent = path26.dirname(candidate);
20496
+ const parent = path28.dirname(candidate);
20112
20497
  if (parent === candidate) break;
20113
20498
  candidate = parent;
20114
20499
  }
@@ -20116,9 +20501,51 @@ var FileWatcher = class {
20116
20501
  }
20117
20502
  getProjectConfigRelativePaths() {
20118
20503
  return this.projectConfigPaths.map(
20119
- (configPath) => path26.normalize(path26.relative(this.projectRoot, configPath))
20504
+ (configPath) => path28.normalize(path28.relative(this.projectRoot, configPath))
20120
20505
  );
20121
20506
  }
20507
+ getConfigPathStates() {
20508
+ const states = /* @__PURE__ */ new Map();
20509
+ for (const configPath of this.projectConfigPaths) {
20510
+ const state = this.getConfigPathState(configPath);
20511
+ if (state) states.set(configPath, state);
20512
+ }
20513
+ return states;
20514
+ }
20515
+ getConfigPathState(configPath) {
20516
+ try {
20517
+ const stats = statSync6(configPath);
20518
+ return stats.isFile() ? { mtimeMs: stats.mtimeMs, size: stats.size } : void 0;
20519
+ } catch (error) {
20520
+ void error;
20521
+ return void 0;
20522
+ }
20523
+ }
20524
+ updateConfigPathState(configPath) {
20525
+ const state = this.getConfigPathState(configPath);
20526
+ if (state) {
20527
+ this.configPathStates.set(configPath, state);
20528
+ } else {
20529
+ this.configPathStates.delete(configPath);
20530
+ }
20531
+ }
20532
+ reconcileConfigPathStates() {
20533
+ const nextStates = this.getConfigPathStates();
20534
+ const changes = [];
20535
+ for (const configPath of this.projectConfigPaths) {
20536
+ const previous = this.configPathStates.get(configPath);
20537
+ const next = nextStates.get(configPath);
20538
+ if (!previous && next) {
20539
+ changes.push({ path: configPath, type: "add" });
20540
+ } else if (previous && !next) {
20541
+ changes.push({ path: configPath, type: "unlink" });
20542
+ } else if (previous && next && (previous.size !== next.size || previous.mtimeMs !== next.mtimeMs)) {
20543
+ changes.push({ path: configPath, type: "change" });
20544
+ }
20545
+ }
20546
+ this.configPathStates = nextStates;
20547
+ this.recordChanges(changes);
20548
+ }
20122
20549
  scheduleFlush() {
20123
20550
  if (this.debounceTimer) {
20124
20551
  clearTimeout(this.debounceTimer);
@@ -20132,7 +20559,7 @@ var FileWatcher = class {
20132
20559
  return;
20133
20560
  }
20134
20561
  const changes = Array.from(this.pendingChanges.entries()).map(
20135
- ([path31, type]) => ({ path: path31, type })
20562
+ ([path33, type]) => ({ path: path33, type })
20136
20563
  );
20137
20564
  this.pendingChanges.clear();
20138
20565
  try {
@@ -20146,20 +20573,31 @@ var FileWatcher = class {
20146
20573
  clearTimeout(this.debounceTimer);
20147
20574
  this.debounceTimer = null;
20148
20575
  }
20576
+ if (this.nativeReconcileTimer) {
20577
+ clearTimeout(this.nativeReconcileTimer);
20578
+ this.nativeReconcileTimer = null;
20579
+ }
20580
+ this.nativeInvalidatedPaths.clear();
20149
20581
  const watcher = this.watcher;
20582
+ const nativeWatcher = this.nativeWatcher;
20150
20583
  const pendingClose = this.pendingClose;
20151
20584
  const resolveReady = this.resolveReady;
20152
20585
  this.watcher = null;
20586
+ this.nativeWatcher = null;
20587
+ this.nativeReconciler = null;
20588
+ this.nativeStarting = false;
20589
+ this.nativeInitializing = false;
20590
+ this.nativeSetupGeneration += 1;
20153
20591
  this.pendingClose = null;
20154
20592
  this.resolveReady = null;
20155
20593
  this.readyPromise = null;
20156
20594
  this.pendingChanges.clear();
20157
20595
  this.onChanges = null;
20158
- await Promise.all([watcher?.close(), pendingClose]);
20596
+ await Promise.all([watcher?.close(), nativeWatcher?.stop(), pendingClose]);
20159
20597
  resolveReady?.();
20160
20598
  }
20161
20599
  isRunning() {
20162
- return this.watcher !== null;
20600
+ return this.watcher !== null || this.nativeWatcher !== null || this.nativeStarting;
20163
20601
  }
20164
20602
  async waitUntilReady() {
20165
20603
  await (this.readyPromise ?? Promise.resolve());
@@ -20167,7 +20605,7 @@ var FileWatcher = class {
20167
20605
  };
20168
20606
 
20169
20607
  // src/watcher/git-head-watcher.ts
20170
- import * as path27 from "path";
20608
+ import * as path29 from "path";
20171
20609
  var GitHeadWatcher = class {
20172
20610
  watcher = null;
20173
20611
  projectRoot;
@@ -20189,13 +20627,13 @@ var GitHeadWatcher = class {
20189
20627
  this.readyPromise = Promise.resolve();
20190
20628
  return;
20191
20629
  }
20192
- this.readyPromise = new Promise((resolve18) => {
20193
- this.resolveReady = resolve18;
20630
+ this.readyPromise = new Promise((resolve20) => {
20631
+ this.resolveReady = resolve20;
20194
20632
  });
20195
20633
  this.onBranchChange = handler;
20196
20634
  this.currentBranch = getCurrentBranch(this.projectRoot);
20197
20635
  const headPath = getHeadPath(this.projectRoot);
20198
- const refsPath = path27.join(this.projectRoot, ".git", "refs", "heads");
20636
+ const refsPath = path29.join(this.projectRoot, ".git", "refs", "heads");
20199
20637
  this.watcher = chokidar_default.watch([headPath, refsPath], {
20200
20638
  persistent: true,
20201
20639
  ignoreInitial: true,
@@ -20331,7 +20769,7 @@ function getConfigPaths(projectRoot, host, options) {
20331
20769
 
20332
20770
  // src/tools/visualize/activity.ts
20333
20771
  import { execFileSync } from "child_process";
20334
- import * as path28 from "path";
20772
+ import * as path30 from "path";
20335
20773
  function attachRecentActivity(data, projectRoot) {
20336
20774
  const activity = readGitActivity(projectRoot);
20337
20775
  const changes = activity.size > 0 ? buildGitChanges(data, activity, projectRoot) : buildGraphChanges(data);
@@ -20493,7 +20931,7 @@ function normalizePath4(filePath) {
20493
20931
  return filePath.replace(/\\/g, "/");
20494
20932
  }
20495
20933
  function toGitRelativePath(projectRoot, filePath) {
20496
- const relativePath = path28.isAbsolute(filePath) ? path28.relative(projectRoot, filePath) : filePath;
20934
+ const relativePath = path30.isAbsolute(filePath) ? path30.relative(projectRoot, filePath) : filePath;
20497
20935
  return normalizePath4(relativePath);
20498
20936
  }
20499
20937
 
@@ -20751,7 +21189,7 @@ render();
20751
21189
  }
20752
21190
 
20753
21191
  // src/tools/visualize/transform.ts
20754
- import * as path29 from "path";
21192
+ import * as path31 from "path";
20755
21193
 
20756
21194
  // src/tools/visualize/modules.ts
20757
21195
  var MAX_MODULES = 18;
@@ -20884,8 +21322,8 @@ function compactModules(prefixToNodes) {
20884
21322
  function deriveModules(nodes) {
20885
21323
  const initial = /* @__PURE__ */ new Map();
20886
21324
  for (const node of nodes) {
20887
- const relative12 = stripToProjectRelative(node.filePath);
20888
- const prefix = modulePrefixFromRelativePath(relative12);
21325
+ const relative14 = stripToProjectRelative(node.filePath);
21326
+ const prefix = modulePrefixFromRelativePath(relative14);
20889
21327
  if (!initial.has(prefix)) initial.set(prefix, []);
20890
21328
  initial.get(prefix)?.push(node);
20891
21329
  }
@@ -21011,7 +21449,7 @@ function transformForVisualization(symbols, edges, options = {}) {
21011
21449
  filePath: s.filePath,
21012
21450
  kind: s.kind,
21013
21451
  line: s.startLine,
21014
- directory: path29.dirname(s.filePath),
21452
+ directory: path31.dirname(s.filePath),
21015
21453
  moduleId: "",
21016
21454
  moduleLabel: ""
21017
21455
  }));
@@ -21039,9 +21477,9 @@ function parseArgs(argv) {
21039
21477
  let host = "opencode";
21040
21478
  for (let i = 2; i < argv.length; i++) {
21041
21479
  if (argv[i] === "--project" && argv[i + 1]) {
21042
- project = path30.resolve(argv[++i]);
21480
+ project = path32.resolve(argv[++i]);
21043
21481
  } else if (argv[i] === "--config" && argv[i + 1]) {
21044
- config = path30.resolve(argv[++i]);
21482
+ config = path32.resolve(argv[++i]);
21045
21483
  } else if (argv[i] === "--host" && argv[i + 1]) {
21046
21484
  host = parseHostMode(argv[++i]);
21047
21485
  } else if (argv[i] === "--host") {
@@ -21068,7 +21506,7 @@ function parseIndexArgs(argv, cwd) {
21068
21506
  if (!arg.startsWith("--project=")) {
21069
21507
  i += 1;
21070
21508
  }
21071
- project = path30.resolve(cwd, value);
21509
+ project = path32.resolve(cwd, value);
21072
21510
  continue;
21073
21511
  }
21074
21512
  if (arg === "--config" || arg.startsWith("--config=")) {
@@ -21079,7 +21517,7 @@ function parseIndexArgs(argv, cwd) {
21079
21517
  if (!arg.startsWith("--config=")) {
21080
21518
  i += 1;
21081
21519
  }
21082
- config = path30.resolve(cwd, value);
21520
+ config = path32.resolve(cwd, value);
21083
21521
  continue;
21084
21522
  }
21085
21523
  if (arg === "--host" || arg.startsWith("--host=")) {
@@ -21145,7 +21583,7 @@ function parseVisualizeArgs(argv, cwd) {
21145
21583
  for (let i = 0; i < argv.length; i++) {
21146
21584
  const arg = argv[i];
21147
21585
  if (arg === "--project" && argv[i + 1]) {
21148
- project = path30.resolve(argv[++i]);
21586
+ project = path32.resolve(argv[++i]);
21149
21587
  } else if (arg === "--max" && argv[i + 1]) {
21150
21588
  maxNodes = Number(argv[++i]);
21151
21589
  } else if (arg.startsWith("--max=") || arg.startsWith("max=")) {
@@ -21182,7 +21620,7 @@ async function handleVisualizeCommand(argv, cwd) {
21182
21620
  console.error("No connected symbols found. Retry with: npm run visualize -- orphans");
21183
21621
  return 1;
21184
21622
  }
21185
- const outputPath = path30.join(os8.tmpdir(), `call-graph-${Date.now()}.html`);
21623
+ const outputPath = path32.join(os8.tmpdir(), `call-graph-${Date.now()}.html`);
21186
21624
  writeFileSync6(outputPath, generateVisualizationHtml(vizData), "utf-8");
21187
21625
  console.log(`Temporal call graph visualization generated: ${outputPath}`);
21188
21626
  console.log(`Nodes: ${vizData.nodes.length} | Edges: ${vizData.edges.length}`);
@@ -21214,8 +21652,60 @@ async function runMcpCli(argv) {
21214
21652
  const config = parseConfig(rawConfig);
21215
21653
  const server = createMcpServer(args.project, config, args.host);
21216
21654
  const transport = new StdioServerTransport();
21217
- await server.connect(transport);
21218
21655
  let watcher = null;
21656
+ let shutdownPromise;
21657
+ const onServerClose = server.server.onclose;
21658
+ const shutdown = () => {
21659
+ if (shutdownPromise) return shutdownPromise;
21660
+ process.stdin.removeListener("end", requestShutdown);
21661
+ process.stdin.removeListener("close", requestShutdown);
21662
+ process.removeListener("SIGHUP", requestShutdown);
21663
+ process.removeListener("SIGINT", requestShutdown);
21664
+ process.removeListener("SIGTERM", requestShutdown);
21665
+ server.server.onclose = onServerClose;
21666
+ shutdownPromise = (async () => {
21667
+ let exitCode = 0;
21668
+ try {
21669
+ await watcher?.stop();
21670
+ } catch (error) {
21671
+ exitCode = 1;
21672
+ console.error("Failed to stop MCP file watcher cleanly:", error);
21673
+ }
21674
+ try {
21675
+ await stopAutoIndex(args.project, args.host);
21676
+ } catch (error) {
21677
+ exitCode = 1;
21678
+ console.error("Failed to stop automatic indexing cleanly:", error);
21679
+ }
21680
+ try {
21681
+ await server.close();
21682
+ } catch (error) {
21683
+ exitCode = 1;
21684
+ console.error("Failed to close MCP server cleanly:", error);
21685
+ }
21686
+ process.exit(exitCode);
21687
+ })();
21688
+ return shutdownPromise;
21689
+ };
21690
+ const requestShutdown = () => {
21691
+ void shutdown();
21692
+ };
21693
+ server.server.onclose = () => {
21694
+ try {
21695
+ onServerClose?.();
21696
+ } finally {
21697
+ requestShutdown();
21698
+ }
21699
+ };
21700
+ process.stdin.once("end", requestShutdown);
21701
+ process.stdin.once("close", requestShutdown);
21702
+ process.once("SIGINT", requestShutdown);
21703
+ if (process.platform !== "win32") {
21704
+ process.once("SIGHUP", requestShutdown);
21705
+ process.once("SIGTERM", requestShutdown);
21706
+ }
21707
+ await server.connect(transport);
21708
+ if (shutdownPromise) return;
21219
21709
  const isHomeDir = isHomeDirectory(args.project);
21220
21710
  const isValidProject = !isHomeDir && (!config.indexing.requireProjectMarker || hasProjectMarker(args.project));
21221
21711
  if (config.indexing.watchFiles && isValidProject) {
@@ -21227,26 +21717,6 @@ async function runMcpCli(argv) {
21227
21717
  args.config ? { configPath: args.config } : {}
21228
21718
  );
21229
21719
  }
21230
- let shuttingDown = false;
21231
- const shutdown = async () => {
21232
- if (shuttingDown) return;
21233
- shuttingDown = true;
21234
- try {
21235
- await watcher?.stop();
21236
- await stopAutoIndex(args.project, args.host);
21237
- await server.close();
21238
- process.exit(0);
21239
- } catch (error) {
21240
- console.error("Failed to stop MCP server cleanly:", error);
21241
- process.exit(1);
21242
- }
21243
- };
21244
- process.on("SIGINT", () => {
21245
- void shutdown();
21246
- });
21247
- process.on("SIGTERM", () => {
21248
- void shutdown();
21249
- });
21250
21720
  }
21251
21721
  function printIndexProgress(onProgress, title, metadata) {
21252
21722
  const details = Object.entries(metadata).filter(([, value]) => value !== void 0 && value !== null).map(([key, value]) => `${key}=${isSensitiveKey(key) ? "[REDACTED]" : String(value)}`).join(" ");