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/index.cjs CHANGED
@@ -333,7 +333,7 @@ var require_ignore = __commonJS({
333
333
  // path matching.
334
334
  // - check `string` either `MODE_IGNORE` or `MODE_CHECK_IGNORE`
335
335
  // @returns {TestResult} true if a file is ignored
336
- test(path28, checkUnignored, mode) {
336
+ test(path30, checkUnignored, mode) {
337
337
  let ignored = false;
338
338
  let unignored = false;
339
339
  let matchedRule;
@@ -342,7 +342,7 @@ var require_ignore = __commonJS({
342
342
  if (unignored === negative && ignored !== unignored || negative && !ignored && !unignored && !checkUnignored) {
343
343
  return;
344
344
  }
345
- const matched = rule[mode].test(path28);
345
+ const matched = rule[mode].test(path30);
346
346
  if (!matched) {
347
347
  return;
348
348
  }
@@ -363,17 +363,17 @@ var require_ignore = __commonJS({
363
363
  var throwError = (message, Ctor) => {
364
364
  throw new Ctor(message);
365
365
  };
366
- var checkPath = (path28, originalPath, doThrow) => {
367
- if (!isString(path28)) {
366
+ var checkPath = (path30, originalPath, doThrow) => {
367
+ if (!isString(path30)) {
368
368
  return doThrow(
369
369
  `path must be a string, but got \`${originalPath}\``,
370
370
  TypeError
371
371
  );
372
372
  }
373
- if (!path28) {
373
+ if (!path30) {
374
374
  return doThrow(`path must not be empty`, TypeError);
375
375
  }
376
- if (checkPath.isNotRelative(path28)) {
376
+ if (checkPath.isNotRelative(path30)) {
377
377
  const r = "`path.relative()`d";
378
378
  return doThrow(
379
379
  `path should be a ${r} string, but got "${originalPath}"`,
@@ -382,7 +382,7 @@ var require_ignore = __commonJS({
382
382
  }
383
383
  return true;
384
384
  };
385
- var isNotRelative = (path28) => REGEX_TEST_INVALID_PATH.test(path28);
385
+ var isNotRelative = (path30) => REGEX_TEST_INVALID_PATH.test(path30);
386
386
  checkPath.isNotRelative = isNotRelative;
387
387
  checkPath.convert = (p) => p;
388
388
  var Ignore2 = class {
@@ -412,19 +412,19 @@ var require_ignore = __commonJS({
412
412
  }
413
413
  // @returns {TestResult}
414
414
  _test(originalPath, cache, checkUnignored, slices) {
415
- const path28 = originalPath && checkPath.convert(originalPath);
415
+ const path30 = originalPath && checkPath.convert(originalPath);
416
416
  checkPath(
417
- path28,
417
+ path30,
418
418
  originalPath,
419
419
  this._strictPathCheck ? throwError : RETURN_FALSE
420
420
  );
421
- return this._t(path28, cache, checkUnignored, slices);
421
+ return this._t(path30, cache, checkUnignored, slices);
422
422
  }
423
- checkIgnore(path28) {
424
- if (!REGEX_TEST_TRAILING_SLASH.test(path28)) {
425
- return this.test(path28);
423
+ checkIgnore(path30) {
424
+ if (!REGEX_TEST_TRAILING_SLASH.test(path30)) {
425
+ return this.test(path30);
426
426
  }
427
- const slices = path28.split(SLASH2).filter(Boolean);
427
+ const slices = path30.split(SLASH2).filter(Boolean);
428
428
  slices.pop();
429
429
  if (slices.length) {
430
430
  const parent = this._t(
@@ -437,18 +437,18 @@ var require_ignore = __commonJS({
437
437
  return parent;
438
438
  }
439
439
  }
440
- return this._rules.test(path28, false, MODE_CHECK_IGNORE);
440
+ return this._rules.test(path30, false, MODE_CHECK_IGNORE);
441
441
  }
442
- _t(path28, cache, checkUnignored, slices) {
443
- if (path28 in cache) {
444
- return cache[path28];
442
+ _t(path30, cache, checkUnignored, slices) {
443
+ if (path30 in cache) {
444
+ return cache[path30];
445
445
  }
446
446
  if (!slices) {
447
- slices = path28.split(SLASH2).filter(Boolean);
447
+ slices = path30.split(SLASH2).filter(Boolean);
448
448
  }
449
449
  slices.pop();
450
450
  if (!slices.length) {
451
- return cache[path28] = this._rules.test(path28, checkUnignored, MODE_IGNORE);
451
+ return cache[path30] = this._rules.test(path30, checkUnignored, MODE_IGNORE);
452
452
  }
453
453
  const parent = this._t(
454
454
  slices.join(SLASH2) + SLASH2,
@@ -456,29 +456,29 @@ var require_ignore = __commonJS({
456
456
  checkUnignored,
457
457
  slices
458
458
  );
459
- return cache[path28] = parent.ignored ? parent : this._rules.test(path28, checkUnignored, MODE_IGNORE);
459
+ return cache[path30] = parent.ignored ? parent : this._rules.test(path30, checkUnignored, MODE_IGNORE);
460
460
  }
461
- ignores(path28) {
462
- return this._test(path28, this._ignoreCache, false).ignored;
461
+ ignores(path30) {
462
+ return this._test(path30, this._ignoreCache, false).ignored;
463
463
  }
464
464
  createFilter() {
465
- return (path28) => !this.ignores(path28);
465
+ return (path30) => !this.ignores(path30);
466
466
  }
467
467
  filter(paths) {
468
468
  return makeArray(paths).filter(this.createFilter());
469
469
  }
470
470
  // @returns {TestResult}
471
- test(path28) {
472
- return this._test(path28, this._testCache, true);
471
+ test(path30) {
472
+ return this._test(path30, this._testCache, true);
473
473
  }
474
474
  };
475
475
  var factory = (options) => new Ignore2(options);
476
- var isPathValid = (path28) => checkPath(path28 && checkPath.convert(path28), path28, RETURN_FALSE);
476
+ var isPathValid = (path30) => checkPath(path30 && checkPath.convert(path30), path30, RETURN_FALSE);
477
477
  var setupWindows = () => {
478
478
  const makePosix = (str) => /^\\\\\?\\/.test(str) || /["<>|\u0000-\u001F]+/u.test(str) ? str : str.replace(/\\/g, "/");
479
479
  checkPath.convert = makePosix;
480
480
  const REGEX_TEST_WINDOWS_PATH_ABSOLUTE = /^[a-z]:\//i;
481
- checkPath.isNotRelative = (path28) => REGEX_TEST_WINDOWS_PATH_ABSOLUTE.test(path28) || isNotRelative(path28);
481
+ checkPath.isNotRelative = (path30) => REGEX_TEST_WINDOWS_PATH_ABSOLUTE.test(path30) || isNotRelative(path30);
482
482
  };
483
483
  if (
484
484
  // Detect `process` so that it can run in browsers.
@@ -663,7 +663,7 @@ __export(index_exports, {
663
663
  module.exports = __toCommonJS(index_exports);
664
664
 
665
665
  // src/adapters/opencode.ts
666
- var path27 = __toESM(require("path"), 1);
666
+ var path29 = __toESM(require("path"), 1);
667
667
  var import_url = require("url");
668
668
 
669
669
  // src/config/constants.ts
@@ -1141,11 +1141,11 @@ function resolveGitDir(repoRoot) {
1141
1141
  return null;
1142
1142
  }
1143
1143
  try {
1144
- const stat4 = (0, import_fs2.statSync)(gitPath);
1145
- if (stat4.isDirectory()) {
1144
+ const stat5 = (0, import_fs2.statSync)(gitPath);
1145
+ if (stat5.isDirectory()) {
1146
1146
  return gitPath;
1147
1147
  }
1148
- if (stat4.isFile()) {
1148
+ if (stat5.isFile()) {
1149
1149
  const content = (0, import_fs2.readFileSync)(gitPath, "utf-8").trim();
1150
1150
  const match = content.match(/^gitdir:\s*(.+)$/);
1151
1151
  if (match) {
@@ -2214,7 +2214,7 @@ function analyzeQueryIntent(query) {
2214
2214
  }
2215
2215
  function isTestPath(filePath) {
2216
2216
  const normalized = normalizePath(filePath);
2217
- return /(?:^|\/)(?:test|tests|__tests__|spec|specs)(?:\/|$)/u.test(normalized) || /\.(?:test|spec)\.[^/]+$/u.test(normalized);
2217
+ return /(?:^|\/)(?:test|tests|__tests__|spec|specs)(?:\/|$)/u.test(normalized) || /(?:\.(?:test|spec)|_(?:test|spec))\.[^/]+$/u.test(normalized) || /(?:^|\/)(?:test|spec)_[^/]+\.[^/]+$/u.test(normalized);
2218
2218
  }
2219
2219
  function isFixturePath(filePath) {
2220
2220
  const normalized = normalizePath(filePath);
@@ -2589,8 +2589,8 @@ function formatExactSearchHandoff(results) {
2589
2589
  }
2590
2590
  function formatContextEvidence(result, index) {
2591
2591
  const symbol = result.name ? ` ${JSON.stringify(compactEvidenceValue(result.name, 80))}` : "";
2592
- const path28 = compactEvidenceValue(result.filePath, 120);
2593
- return `[${index}] ${result.chunkType}${symbol} in ${path28}:${result.startLine}-${result.endLine} (score ${result.score.toFixed(2)})`;
2592
+ const path30 = compactEvidenceValue(result.filePath, 120);
2593
+ return `[${index}] ${result.chunkType}${symbol} in ${path30}:${result.startLine}-${result.endLine} (score ${result.score.toFixed(2)})`;
2594
2594
  }
2595
2595
  function formatContextPack(heading, selected, candidateCount, duplicateCount, limitOmittedCount, budgetOmittedCount, includeExactSearchHandoff) {
2596
2596
  const lines = selected.map((result, index) => formatContextEvidence(result, index + 1));
@@ -3743,8 +3743,8 @@ async function* walkDirectory(dir, projectRoot, includePatterns, excludePatterns
3743
3743
  if (entry.isDirectory()) {
3744
3744
  subdirs.push({ fullPath, relativePath });
3745
3745
  } else if (entry.isFile()) {
3746
- const stat4 = await import_fs6.promises.stat(fullPath);
3747
- if (stat4.size > maxFileSize) {
3746
+ const stat5 = await import_fs6.promises.stat(fullPath);
3747
+ if (stat5.size > maxFileSize) {
3748
3748
  skipped.push({ path: relativePath, reason: "too_large" });
3749
3749
  continue;
3750
3750
  }
@@ -3762,7 +3762,7 @@ async function* walkDirectory(dir, projectRoot, includePatterns, excludePatterns
3762
3762
  }
3763
3763
  }
3764
3764
  if (matched) {
3765
- filesInDir.push({ path: fullPath, size: stat4.size });
3765
+ filesInDir.push({ path: fullPath, size: stat5.size });
3766
3766
  }
3767
3767
  }
3768
3768
  }
@@ -3819,8 +3819,8 @@ async function collectFiles(projectRoot, includePatterns, excludePatterns, maxFi
3819
3819
  }
3820
3820
  for (const resolvedKbRoot of normalizedRoots) {
3821
3821
  try {
3822
- const stat4 = await import_fs6.promises.stat(resolvedKbRoot);
3823
- if (!stat4.isDirectory()) {
3822
+ const stat5 = await import_fs6.promises.stat(resolvedKbRoot);
3823
+ if (!stat5.isDirectory()) {
3824
3824
  skipped.push({ path: resolvedKbRoot, reason: "excluded" });
3825
3825
  continue;
3826
3826
  }
@@ -3854,7 +3854,7 @@ function getErrorMessage(error) {
3854
3854
  return error instanceof Error ? error.message : String(error);
3855
3855
  }
3856
3856
  function runCommand(file, args, options) {
3857
- return new Promise((resolve15, reject) => {
3857
+ return new Promise((resolve17, reject) => {
3858
3858
  childProcess.execFile(
3859
3859
  file,
3860
3860
  args,
@@ -3864,7 +3864,7 @@ function runCommand(file, args, options) {
3864
3864
  reject(error);
3865
3865
  return;
3866
3866
  }
3867
- resolve15(stdout);
3867
+ resolve17(stdout);
3868
3868
  }
3869
3869
  );
3870
3870
  });
@@ -4009,10 +4009,10 @@ function safeFailureMessage(error) {
4009
4009
  }
4010
4010
  function cancellableDelay(delayMs, signal) {
4011
4011
  if (signal.aborted) return Promise.reject(new AutoIndexCancelledError());
4012
- return new Promise((resolve15, reject) => {
4012
+ return new Promise((resolve17, reject) => {
4013
4013
  const timer = setTimeout(() => {
4014
4014
  signal.removeEventListener("abort", onAbort);
4015
- resolve15();
4015
+ resolve17();
4016
4016
  }, delayMs);
4017
4017
  timer.unref?.();
4018
4018
  const onAbort = () => {
@@ -4024,15 +4024,15 @@ function cancellableDelay(delayMs, signal) {
4024
4024
  }
4025
4025
  function withTimeout(promise, timeoutMs) {
4026
4026
  if (timeoutMs <= 0) return Promise.resolve(void 0);
4027
- return new Promise((resolve15) => {
4028
- const timer = setTimeout(() => resolve15(void 0), timeoutMs);
4027
+ return new Promise((resolve17) => {
4028
+ const timer = setTimeout(() => resolve17(void 0), timeoutMs);
4029
4029
  timer.unref?.();
4030
4030
  void promise.then((value) => {
4031
4031
  clearTimeout(timer);
4032
- resolve15(value);
4032
+ resolve17(value);
4033
4033
  }, () => {
4034
4034
  clearTimeout(timer);
4035
- resolve15(void 0);
4035
+ resolve17(void 0);
4036
4036
  });
4037
4037
  });
4038
4038
  }
@@ -4414,17 +4414,17 @@ var AutoIndexCoordinator = class {
4414
4414
  }
4415
4415
  }
4416
4416
  waitForBatteryRetry(delayMs) {
4417
- return new Promise((resolve15) => {
4417
+ return new Promise((resolve17) => {
4418
4418
  const timer = setTimeout(() => {
4419
4419
  if (this.batteryRetryTimer === timer) {
4420
4420
  this.batteryRetryTimer = null;
4421
4421
  this.resolveBatteryRetry = null;
4422
4422
  }
4423
- resolve15();
4423
+ resolve17();
4424
4424
  }, delayMs);
4425
4425
  timer.unref?.();
4426
4426
  this.batteryRetryTimer = timer;
4427
- this.resolveBatteryRetry = resolve15;
4427
+ this.resolveBatteryRetry = resolve17;
4428
4428
  });
4429
4429
  }
4430
4430
  cancelBatteryRetry() {
@@ -4432,9 +4432,9 @@ var AutoIndexCoordinator = class {
4432
4432
  clearTimeout(this.batteryRetryTimer);
4433
4433
  this.batteryRetryTimer = null;
4434
4434
  }
4435
- const resolve15 = this.resolveBatteryRetry;
4435
+ const resolve17 = this.resolveBatteryRetry;
4436
4436
  this.resolveBatteryRetry = null;
4437
- resolve15?.();
4437
+ resolve17?.();
4438
4438
  }
4439
4439
  finishBatteryCheck(batteryCheck) {
4440
4440
  if (this.batteryCheck !== batteryCheck) return;
@@ -4639,7 +4639,7 @@ function pTimeout(promise, options) {
4639
4639
  } = options;
4640
4640
  let timer;
4641
4641
  let abortHandler;
4642
- const wrappedPromise = new Promise((resolve15, reject) => {
4642
+ const wrappedPromise = new Promise((resolve17, reject) => {
4643
4643
  if (typeof milliseconds !== "number" || Math.sign(milliseconds) !== 1) {
4644
4644
  throw new TypeError(`Expected \`milliseconds\` to be a positive number, got \`${milliseconds}\``);
4645
4645
  }
@@ -4653,7 +4653,7 @@ function pTimeout(promise, options) {
4653
4653
  };
4654
4654
  signal.addEventListener("abort", abortHandler, { once: true });
4655
4655
  }
4656
- promise.then(resolve15, reject);
4656
+ promise.then(resolve17, reject);
4657
4657
  if (milliseconds === Number.POSITIVE_INFINITY) {
4658
4658
  return;
4659
4659
  }
@@ -4661,7 +4661,7 @@ function pTimeout(promise, options) {
4661
4661
  timer = customTimers.setTimeout.call(void 0, () => {
4662
4662
  if (fallback) {
4663
4663
  try {
4664
- resolve15(fallback());
4664
+ resolve17(fallback());
4665
4665
  } catch (error) {
4666
4666
  reject(error);
4667
4667
  }
@@ -4671,7 +4671,7 @@ function pTimeout(promise, options) {
4671
4671
  promise.cancel();
4672
4672
  }
4673
4673
  if (message === false) {
4674
- resolve15();
4674
+ resolve17();
4675
4675
  } else if (message instanceof Error) {
4676
4676
  reject(message);
4677
4677
  } else {
@@ -5073,7 +5073,7 @@ var PQueue = class extends import_index.default {
5073
5073
  // Assign unique ID if not provided
5074
5074
  id: options.id ?? (this.#idAssigner++).toString()
5075
5075
  };
5076
- return new Promise((resolve15, reject) => {
5076
+ return new Promise((resolve17, reject) => {
5077
5077
  const taskSymbol = /* @__PURE__ */ Symbol(`task-${options.id}`);
5078
5078
  let cleanupQueueAbortHandler = () => void 0;
5079
5079
  const run = async () => {
@@ -5113,7 +5113,7 @@ var PQueue = class extends import_index.default {
5113
5113
  })]);
5114
5114
  }
5115
5115
  const result = await operation;
5116
- resolve15(result);
5116
+ resolve17(result);
5117
5117
  this.emit("completed", result);
5118
5118
  } catch (error) {
5119
5119
  reject(error);
@@ -5301,13 +5301,13 @@ var PQueue = class extends import_index.default {
5301
5301
  });
5302
5302
  }
5303
5303
  async #onEvent(event, filter) {
5304
- return new Promise((resolve15) => {
5304
+ return new Promise((resolve17) => {
5305
5305
  const listener = () => {
5306
5306
  if (filter && !filter()) {
5307
5307
  return;
5308
5308
  }
5309
5309
  this.off(event, listener);
5310
- resolve15();
5310
+ resolve17();
5311
5311
  };
5312
5312
  this.on(event, listener);
5313
5313
  });
@@ -5593,7 +5593,7 @@ async function onAttemptFailure({ error, attemptNumber, retriesConsumed, startTi
5593
5593
  const finalDelay = Math.min(delayTime, remainingTime);
5594
5594
  options.signal?.throwIfAborted();
5595
5595
  if (finalDelay > 0) {
5596
- await new Promise((resolve15, reject) => {
5596
+ await new Promise((resolve17, reject) => {
5597
5597
  const onAbort = () => {
5598
5598
  clearTimeout(timeoutToken);
5599
5599
  options.signal?.removeEventListener("abort", onAbort);
@@ -5601,7 +5601,7 @@ async function onAttemptFailure({ error, attemptNumber, retriesConsumed, startTi
5601
5601
  };
5602
5602
  const timeoutToken = setTimeout(() => {
5603
5603
  options.signal?.removeEventListener("abort", onAbort);
5604
- resolve15();
5604
+ resolve17();
5605
5605
  }, finalDelay);
5606
5606
  if (options.unref) {
5607
5607
  timeoutToken.unref?.();
@@ -6407,85 +6407,6 @@ function createEmbeddingProvider(configuredProviderInfo) {
6407
6407
  }
6408
6408
  }
6409
6409
 
6410
- // src/rerank/index.ts
6411
- function createReranker(config) {
6412
- if (!config.enabled) {
6413
- return new NoOpReranker();
6414
- }
6415
- return new SiliconFlowReranker(config);
6416
- }
6417
- var NoOpReranker = class {
6418
- isAvailable() {
6419
- return false;
6420
- }
6421
- async rerank(_query, documents, _topN) {
6422
- return {
6423
- results: documents.map((_, index) => ({ index, relevanceScore: 0 }))
6424
- };
6425
- }
6426
- };
6427
- var SiliconFlowReranker = class {
6428
- config;
6429
- constructor(config) {
6430
- this.config = config;
6431
- }
6432
- isAvailable() {
6433
- return this.config.enabled && !!this.config.baseUrl && !!this.config.model;
6434
- }
6435
- async rerank(query, documents, topN) {
6436
- if (documents.length === 0) {
6437
- return { results: [] };
6438
- }
6439
- const headers = {
6440
- "Content-Type": "application/json"
6441
- };
6442
- if (this.config.apiKey) {
6443
- headers["Authorization"] = `Bearer ${this.config.apiKey}`;
6444
- }
6445
- const baseUrl = this.config.baseUrl;
6446
- if (!baseUrl) {
6447
- throw new Error("Reranker baseUrl is required. Configure reranker.baseUrl in your codebase-index.json.");
6448
- }
6449
- const timeoutMs = this.config.timeoutMs ?? 3e4;
6450
- const controller = new AbortController();
6451
- const timeout = setTimeout(() => controller.abort(), timeoutMs);
6452
- try {
6453
- const response = await fetch(`${baseUrl}/rerank`, {
6454
- method: "POST",
6455
- headers,
6456
- body: JSON.stringify({
6457
- model: this.config.model,
6458
- query,
6459
- documents,
6460
- top_n: topN ?? this.config.topN ?? 20,
6461
- return_documents: false
6462
- }),
6463
- signal: controller.signal
6464
- });
6465
- clearTimeout(timeout);
6466
- if (!response.ok) {
6467
- const errorText = await response.text();
6468
- throw new Error(`Rerank API error: ${response.status} - ${errorText}`);
6469
- }
6470
- const data = await response.json();
6471
- return {
6472
- results: data.results.map((r) => ({
6473
- index: r.index,
6474
- relevanceScore: r.relevance_score,
6475
- document: r.document?.text
6476
- })),
6477
- tokensUsed: data.meta?.tokens?.input_tokens
6478
- };
6479
- } catch (error) {
6480
- clearTimeout(timeout);
6481
- if (error instanceof Error && error.name === "AbortError") {
6482
- throw new Error(`Rerank API request timed out after ${timeoutMs}ms`);
6483
- }
6484
- throw error;
6485
- }
6486
- }
6487
- };
6488
-
6489
6410
  // src/utils/cost.ts
6490
6411
  function estimateChunksFromFiles(files) {
6491
6412
  let totalChunks = 0;
@@ -8046,8 +7967,8 @@ async function isWorktreeRegistered(projectRoot, worktreePath) {
8046
7967
  return false;
8047
7968
  }
8048
7969
  function isPathWithinRoot(filePath, rootPath) {
8049
- const relative12 = path15.relative(path15.resolve(rootPath), path15.resolve(filePath));
8050
- return relative12 === "" || !relative12.startsWith(`..${path15.sep}`) && relative12 !== ".." && !path15.isAbsolute(relative12);
7970
+ const relative14 = path15.relative(path15.resolve(rootPath), path15.resolve(filePath));
7971
+ return relative14 === "" || !relative14.startsWith(`..${path15.sep}`) && relative14 !== ".." && !path15.isAbsolute(relative14);
8051
7972
  }
8052
7973
  async function pruneExactMissingWorktreeRegistration(projectRoot, worktreePath) {
8053
7974
  if (await pathExists(worktreePath)) return false;
@@ -8424,11 +8345,11 @@ function normalizeFiles(rawFiles, projectRoot) {
8424
8345
  for (const raw of rawFiles) {
8425
8346
  if (raw.length === 0) continue;
8426
8347
  const absolute = path16.resolve(root, raw);
8427
- const relative12 = path16.relative(root, absolute);
8428
- if (path16.isAbsolute(raw) || relative12 === ".." || relative12.startsWith(`..${path16.sep}`) || path16.isAbsolute(relative12)) {
8348
+ const relative14 = path16.relative(root, absolute);
8349
+ if (path16.isAbsolute(raw) || relative14 === ".." || relative14.startsWith(`..${path16.sep}`) || path16.isAbsolute(relative14)) {
8429
8350
  throw new Error(`Changed file escapes the project root: ${JSON.stringify(raw)}`);
8430
8351
  }
8431
- const cleaned = relative12.startsWith(`.${path16.sep}`) ? relative12.slice(2) : relative12;
8352
+ const cleaned = relative14.startsWith(`.${path16.sep}`) ? relative14.slice(2) : relative14;
8432
8353
  if (!seen.has(cleaned)) {
8433
8354
  seen.add(cleaned);
8434
8355
  result.push(cleaned);
@@ -8660,7 +8581,8 @@ function rankHybridResults(query, semanticResults, keywordResults, options) {
8660
8581
  return cached;
8661
8582
  }
8662
8583
  }
8663
- const overfetchLimit = Math.max(options.limit * 4, options.limit);
8584
+ const overfetchFactor = prioritizeSourcePaths ? 12 : 4;
8585
+ const overfetchLimit = Math.max(options.limit * overfetchFactor, options.limit);
8664
8586
  const fused = options.fusionStrategy === "rrf" ? fuseResultsRrf(semanticResults, keywordResults, options.rrfK, overfetchLimit) : fuseResultsWeighted(semanticResults, keywordResults, options.hybridWeight, overfetchLimit);
8665
8587
  const rerankPoolLimit = Math.max(overfetchLimit, options.rerankTopN * 3, options.limit * 6);
8666
8588
  const rerankPool = fused.slice(0, rerankPoolLimit);
@@ -10171,7 +10093,6 @@ var Indexer = class _Indexer {
10171
10093
  database = null;
10172
10094
  provider = null;
10173
10095
  configuredProviderInfo = null;
10174
- reranker = null;
10175
10096
  fileHashCache = /* @__PURE__ */ new Map();
10176
10097
  fileHashCachePath = "";
10177
10098
  failedBatchesPath = "";
@@ -10331,7 +10252,6 @@ var Indexer = class _Indexer {
10331
10252
  this.database = null;
10332
10253
  this.provider = null;
10333
10254
  this.configuredProviderInfo = null;
10334
- this.reranker = null;
10335
10255
  this.indexCompatibility = null;
10336
10256
  this.initializationMode = "none";
10337
10257
  this.readIssues = [];
@@ -11061,7 +10981,7 @@ var Indexer = class _Indexer {
11061
10981
  await options.queue.onSizeLessThan(Math.max(1, options.providerRateLimits.concurrency));
11062
10982
  const task = options.queue.add(async () => {
11063
10983
  if (options.rateLimitState.backoffMs > 0) {
11064
- await new Promise((resolve15) => setTimeout(resolve15, options.rateLimitState.backoffMs));
10984
+ await new Promise((resolve17) => setTimeout(resolve17, options.rateLimitState.backoffMs));
11065
10985
  }
11066
10986
  try {
11067
10987
  const embeddingResult = await pRetry(
@@ -11628,15 +11548,6 @@ var Indexer = class _Indexer {
11628
11548
  rerankerEnabled: this.config.reranker?.enabled ?? false
11629
11549
  });
11630
11550
  this.provider = createEmbeddingProvider(this.configuredProviderInfo);
11631
- if (this.config.reranker?.enabled) {
11632
- this.reranker = createReranker(this.config.reranker);
11633
- if (this.reranker.isAvailable()) {
11634
- this.logger.info("Reranker initialized", {
11635
- model: this.config.reranker.model,
11636
- baseUrl: this.config.reranker.baseUrl
11637
- });
11638
- }
11639
- }
11640
11551
  const dimensions = this.configuredProviderInfo.modelInfo.dimensions;
11641
11552
  const storePath = path19.join(this.indexPath, "vectors");
11642
11553
  const vectorMetadataPath = `${storePath}.meta.json`;
@@ -13041,6 +12952,7 @@ var Indexer = class _Indexer {
13041
12952
  const filterByBranch = options?.filterByBranch ?? true;
13042
12953
  const sourceIntent = options?.definitionIntent === true || classifyQueryIntentRaw(query) === "source";
13043
12954
  const identifierHints = extractIdentifierHints(query);
12955
+ const candidateLimit = maxResults * (sourceIntent ? 12 : 4);
13044
12956
  this.logger.search("debug", "Starting search", {
13045
12957
  query,
13046
12958
  maxResults,
@@ -13077,7 +12989,7 @@ var Indexer = class _Indexer {
13077
12989
  const semanticCandidates = embedding ? this.searchSemanticCandidates(
13078
12990
  store,
13079
12991
  embedding,
13080
- maxResults * 4,
12992
+ candidateLimit,
13081
12993
  branchChunkIds,
13082
12994
  shouldPrefilterByBranch
13083
12995
  ) : [];
@@ -13085,7 +12997,7 @@ var Indexer = class _Indexer {
13085
12997
  const keywordStartTime = import_perf_hooks.performance.now();
13086
12998
  const keywordCandidates = await this.keywordSearch(
13087
12999
  query,
13088
- maxResults * 4,
13000
+ candidateLimit,
13089
13001
  store,
13090
13002
  invertedIndex,
13091
13003
  branchChunkIds,
@@ -13843,9 +13755,9 @@ var Indexer = class _Indexer {
13843
13755
  this.requireReadableComponents(readIssues, "database");
13844
13756
  let shortest = [];
13845
13757
  for (const branchKey of this.getBranchCatalogKeys()) {
13846
- const path28 = database.findShortestPath(fromName, toName, branchKey, maxDepth);
13847
- if (path28.length > 0 && (shortest.length === 0 || path28.length < shortest.length)) {
13848
- shortest = path28;
13758
+ const path30 = database.findShortestPath(fromName, toName, branchKey, maxDepth);
13759
+ if (path30.length > 0 && (shortest.length === 0 || path30.length < shortest.length)) {
13760
+ shortest = path30;
13849
13761
  }
13850
13762
  }
13851
13763
  return shortest.map((hop) => this.resolveFilePathRecord(hop));
@@ -13893,13 +13805,13 @@ var Indexer = class _Indexer {
13893
13805
  }
13894
13806
  }
13895
13807
  if (!found) continue;
13896
- const path28 = [];
13808
+ const path30 = [];
13897
13809
  let currentSymbolId = toSymbolId;
13898
13810
  while (true) {
13899
13811
  const symbol = symbolsById.get(currentSymbolId);
13900
13812
  if (!symbol) break;
13901
13813
  const parent = parentBySymbolId.get(currentSymbolId);
13902
- path28.push({
13814
+ path30.push({
13903
13815
  symbolId: symbol.id,
13904
13816
  symbolName: symbol.name,
13905
13817
  filePath: symbol.filePath,
@@ -13909,9 +13821,9 @@ var Indexer = class _Indexer {
13909
13821
  if (!parent) break;
13910
13822
  currentSymbolId = parent.parentId;
13911
13823
  }
13912
- path28.reverse();
13913
- if (path28.length > 0 && (shortest.length === 0 || path28.length < shortest.length)) {
13914
- shortest = path28;
13824
+ path30.reverse();
13825
+ if (path30.length > 0 && (shortest.length === 0 || path30.length < shortest.length)) {
13826
+ shortest = path30;
13915
13827
  }
13916
13828
  }
13917
13829
  return shortest.map((hop) => this.resolveFilePathRecord(hop));
@@ -14247,7 +14159,6 @@ var Indexer = class _Indexer {
14247
14159
  this.store = null;
14248
14160
  this.invertedIndex = null;
14249
14161
  this.provider = null;
14250
- this.reranker = null;
14251
14162
  this.configuredProviderInfo = null;
14252
14163
  this.indexCompatibility = null;
14253
14164
  this.initializationMode = "none";
@@ -14572,12 +14483,12 @@ async function getCallGraphPath(projectRoot, host, from, to, maxDepth, fromFileP
14572
14483
  if (fromResolution.status !== "resolved" || toResolution.status !== "resolved") {
14573
14484
  return { from: fromResolution, to: toResolution, path: [] };
14574
14485
  }
14575
- const path28 = await indexer.findCallPathBySymbolIds(
14486
+ const path30 = await indexer.findCallPathBySymbolIds(
14576
14487
  fromResolution.symbolId,
14577
14488
  toResolution.symbolId,
14578
14489
  maxDepth
14579
14490
  );
14580
- return { from: fromResolution, to: toResolution, path: path28 };
14491
+ return { from: fromResolution, to: toResolution, path: path30 };
14581
14492
  }
14582
14493
  async function runIndexCodebase(projectRoot, host, args, onProgress) {
14583
14494
  const root = getProjectRoot(projectRoot, host);
@@ -14803,8 +14714,8 @@ function addKnowledgeBase(projectRoot, host, knowledgeBasePath) {
14803
14714
  }
14804
14715
  }
14805
14716
  try {
14806
- const stat4 = (0, import_fs13.statSync)(normalizedPath2);
14807
- if (!stat4.isDirectory()) {
14717
+ const stat5 = (0, import_fs13.statSync)(normalizedPath2);
14718
+ if (!stat5.isDirectory()) {
14808
14719
  return `Error: Path is not a directory: ${normalizedPath2}`;
14809
14720
  }
14810
14721
  } catch (error) {
@@ -14852,8 +14763,8 @@ function listKnowledgeBases(projectRoot, host) {
14852
14763
  `;
14853
14764
  if (exists) {
14854
14765
  try {
14855
- const stat4 = (0, import_fs13.statSync)(resolvedPath);
14856
- result += ` Type: ${stat4.isDirectory() ? "Directory" : "File"}
14766
+ const stat5 = (0, import_fs13.statSync)(resolvedPath);
14767
+ result += ` Type: ${stat5.isDirectory() ? "Directory" : "File"}
14857
14768
  `;
14858
14769
  } catch {
14859
14770
  }
@@ -14986,7 +14897,7 @@ var ReaddirpStream = class extends import_node_stream.Readable {
14986
14897
  this._directoryFilter = normalizeFilter(opts.directoryFilter);
14987
14898
  const statMethod = opts.lstat ? import_promises.lstat : import_promises.stat;
14988
14899
  if (wantBigintFsStats) {
14989
- this._stat = (path28) => statMethod(path28, { bigint: true });
14900
+ this._stat = (path30) => statMethod(path30, { bigint: true });
14990
14901
  } else {
14991
14902
  this._stat = statMethod;
14992
14903
  }
@@ -15011,8 +14922,8 @@ var ReaddirpStream = class extends import_node_stream.Readable {
15011
14922
  const par = this.parent;
15012
14923
  const fil = par && par.files;
15013
14924
  if (fil && fil.length > 0) {
15014
- const { path: path28, depth } = par;
15015
- const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path28));
14925
+ const { path: path30, depth } = par;
14926
+ const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path30));
15016
14927
  const awaited = await Promise.all(slice);
15017
14928
  for (const entry of awaited) {
15018
14929
  if (!entry)
@@ -15052,20 +14963,20 @@ var ReaddirpStream = class extends import_node_stream.Readable {
15052
14963
  this.reading = false;
15053
14964
  }
15054
14965
  }
15055
- async _exploreDir(path28, depth) {
14966
+ async _exploreDir(path30, depth) {
15056
14967
  let files;
15057
14968
  try {
15058
- files = await (0, import_promises.readdir)(path28, this._rdOptions);
14969
+ files = await (0, import_promises.readdir)(path30, this._rdOptions);
15059
14970
  } catch (error) {
15060
14971
  this._onError(error);
15061
14972
  }
15062
- return { files, depth, path: path28 };
14973
+ return { files, depth, path: path30 };
15063
14974
  }
15064
- async _formatEntry(dirent, path28) {
14975
+ async _formatEntry(dirent, path30) {
15065
14976
  let entry;
15066
14977
  const basename9 = this._isDirent ? dirent.name : dirent;
15067
14978
  try {
15068
- const fullPath = (0, import_node_path.resolve)((0, import_node_path.join)(path28, basename9));
14979
+ const fullPath = (0, import_node_path.resolve)((0, import_node_path.join)(path30, basename9));
15069
14980
  entry = { path: (0, import_node_path.relative)(this._root, fullPath), fullPath, basename: basename9 };
15070
14981
  entry[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath);
15071
14982
  } catch (err) {
@@ -15465,16 +15376,16 @@ var delFromSet = (main, prop, item) => {
15465
15376
  };
15466
15377
  var isEmptySet = (val) => val instanceof Set ? val.size === 0 : !val;
15467
15378
  var FsWatchInstances = /* @__PURE__ */ new Map();
15468
- function createFsWatchInstance(path28, options, listener, errHandler, emitRaw) {
15379
+ function createFsWatchInstance(path30, options, listener, errHandler, emitRaw) {
15469
15380
  const handleEvent = (rawEvent, evPath) => {
15470
- listener(path28);
15471
- emitRaw(rawEvent, evPath, { watchedPath: path28 });
15472
- if (evPath && path28 !== evPath) {
15473
- fsWatchBroadcast(sp.resolve(path28, evPath), KEY_LISTENERS, sp.join(path28, evPath));
15381
+ listener(path30);
15382
+ emitRaw(rawEvent, evPath, { watchedPath: path30 });
15383
+ if (evPath && path30 !== evPath) {
15384
+ fsWatchBroadcast(sp.resolve(path30, evPath), KEY_LISTENERS, sp.join(path30, evPath));
15474
15385
  }
15475
15386
  };
15476
15387
  try {
15477
- return (0, import_node_fs.watch)(path28, {
15388
+ return (0, import_node_fs.watch)(path30, {
15478
15389
  persistent: options.persistent
15479
15390
  }, handleEvent);
15480
15391
  } catch (error) {
@@ -15490,12 +15401,12 @@ var fsWatchBroadcast = (fullPath, listenerType, val1, val2, val3) => {
15490
15401
  listener(val1, val2, val3);
15491
15402
  });
15492
15403
  };
15493
- var setFsWatchListener = (path28, fullPath, options, handlers) => {
15404
+ var setFsWatchListener = (path30, fullPath, options, handlers) => {
15494
15405
  const { listener, errHandler, rawEmitter } = handlers;
15495
15406
  let cont = FsWatchInstances.get(fullPath);
15496
15407
  let watcher;
15497
15408
  if (!options.persistent) {
15498
- watcher = createFsWatchInstance(path28, options, listener, errHandler, rawEmitter);
15409
+ watcher = createFsWatchInstance(path30, options, listener, errHandler, rawEmitter);
15499
15410
  if (!watcher)
15500
15411
  return;
15501
15412
  return watcher.close.bind(watcher);
@@ -15506,7 +15417,7 @@ var setFsWatchListener = (path28, fullPath, options, handlers) => {
15506
15417
  addAndConvert(cont, KEY_RAW, rawEmitter);
15507
15418
  } else {
15508
15419
  watcher = createFsWatchInstance(
15509
- path28,
15420
+ path30,
15510
15421
  options,
15511
15422
  fsWatchBroadcast.bind(null, fullPath, KEY_LISTENERS),
15512
15423
  errHandler,
@@ -15521,7 +15432,7 @@ var setFsWatchListener = (path28, fullPath, options, handlers) => {
15521
15432
  cont.watcherUnusable = true;
15522
15433
  if (isWindows && error.code === "EPERM") {
15523
15434
  try {
15524
- const fd = await (0, import_promises2.open)(path28, "r");
15435
+ const fd = await (0, import_promises2.open)(path30, "r");
15525
15436
  await fd.close();
15526
15437
  broadcastErr(error);
15527
15438
  } catch (err) {
@@ -15552,7 +15463,7 @@ var setFsWatchListener = (path28, fullPath, options, handlers) => {
15552
15463
  };
15553
15464
  };
15554
15465
  var FsWatchFileInstances = /* @__PURE__ */ new Map();
15555
- var setFsWatchFileListener = (path28, fullPath, options, handlers) => {
15466
+ var setFsWatchFileListener = (path30, fullPath, options, handlers) => {
15556
15467
  const { listener, rawEmitter } = handlers;
15557
15468
  let cont = FsWatchFileInstances.get(fullPath);
15558
15469
  const copts = cont && cont.options;
@@ -15574,7 +15485,7 @@ var setFsWatchFileListener = (path28, fullPath, options, handlers) => {
15574
15485
  });
15575
15486
  const currmtime = curr.mtimeMs;
15576
15487
  if (curr.size !== prev.size || currmtime > prev.mtimeMs || currmtime === 0) {
15577
- foreach(cont.listeners, (listener2) => listener2(path28, curr));
15488
+ foreach(cont.listeners, (listener2) => listener2(path30, curr));
15578
15489
  }
15579
15490
  })
15580
15491
  };
@@ -15604,13 +15515,13 @@ var NodeFsHandler = class {
15604
15515
  * @param listener on fs change
15605
15516
  * @returns closer for the watcher instance
15606
15517
  */
15607
- _watchWithNodeFs(path28, listener) {
15518
+ _watchWithNodeFs(path30, listener) {
15608
15519
  const opts = this.fsw.options;
15609
- const directory = sp.dirname(path28);
15610
- const basename9 = sp.basename(path28);
15520
+ const directory = sp.dirname(path30);
15521
+ const basename9 = sp.basename(path30);
15611
15522
  const parent = this.fsw._getWatchedDir(directory);
15612
15523
  parent.add(basename9);
15613
- const absolutePath = sp.resolve(path28);
15524
+ const absolutePath = sp.resolve(path30);
15614
15525
  const options = {
15615
15526
  persistent: opts.persistent
15616
15527
  };
@@ -15620,12 +15531,12 @@ var NodeFsHandler = class {
15620
15531
  if (opts.usePolling) {
15621
15532
  const enableBin = opts.interval !== opts.binaryInterval;
15622
15533
  options.interval = enableBin && isBinaryPath(basename9) ? opts.binaryInterval : opts.interval;
15623
- closer = setFsWatchFileListener(path28, absolutePath, options, {
15534
+ closer = setFsWatchFileListener(path30, absolutePath, options, {
15624
15535
  listener,
15625
15536
  rawEmitter: this.fsw._emitRaw
15626
15537
  });
15627
15538
  } else {
15628
- closer = setFsWatchListener(path28, absolutePath, options, {
15539
+ closer = setFsWatchListener(path30, absolutePath, options, {
15629
15540
  listener,
15630
15541
  errHandler: this._boundHandleError,
15631
15542
  rawEmitter: this.fsw._emitRaw
@@ -15647,7 +15558,7 @@ var NodeFsHandler = class {
15647
15558
  let prevStats = stats;
15648
15559
  if (parent.has(basename9))
15649
15560
  return;
15650
- const listener = async (path28, newStats) => {
15561
+ const listener = async (path30, newStats) => {
15651
15562
  if (!this.fsw._throttle(THROTTLE_MODE_WATCH, file, 5))
15652
15563
  return;
15653
15564
  if (!newStats || newStats.mtimeMs === 0) {
@@ -15661,11 +15572,11 @@ var NodeFsHandler = class {
15661
15572
  this.fsw._emit(EV.CHANGE, file, newStats2);
15662
15573
  }
15663
15574
  if ((isMacos || isLinux || isFreeBSD) && prevStats.ino !== newStats2.ino) {
15664
- this.fsw._closeFile(path28);
15575
+ this.fsw._closeFile(path30);
15665
15576
  prevStats = newStats2;
15666
15577
  const closer2 = this._watchWithNodeFs(file, listener);
15667
15578
  if (closer2)
15668
- this.fsw._addPathCloser(path28, closer2);
15579
+ this.fsw._addPathCloser(path30, closer2);
15669
15580
  } else {
15670
15581
  prevStats = newStats2;
15671
15582
  }
@@ -15697,7 +15608,7 @@ var NodeFsHandler = class {
15697
15608
  * @param item basename of this item
15698
15609
  * @returns true if no more processing is needed for this entry.
15699
15610
  */
15700
- async _handleSymlink(entry, directory, path28, item) {
15611
+ async _handleSymlink(entry, directory, path30, item) {
15701
15612
  if (this.fsw.closed) {
15702
15613
  return;
15703
15614
  }
@@ -15707,7 +15618,7 @@ var NodeFsHandler = class {
15707
15618
  this.fsw._incrReadyCount();
15708
15619
  let linkPath;
15709
15620
  try {
15710
- linkPath = await (0, import_promises2.realpath)(path28);
15621
+ linkPath = await (0, import_promises2.realpath)(path30);
15711
15622
  } catch (e) {
15712
15623
  this.fsw._emitReady();
15713
15624
  return true;
@@ -15717,12 +15628,12 @@ var NodeFsHandler = class {
15717
15628
  if (dir.has(item)) {
15718
15629
  if (this.fsw._symlinkPaths.get(full) !== linkPath) {
15719
15630
  this.fsw._symlinkPaths.set(full, linkPath);
15720
- this.fsw._emit(EV.CHANGE, path28, entry.stats);
15631
+ this.fsw._emit(EV.CHANGE, path30, entry.stats);
15721
15632
  }
15722
15633
  } else {
15723
15634
  dir.add(item);
15724
15635
  this.fsw._symlinkPaths.set(full, linkPath);
15725
- this.fsw._emit(EV.ADD, path28, entry.stats);
15636
+ this.fsw._emit(EV.ADD, path30, entry.stats);
15726
15637
  }
15727
15638
  this.fsw._emitReady();
15728
15639
  return true;
@@ -15752,9 +15663,9 @@ var NodeFsHandler = class {
15752
15663
  return;
15753
15664
  }
15754
15665
  const item = entry.path;
15755
- let path28 = sp.join(directory, item);
15666
+ let path30 = sp.join(directory, item);
15756
15667
  current.add(item);
15757
- if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path28, item)) {
15668
+ if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path30, item)) {
15758
15669
  return;
15759
15670
  }
15760
15671
  if (this.fsw.closed) {
@@ -15763,11 +15674,11 @@ var NodeFsHandler = class {
15763
15674
  }
15764
15675
  if (item === target || !target && !previous.has(item)) {
15765
15676
  this.fsw._incrReadyCount();
15766
- path28 = sp.join(dir, sp.relative(dir, path28));
15767
- this._addToNodeFs(path28, initialAdd, wh, depth + 1);
15677
+ path30 = sp.join(dir, sp.relative(dir, path30));
15678
+ this._addToNodeFs(path30, initialAdd, wh, depth + 1);
15768
15679
  }
15769
15680
  }).on(EV.ERROR, this._boundHandleError);
15770
- return new Promise((resolve15, reject) => {
15681
+ return new Promise((resolve17, reject) => {
15771
15682
  if (!stream)
15772
15683
  return reject();
15773
15684
  stream.once(STR_END, () => {
@@ -15776,7 +15687,7 @@ var NodeFsHandler = class {
15776
15687
  return;
15777
15688
  }
15778
15689
  const wasThrottled = throttler ? throttler.clear() : false;
15779
- resolve15(void 0);
15690
+ resolve17(void 0);
15780
15691
  previous.getChildren().filter((item) => {
15781
15692
  return item !== directory && !current.has(item);
15782
15693
  }).forEach((item) => {
@@ -15833,13 +15744,13 @@ var NodeFsHandler = class {
15833
15744
  * @param depth Child path actually targeted for watch
15834
15745
  * @param target Child path actually targeted for watch
15835
15746
  */
15836
- async _addToNodeFs(path28, initialAdd, priorWh, depth, target) {
15747
+ async _addToNodeFs(path30, initialAdd, priorWh, depth, target) {
15837
15748
  const ready = this.fsw._emitReady;
15838
- if (this.fsw._isIgnored(path28) || this.fsw.closed) {
15749
+ if (this.fsw._isIgnored(path30) || this.fsw.closed) {
15839
15750
  ready();
15840
15751
  return false;
15841
15752
  }
15842
- const wh = this.fsw._getWatchHelpers(path28);
15753
+ const wh = this.fsw._getWatchHelpers(path30);
15843
15754
  if (priorWh) {
15844
15755
  wh.filterPath = (entry) => priorWh.filterPath(entry);
15845
15756
  wh.filterDir = (entry) => priorWh.filterDir(entry);
@@ -15855,8 +15766,8 @@ var NodeFsHandler = class {
15855
15766
  const follow = this.fsw.options.followSymlinks;
15856
15767
  let closer;
15857
15768
  if (stats.isDirectory()) {
15858
- const absPath = sp.resolve(path28);
15859
- const targetPath = follow ? await (0, import_promises2.realpath)(path28) : path28;
15769
+ const absPath = sp.resolve(path30);
15770
+ const targetPath = follow ? await (0, import_promises2.realpath)(path30) : path30;
15860
15771
  if (this.fsw.closed)
15861
15772
  return;
15862
15773
  closer = await this._handleDir(wh.watchPath, stats, initialAdd, depth, target, wh, targetPath);
@@ -15866,29 +15777,29 @@ var NodeFsHandler = class {
15866
15777
  this.fsw._symlinkPaths.set(absPath, targetPath);
15867
15778
  }
15868
15779
  } else if (stats.isSymbolicLink()) {
15869
- const targetPath = follow ? await (0, import_promises2.realpath)(path28) : path28;
15780
+ const targetPath = follow ? await (0, import_promises2.realpath)(path30) : path30;
15870
15781
  if (this.fsw.closed)
15871
15782
  return;
15872
15783
  const parent = sp.dirname(wh.watchPath);
15873
15784
  this.fsw._getWatchedDir(parent).add(wh.watchPath);
15874
15785
  this.fsw._emit(EV.ADD, wh.watchPath, stats);
15875
- closer = await this._handleDir(parent, stats, initialAdd, depth, path28, wh, targetPath);
15786
+ closer = await this._handleDir(parent, stats, initialAdd, depth, path30, wh, targetPath);
15876
15787
  if (this.fsw.closed)
15877
15788
  return;
15878
15789
  if (targetPath !== void 0) {
15879
- this.fsw._symlinkPaths.set(sp.resolve(path28), targetPath);
15790
+ this.fsw._symlinkPaths.set(sp.resolve(path30), targetPath);
15880
15791
  }
15881
15792
  } else {
15882
15793
  closer = this._handleFile(wh.watchPath, stats, initialAdd);
15883
15794
  }
15884
15795
  ready();
15885
15796
  if (closer)
15886
- this.fsw._addPathCloser(path28, closer);
15797
+ this.fsw._addPathCloser(path30, closer);
15887
15798
  return false;
15888
15799
  } catch (error) {
15889
15800
  if (this.fsw._handleError(error)) {
15890
15801
  ready();
15891
- return path28;
15802
+ return path30;
15892
15803
  }
15893
15804
  }
15894
15805
  }
@@ -15920,35 +15831,35 @@ function createPattern(matcher) {
15920
15831
  if (matcher.path === string)
15921
15832
  return true;
15922
15833
  if (matcher.recursive) {
15923
- const relative12 = sp2.relative(matcher.path, string);
15924
- if (!relative12) {
15834
+ const relative14 = sp2.relative(matcher.path, string);
15835
+ if (!relative14) {
15925
15836
  return false;
15926
15837
  }
15927
- return !relative12.startsWith("..") && !sp2.isAbsolute(relative12);
15838
+ return !relative14.startsWith("..") && !sp2.isAbsolute(relative14);
15928
15839
  }
15929
15840
  return false;
15930
15841
  };
15931
15842
  }
15932
15843
  return () => false;
15933
15844
  }
15934
- function normalizePath2(path28) {
15935
- if (typeof path28 !== "string")
15845
+ function normalizePath2(path30) {
15846
+ if (typeof path30 !== "string")
15936
15847
  throw new Error("string expected");
15937
- path28 = sp2.normalize(path28);
15938
- path28 = path28.replace(/\\/g, "/");
15848
+ path30 = sp2.normalize(path30);
15849
+ path30 = path30.replace(/\\/g, "/");
15939
15850
  let prepend = false;
15940
- if (path28.startsWith("//"))
15851
+ if (path30.startsWith("//"))
15941
15852
  prepend = true;
15942
- path28 = path28.replace(DOUBLE_SLASH_RE, "/");
15853
+ path30 = path30.replace(DOUBLE_SLASH_RE, "/");
15943
15854
  if (prepend)
15944
- path28 = "/" + path28;
15945
- return path28;
15855
+ path30 = "/" + path30;
15856
+ return path30;
15946
15857
  }
15947
15858
  function matchPatterns(patterns, testString, stats) {
15948
- const path28 = normalizePath2(testString);
15859
+ const path30 = normalizePath2(testString);
15949
15860
  for (let index = 0; index < patterns.length; index++) {
15950
15861
  const pattern = patterns[index];
15951
- if (pattern(path28, stats)) {
15862
+ if (pattern(path30, stats)) {
15952
15863
  return true;
15953
15864
  }
15954
15865
  }
@@ -15986,19 +15897,19 @@ var toUnix = (string) => {
15986
15897
  }
15987
15898
  return str;
15988
15899
  };
15989
- var normalizePathToUnix = (path28) => toUnix(sp2.normalize(toUnix(path28)));
15990
- var normalizeIgnored = (cwd = "") => (path28) => {
15991
- if (typeof path28 === "string") {
15992
- return normalizePathToUnix(sp2.isAbsolute(path28) ? path28 : sp2.join(cwd, path28));
15900
+ var normalizePathToUnix = (path30) => toUnix(sp2.normalize(toUnix(path30)));
15901
+ var normalizeIgnored = (cwd = "") => (path30) => {
15902
+ if (typeof path30 === "string") {
15903
+ return normalizePathToUnix(sp2.isAbsolute(path30) ? path30 : sp2.join(cwd, path30));
15993
15904
  } else {
15994
- return path28;
15905
+ return path30;
15995
15906
  }
15996
15907
  };
15997
- var getAbsolutePath = (path28, cwd) => {
15998
- if (sp2.isAbsolute(path28)) {
15999
- return path28;
15908
+ var getAbsolutePath = (path30, cwd) => {
15909
+ if (sp2.isAbsolute(path30)) {
15910
+ return path30;
16000
15911
  }
16001
- return sp2.join(cwd, path28);
15912
+ return sp2.join(cwd, path30);
16002
15913
  };
16003
15914
  var EMPTY_SET = Object.freeze(/* @__PURE__ */ new Set());
16004
15915
  var DirEntry = class {
@@ -16063,10 +15974,10 @@ var WatchHelper = class {
16063
15974
  dirParts;
16064
15975
  followSymlinks;
16065
15976
  statMethod;
16066
- constructor(path28, follow, fsw) {
15977
+ constructor(path30, follow, fsw) {
16067
15978
  this.fsw = fsw;
16068
- const watchPath = path28;
16069
- this.path = path28 = path28.replace(REPLACER_RE, "");
15979
+ const watchPath = path30;
15980
+ this.path = path30 = path30.replace(REPLACER_RE, "");
16070
15981
  this.watchPath = watchPath;
16071
15982
  this.fullWatchPath = sp2.resolve(watchPath);
16072
15983
  this.dirParts = [];
@@ -16206,20 +16117,20 @@ var FSWatcher = class extends import_node_events.EventEmitter {
16206
16117
  this._closePromise = void 0;
16207
16118
  let paths = unifyPaths(paths_);
16208
16119
  if (cwd) {
16209
- paths = paths.map((path28) => {
16210
- const absPath = getAbsolutePath(path28, cwd);
16120
+ paths = paths.map((path30) => {
16121
+ const absPath = getAbsolutePath(path30, cwd);
16211
16122
  return absPath;
16212
16123
  });
16213
16124
  }
16214
- paths.forEach((path28) => {
16215
- this._removeIgnoredPath(path28);
16125
+ paths.forEach((path30) => {
16126
+ this._removeIgnoredPath(path30);
16216
16127
  });
16217
16128
  this._userIgnored = void 0;
16218
16129
  if (!this._readyCount)
16219
16130
  this._readyCount = 0;
16220
16131
  this._readyCount += paths.length;
16221
- Promise.all(paths.map(async (path28) => {
16222
- const res = await this._nodeFsHandler._addToNodeFs(path28, !_internal, void 0, 0, _origAdd);
16132
+ Promise.all(paths.map(async (path30) => {
16133
+ const res = await this._nodeFsHandler._addToNodeFs(path30, !_internal, void 0, 0, _origAdd);
16223
16134
  if (res)
16224
16135
  this._emitReady();
16225
16136
  return res;
@@ -16241,17 +16152,17 @@ var FSWatcher = class extends import_node_events.EventEmitter {
16241
16152
  return this;
16242
16153
  const paths = unifyPaths(paths_);
16243
16154
  const { cwd } = this.options;
16244
- paths.forEach((path28) => {
16245
- if (!sp2.isAbsolute(path28) && !this._closers.has(path28)) {
16155
+ paths.forEach((path30) => {
16156
+ if (!sp2.isAbsolute(path30) && !this._closers.has(path30)) {
16246
16157
  if (cwd)
16247
- path28 = sp2.join(cwd, path28);
16248
- path28 = sp2.resolve(path28);
16158
+ path30 = sp2.join(cwd, path30);
16159
+ path30 = sp2.resolve(path30);
16249
16160
  }
16250
- this._closePath(path28);
16251
- this._addIgnoredPath(path28);
16252
- if (this._watched.has(path28)) {
16161
+ this._closePath(path30);
16162
+ this._addIgnoredPath(path30);
16163
+ if (this._watched.has(path30)) {
16253
16164
  this._addIgnoredPath({
16254
- path: path28,
16165
+ path: path30,
16255
16166
  recursive: true
16256
16167
  });
16257
16168
  }
@@ -16315,38 +16226,38 @@ var FSWatcher = class extends import_node_events.EventEmitter {
16315
16226
  * @param stats arguments to be passed with event
16316
16227
  * @returns the error if defined, otherwise the value of the FSWatcher instance's `closed` flag
16317
16228
  */
16318
- async _emit(event, path28, stats) {
16229
+ async _emit(event, path30, stats) {
16319
16230
  if (this.closed)
16320
16231
  return;
16321
16232
  const opts = this.options;
16322
16233
  if (isWindows)
16323
- path28 = sp2.normalize(path28);
16234
+ path30 = sp2.normalize(path30);
16324
16235
  if (opts.cwd)
16325
- path28 = sp2.relative(opts.cwd, path28);
16326
- const args = [path28];
16236
+ path30 = sp2.relative(opts.cwd, path30);
16237
+ const args = [path30];
16327
16238
  if (stats != null)
16328
16239
  args.push(stats);
16329
16240
  const awf = opts.awaitWriteFinish;
16330
16241
  let pw;
16331
- if (awf && (pw = this._pendingWrites.get(path28))) {
16242
+ if (awf && (pw = this._pendingWrites.get(path30))) {
16332
16243
  pw.lastChange = /* @__PURE__ */ new Date();
16333
16244
  return this;
16334
16245
  }
16335
16246
  if (opts.atomic) {
16336
16247
  if (event === EVENTS.UNLINK) {
16337
- this._pendingUnlinks.set(path28, [event, ...args]);
16248
+ this._pendingUnlinks.set(path30, [event, ...args]);
16338
16249
  setTimeout(() => {
16339
- this._pendingUnlinks.forEach((entry, path29) => {
16250
+ this._pendingUnlinks.forEach((entry, path31) => {
16340
16251
  this.emit(...entry);
16341
16252
  this.emit(EVENTS.ALL, ...entry);
16342
- this._pendingUnlinks.delete(path29);
16253
+ this._pendingUnlinks.delete(path31);
16343
16254
  });
16344
16255
  }, typeof opts.atomic === "number" ? opts.atomic : 100);
16345
16256
  return this;
16346
16257
  }
16347
- if (event === EVENTS.ADD && this._pendingUnlinks.has(path28)) {
16258
+ if (event === EVENTS.ADD && this._pendingUnlinks.has(path30)) {
16348
16259
  event = EVENTS.CHANGE;
16349
- this._pendingUnlinks.delete(path28);
16260
+ this._pendingUnlinks.delete(path30);
16350
16261
  }
16351
16262
  }
16352
16263
  if (awf && (event === EVENTS.ADD || event === EVENTS.CHANGE) && this._readyEmitted) {
@@ -16364,16 +16275,16 @@ var FSWatcher = class extends import_node_events.EventEmitter {
16364
16275
  this.emitWithAll(event, args);
16365
16276
  }
16366
16277
  };
16367
- this._awaitWriteFinish(path28, awf.stabilityThreshold, event, awfEmit);
16278
+ this._awaitWriteFinish(path30, awf.stabilityThreshold, event, awfEmit);
16368
16279
  return this;
16369
16280
  }
16370
16281
  if (event === EVENTS.CHANGE) {
16371
- const isThrottled = !this._throttle(EVENTS.CHANGE, path28, 50);
16282
+ const isThrottled = !this._throttle(EVENTS.CHANGE, path30, 50);
16372
16283
  if (isThrottled)
16373
16284
  return this;
16374
16285
  }
16375
16286
  if (opts.alwaysStat && stats === void 0 && (event === EVENTS.ADD || event === EVENTS.ADD_DIR || event === EVENTS.CHANGE)) {
16376
- const fullPath = opts.cwd ? sp2.join(opts.cwd, path28) : path28;
16287
+ const fullPath = opts.cwd ? sp2.join(opts.cwd, path30) : path30;
16377
16288
  let stats2;
16378
16289
  try {
16379
16290
  stats2 = await (0, import_promises3.stat)(fullPath);
@@ -16404,23 +16315,23 @@ var FSWatcher = class extends import_node_events.EventEmitter {
16404
16315
  * @param timeout duration of time to suppress duplicate actions
16405
16316
  * @returns tracking object or false if action should be suppressed
16406
16317
  */
16407
- _throttle(actionType, path28, timeout) {
16318
+ _throttle(actionType, path30, timeout) {
16408
16319
  if (!this._throttled.has(actionType)) {
16409
16320
  this._throttled.set(actionType, /* @__PURE__ */ new Map());
16410
16321
  }
16411
16322
  const action = this._throttled.get(actionType);
16412
16323
  if (!action)
16413
16324
  throw new Error("invalid throttle");
16414
- const actionPath = action.get(path28);
16325
+ const actionPath = action.get(path30);
16415
16326
  if (actionPath) {
16416
16327
  actionPath.count++;
16417
16328
  return false;
16418
16329
  }
16419
16330
  let timeoutObject;
16420
16331
  const clear = () => {
16421
- const item = action.get(path28);
16332
+ const item = action.get(path30);
16422
16333
  const count = item ? item.count : 0;
16423
- action.delete(path28);
16334
+ action.delete(path30);
16424
16335
  clearTimeout(timeoutObject);
16425
16336
  if (item)
16426
16337
  clearTimeout(item.timeoutObject);
@@ -16428,7 +16339,7 @@ var FSWatcher = class extends import_node_events.EventEmitter {
16428
16339
  };
16429
16340
  timeoutObject = setTimeout(clear, timeout);
16430
16341
  const thr = { timeoutObject, clear, count: 0 };
16431
- action.set(path28, thr);
16342
+ action.set(path30, thr);
16432
16343
  return thr;
16433
16344
  }
16434
16345
  _incrReadyCount() {
@@ -16442,44 +16353,44 @@ var FSWatcher = class extends import_node_events.EventEmitter {
16442
16353
  * @param event
16443
16354
  * @param awfEmit Callback to be called when ready for event to be emitted.
16444
16355
  */
16445
- _awaitWriteFinish(path28, threshold, event, awfEmit) {
16356
+ _awaitWriteFinish(path30, threshold, event, awfEmit) {
16446
16357
  const awf = this.options.awaitWriteFinish;
16447
16358
  if (typeof awf !== "object")
16448
16359
  return;
16449
16360
  const pollInterval = awf.pollInterval;
16450
16361
  let timeoutHandler;
16451
- let fullPath = path28;
16452
- if (this.options.cwd && !sp2.isAbsolute(path28)) {
16453
- fullPath = sp2.join(this.options.cwd, path28);
16362
+ let fullPath = path30;
16363
+ if (this.options.cwd && !sp2.isAbsolute(path30)) {
16364
+ fullPath = sp2.join(this.options.cwd, path30);
16454
16365
  }
16455
16366
  const now2 = /* @__PURE__ */ new Date();
16456
16367
  const writes = this._pendingWrites;
16457
16368
  function awaitWriteFinishFn(prevStat) {
16458
16369
  (0, import_node_fs2.stat)(fullPath, (err, curStat) => {
16459
- if (err || !writes.has(path28)) {
16370
+ if (err || !writes.has(path30)) {
16460
16371
  if (err && err.code !== "ENOENT")
16461
16372
  awfEmit(err);
16462
16373
  return;
16463
16374
  }
16464
16375
  const now3 = Number(/* @__PURE__ */ new Date());
16465
16376
  if (prevStat && curStat.size !== prevStat.size) {
16466
- writes.get(path28).lastChange = now3;
16377
+ writes.get(path30).lastChange = now3;
16467
16378
  }
16468
- const pw = writes.get(path28);
16379
+ const pw = writes.get(path30);
16469
16380
  const df = now3 - pw.lastChange;
16470
16381
  if (df >= threshold) {
16471
- writes.delete(path28);
16382
+ writes.delete(path30);
16472
16383
  awfEmit(void 0, curStat);
16473
16384
  } else {
16474
16385
  timeoutHandler = setTimeout(awaitWriteFinishFn, pollInterval, curStat);
16475
16386
  }
16476
16387
  });
16477
16388
  }
16478
- if (!writes.has(path28)) {
16479
- writes.set(path28, {
16389
+ if (!writes.has(path30)) {
16390
+ writes.set(path30, {
16480
16391
  lastChange: now2,
16481
16392
  cancelWait: () => {
16482
- writes.delete(path28);
16393
+ writes.delete(path30);
16483
16394
  clearTimeout(timeoutHandler);
16484
16395
  return event;
16485
16396
  }
@@ -16490,8 +16401,8 @@ var FSWatcher = class extends import_node_events.EventEmitter {
16490
16401
  /**
16491
16402
  * Determines whether user has asked to ignore this path.
16492
16403
  */
16493
- _isIgnored(path28, stats) {
16494
- if (this.options.atomic && DOT_RE.test(path28))
16404
+ _isIgnored(path30, stats) {
16405
+ if (this.options.atomic && DOT_RE.test(path30))
16495
16406
  return true;
16496
16407
  if (!this._userIgnored) {
16497
16408
  const { cwd } = this.options;
@@ -16501,17 +16412,17 @@ var FSWatcher = class extends import_node_events.EventEmitter {
16501
16412
  const list = [...ignoredPaths.map(normalizeIgnored(cwd)), ...ignored];
16502
16413
  this._userIgnored = anymatch(list, void 0);
16503
16414
  }
16504
- return this._userIgnored(path28, stats);
16415
+ return this._userIgnored(path30, stats);
16505
16416
  }
16506
- _isntIgnored(path28, stat4) {
16507
- return !this._isIgnored(path28, stat4);
16417
+ _isntIgnored(path30, stat5) {
16418
+ return !this._isIgnored(path30, stat5);
16508
16419
  }
16509
16420
  /**
16510
16421
  * Provides a set of common helpers and properties relating to symlink handling.
16511
16422
  * @param path file or directory pattern being watched
16512
16423
  */
16513
- _getWatchHelpers(path28) {
16514
- return new WatchHelper(path28, this.options.followSymlinks, this);
16424
+ _getWatchHelpers(path30) {
16425
+ return new WatchHelper(path30, this.options.followSymlinks, this);
16515
16426
  }
16516
16427
  // Directory helpers
16517
16428
  // -----------------
@@ -16543,63 +16454,63 @@ var FSWatcher = class extends import_node_events.EventEmitter {
16543
16454
  * @param item base path of item/directory
16544
16455
  */
16545
16456
  _remove(directory, item, isDirectory) {
16546
- const path28 = sp2.join(directory, item);
16547
- const fullPath = sp2.resolve(path28);
16548
- isDirectory = isDirectory != null ? isDirectory : this._watched.has(path28) || this._watched.has(fullPath);
16549
- if (!this._throttle("remove", path28, 100))
16457
+ const path30 = sp2.join(directory, item);
16458
+ const fullPath = sp2.resolve(path30);
16459
+ isDirectory = isDirectory != null ? isDirectory : this._watched.has(path30) || this._watched.has(fullPath);
16460
+ if (!this._throttle("remove", path30, 100))
16550
16461
  return;
16551
16462
  if (!isDirectory && this._watched.size === 1) {
16552
16463
  this.add(directory, item, true);
16553
16464
  }
16554
- const wp = this._getWatchedDir(path28);
16465
+ const wp = this._getWatchedDir(path30);
16555
16466
  const nestedDirectoryChildren = wp.getChildren();
16556
- nestedDirectoryChildren.forEach((nested) => this._remove(path28, nested));
16467
+ nestedDirectoryChildren.forEach((nested) => this._remove(path30, nested));
16557
16468
  const parent = this._getWatchedDir(directory);
16558
16469
  const wasTracked = parent.has(item);
16559
16470
  parent.remove(item);
16560
16471
  if (this._symlinkPaths.has(fullPath)) {
16561
16472
  this._symlinkPaths.delete(fullPath);
16562
16473
  }
16563
- let relPath = path28;
16474
+ let relPath = path30;
16564
16475
  if (this.options.cwd)
16565
- relPath = sp2.relative(this.options.cwd, path28);
16476
+ relPath = sp2.relative(this.options.cwd, path30);
16566
16477
  if (this.options.awaitWriteFinish && this._pendingWrites.has(relPath)) {
16567
16478
  const event = this._pendingWrites.get(relPath).cancelWait();
16568
16479
  if (event === EVENTS.ADD)
16569
16480
  return;
16570
16481
  }
16571
- this._watched.delete(path28);
16482
+ this._watched.delete(path30);
16572
16483
  this._watched.delete(fullPath);
16573
16484
  const eventName = isDirectory ? EVENTS.UNLINK_DIR : EVENTS.UNLINK;
16574
- if (wasTracked && !this._isIgnored(path28))
16575
- this._emit(eventName, path28);
16576
- this._closePath(path28);
16485
+ if (wasTracked && !this._isIgnored(path30))
16486
+ this._emit(eventName, path30);
16487
+ this._closePath(path30);
16577
16488
  }
16578
16489
  /**
16579
16490
  * Closes all watchers for a path
16580
16491
  */
16581
- _closePath(path28) {
16582
- this._closeFile(path28);
16583
- const dir = sp2.dirname(path28);
16584
- this._getWatchedDir(dir).remove(sp2.basename(path28));
16492
+ _closePath(path30) {
16493
+ this._closeFile(path30);
16494
+ const dir = sp2.dirname(path30);
16495
+ this._getWatchedDir(dir).remove(sp2.basename(path30));
16585
16496
  }
16586
16497
  /**
16587
16498
  * Closes only file-specific watchers
16588
16499
  */
16589
- _closeFile(path28) {
16590
- const closers = this._closers.get(path28);
16500
+ _closeFile(path30) {
16501
+ const closers = this._closers.get(path30);
16591
16502
  if (!closers)
16592
16503
  return;
16593
16504
  closers.forEach((closer) => closer());
16594
- this._closers.delete(path28);
16505
+ this._closers.delete(path30);
16595
16506
  }
16596
- _addPathCloser(path28, closer) {
16507
+ _addPathCloser(path30, closer) {
16597
16508
  if (!closer)
16598
16509
  return;
16599
- let list = this._closers.get(path28);
16510
+ let list = this._closers.get(path30);
16600
16511
  if (!list) {
16601
16512
  list = [];
16602
- this._closers.set(path28, list);
16513
+ this._closers.set(path30, list);
16603
16514
  }
16604
16515
  list.push(closer);
16605
16516
  }
@@ -16629,12 +16540,291 @@ function watch(paths, options = {}) {
16629
16540
  var chokidar_default = { watch, FSWatcher };
16630
16541
 
16631
16542
  // src/watcher/file-watcher.ts
16543
+ var path23 = __toESM(require("path"), 1);
16544
+
16545
+ // src/watcher/native-recursive-watcher.ts
16546
+ var import_node_fs3 = require("fs");
16632
16547
  var path21 = __toESM(require("path"), 1);
16548
+ var NativeRecursiveWatcher = class {
16549
+ constructor(root, onChange, options = {}) {
16550
+ this.root = root;
16551
+ this.onChange = onChange;
16552
+ this.watchFactory = options.watchFactory ?? this.defaultWatchFactory;
16553
+ this.onError = options.onError;
16554
+ }
16555
+ root;
16556
+ onChange;
16557
+ watcher = null;
16558
+ listenerToken = 0;
16559
+ watchFactory;
16560
+ onError;
16561
+ start() {
16562
+ if (this.watcher) return;
16563
+ const token = ++this.listenerToken;
16564
+ const listener = (_eventType, filename) => {
16565
+ if (this.watcher === null || this.listenerToken !== token) return;
16566
+ const absolutePath = this.toAbsolutePath(filename);
16567
+ const nextResult = this.onChange(absolutePath);
16568
+ if (nextResult instanceof Promise) {
16569
+ void nextResult.catch((error) => {
16570
+ console.error("[codebase-index] Error handling native watcher event:", error);
16571
+ });
16572
+ }
16573
+ };
16574
+ const watcher = this.watchFactory(this.root, listener, {
16575
+ persistent: true,
16576
+ recursive: true
16577
+ });
16578
+ watcher.on?.("error", (error) => {
16579
+ if (this.watcher === watcher && this.listenerToken === token) {
16580
+ this.onError?.(error);
16581
+ }
16582
+ });
16583
+ this.watcher = watcher;
16584
+ }
16585
+ async stop() {
16586
+ const watcher = this.watcher;
16587
+ this.watcher = null;
16588
+ this.listenerToken += 1;
16589
+ if (!watcher) return;
16590
+ await watcher.close();
16591
+ }
16592
+ toAbsolutePath(filename) {
16593
+ if (filename == null) return null;
16594
+ const normalizedFilename = typeof filename === "string" ? filename : filename.toString();
16595
+ const absolutePath = path21.resolve(this.root, normalizedFilename);
16596
+ const relativePath = path21.relative(this.root, absolutePath);
16597
+ const outsideRoot = relativePath === ".." || relativePath.startsWith(`..${path21.sep}`) || path21.isAbsolute(relativePath);
16598
+ return outsideRoot ? null : absolutePath;
16599
+ }
16600
+ defaultWatchFactory = (root, listener, options) => (0, import_node_fs3.watch)(root, options, listener);
16601
+ };
16602
+
16603
+ // src/watcher/snapshot.ts
16604
+ var fsPromises4 = __toESM(require("fs/promises"), 1);
16605
+ var path22 = __toESM(require("path"), 1);
16606
+ async function buildFileSnapshotScan(projectRoot, config, configPaths = []) {
16607
+ const normalizedProjectRoot = path22.resolve(projectRoot);
16608
+ const ignoreFilter = createIgnoreFilter(normalizedProjectRoot);
16609
+ const includePatterns = [...config.include, ...config.additionalInclude ?? []];
16610
+ const maxDepth = config.indexing?.maxDepth ?? -1;
16611
+ const snapshot = /* @__PURE__ */ new Map();
16612
+ const unreadablePrefixes = /* @__PURE__ */ new Set();
16613
+ const includeFile = async (filePath) => {
16614
+ const normalizedPath2 = path22.resolve(filePath);
16615
+ if (!shouldIncludeFile(normalizedPath2, normalizedProjectRoot, includePatterns, config.exclude, ignoreFilter)) return;
16616
+ const stat5 = await readStatIfFile(normalizedPath2, unreadablePrefixes);
16617
+ if (stat5) snapshot.set(normalizedPath2, { size: stat5.size, mtimeMs: stat5.mtimeMs });
16618
+ };
16619
+ const walk = async (directoryPath, depth) => {
16620
+ let entries;
16621
+ try {
16622
+ entries = await fsPromises4.readdir(directoryPath, { withFileTypes: true });
16623
+ } catch (error) {
16624
+ if (isMissingFsError(error)) return;
16625
+ if (isPermissionFsError(error)) {
16626
+ unreadablePrefixes.add(path22.resolve(directoryPath));
16627
+ return;
16628
+ }
16629
+ throw error;
16630
+ }
16631
+ for (const entry of entries) {
16632
+ const fullPath = path22.join(directoryPath, entry.name);
16633
+ const relativePath = path22.relative(normalizedProjectRoot, fullPath);
16634
+ if (entry.isDirectory()) {
16635
+ if (hasFilteredPathSegment(relativePath, path22.sep) || isRestrictedDirectory(relativePath, path22.sep)) continue;
16636
+ if (ignoreFilter.ignores(relativePath)) continue;
16637
+ if (maxDepth === -1 || depth < maxDepth) await walk(fullPath, depth + 1);
16638
+ } else if (entry.isFile()) {
16639
+ await includeFile(fullPath);
16640
+ }
16641
+ }
16642
+ };
16643
+ await walk(normalizedProjectRoot, 0);
16644
+ await includeExplicitConfigPaths(snapshot, unreadablePrefixes, configPaths);
16645
+ return { entries: snapshot, unreadablePrefixes };
16646
+ }
16647
+ async function buildFileSnapshotForPathScan(projectRoot, config, configPaths, targetPath) {
16648
+ const normalizedProjectRoot = path22.resolve(projectRoot);
16649
+ const normalizedTargetPath = path22.resolve(targetPath);
16650
+ if (!isWithinPath(normalizedProjectRoot, normalizedTargetPath)) {
16651
+ return { entries: /* @__PURE__ */ new Map(), unreadablePrefixes: /* @__PURE__ */ new Set() };
16652
+ }
16653
+ const ignoreFilter = createIgnoreFilter(normalizedProjectRoot);
16654
+ const includePatterns = [...config.include, ...config.additionalInclude ?? []];
16655
+ const maxDepth = config.indexing?.maxDepth ?? -1;
16656
+ const explicitConfigPaths = new Set(configPaths.map((configPath) => path22.resolve(configPath)));
16657
+ const snapshot = /* @__PURE__ */ new Map();
16658
+ const unreadablePrefixes = /* @__PURE__ */ new Set();
16659
+ const includeFile = async (filePath) => {
16660
+ const normalizedPath2 = path22.resolve(filePath);
16661
+ if (!explicitConfigPaths.has(normalizedPath2) && !shouldIncludeFile(
16662
+ normalizedPath2,
16663
+ normalizedProjectRoot,
16664
+ includePatterns,
16665
+ config.exclude,
16666
+ ignoreFilter
16667
+ )) return;
16668
+ const stat5 = await readStatIfFile(normalizedPath2, unreadablePrefixes);
16669
+ if (stat5) snapshot.set(normalizedPath2, { size: stat5.size, mtimeMs: stat5.mtimeMs });
16670
+ };
16671
+ const walk = async (directoryPath, depth) => {
16672
+ let entries;
16673
+ try {
16674
+ entries = await fsPromises4.readdir(directoryPath, { withFileTypes: true });
16675
+ } catch (error) {
16676
+ if (isMissingFsError(error)) return;
16677
+ if (isPermissionFsError(error)) {
16678
+ unreadablePrefixes.add(path22.resolve(directoryPath));
16679
+ return;
16680
+ }
16681
+ throw error;
16682
+ }
16683
+ for (const entry of entries) {
16684
+ const fullPath = path22.join(directoryPath, entry.name);
16685
+ const relativePath = path22.relative(normalizedProjectRoot, fullPath);
16686
+ if (entry.isDirectory()) {
16687
+ if (hasFilteredPathSegment(relativePath, path22.sep) || isRestrictedDirectory(relativePath, path22.sep)) continue;
16688
+ if (ignoreFilter.ignores(relativePath)) continue;
16689
+ if (maxDepth === -1 || depth < maxDepth) await walk(fullPath, depth + 1);
16690
+ } else if (entry.isFile()) {
16691
+ await includeFile(fullPath);
16692
+ }
16693
+ }
16694
+ };
16695
+ const targetStat = await readStatIfFile(normalizedTargetPath, unreadablePrefixes);
16696
+ if (targetStat) await includeFile(normalizedTargetPath);
16697
+ else await walk(normalizedTargetPath, 0);
16698
+ await includeExplicitConfigPathsInPath(snapshot, unreadablePrefixes, configPaths, normalizedTargetPath);
16699
+ return { entries: snapshot, unreadablePrefixes };
16700
+ }
16701
+ function completeFileSnapshot(previous, scan) {
16702
+ const completed = new Map(scan.entries);
16703
+ for (const unreadablePrefix of scan.unreadablePrefixes) {
16704
+ for (const [entryPath, entry] of previous) {
16705
+ if (isWithinPath(unreadablePrefix, entryPath) && !completed.has(entryPath)) completed.set(entryPath, entry);
16706
+ }
16707
+ }
16708
+ return completed;
16709
+ }
16710
+ async function includeExplicitConfigPaths(snapshot, unreadablePrefixes, configPaths) {
16711
+ for (const configPath of [...new Set(configPaths.map((value) => path22.resolve(value)))]) {
16712
+ if (snapshot.has(configPath)) continue;
16713
+ const stat5 = await readStatIfFile(configPath, unreadablePrefixes);
16714
+ if (stat5) snapshot.set(configPath, { size: stat5.size, mtimeMs: stat5.mtimeMs });
16715
+ }
16716
+ }
16717
+ async function includeExplicitConfigPathsInPath(snapshot, unreadablePrefixes, configPaths, targetPath) {
16718
+ await includeExplicitConfigPaths(
16719
+ snapshot,
16720
+ unreadablePrefixes,
16721
+ configPaths.filter((configPath) => isWithinPath(targetPath, path22.resolve(configPath)))
16722
+ );
16723
+ }
16724
+ function isWithinPath(parentPath, childPath) {
16725
+ const relativePath = path22.relative(parentPath, childPath);
16726
+ return relativePath === "" || !relativePath.startsWith(`..${path22.sep}`) && relativePath !== ".." && !path22.isAbsolute(relativePath);
16727
+ }
16728
+ async function readStatIfFile(filePath, unreadablePrefixes) {
16729
+ try {
16730
+ const stat5 = await fsPromises4.stat(filePath);
16731
+ return stat5.isFile() ? stat5 : null;
16732
+ } catch (error) {
16733
+ if (isMissingFsError(error)) return null;
16734
+ if (isPermissionFsError(error)) {
16735
+ unreadablePrefixes.add(path22.resolve(filePath));
16736
+ return null;
16737
+ }
16738
+ throw error;
16739
+ }
16740
+ }
16741
+ function isMissingFsError(error) {
16742
+ return error instanceof Error && ["ENOENT", "ENOTDIR"].includes(error.code ?? "");
16743
+ }
16744
+ function isPermissionFsError(error) {
16745
+ return error instanceof Error && ["EACCES", "EPERM"].includes(error.code ?? "");
16746
+ }
16747
+ var diffTypeOrder = { add: 0, change: 1, unlink: 2 };
16748
+ function diffFileSnapshots(previous, current, forcedChanges = /* @__PURE__ */ new Set()) {
16749
+ const changes = [];
16750
+ for (const [filePath, previousEntry] of previous) {
16751
+ const currentEntry = current.get(filePath);
16752
+ if (!currentEntry) changes.push({ type: "unlink", path: filePath });
16753
+ else if (forcedChanges.has(filePath) || currentEntry.size !== previousEntry.size || currentEntry.mtimeMs !== previousEntry.mtimeMs) {
16754
+ changes.push({ type: "change", path: filePath });
16755
+ }
16756
+ }
16757
+ for (const [filePath] of current) {
16758
+ if (!previous.has(filePath)) changes.push({ type: "add", path: filePath });
16759
+ }
16760
+ return changes.sort((left, right) => left.path.localeCompare(right.path) || diffTypeOrder[left.type] - diffTypeOrder[right.type]);
16761
+ }
16762
+
16763
+ // src/watcher/snapshot-reconciler.ts
16764
+ var FileSnapshotReconciler = class {
16765
+ constructor(projectRoot, config, configPaths) {
16766
+ this.projectRoot = projectRoot;
16767
+ this.config = config;
16768
+ this.configPaths = configPaths;
16769
+ }
16770
+ projectRoot;
16771
+ config;
16772
+ configPaths;
16773
+ snapshot = null;
16774
+ reconciliationTail = Promise.resolve();
16775
+ async initialize() {
16776
+ this.snapshot = (await buildFileSnapshotScan(this.projectRoot, this.config, this.configPaths)).entries;
16777
+ }
16778
+ async reconcile(invalidations = []) {
16779
+ if (this.snapshot === null) {
16780
+ throw new Error("FileSnapshotReconciler is not initialized. Call initialize() before reconcile().");
16781
+ }
16782
+ const reconciliation = this.reconciliationTail.then(async () => {
16783
+ const previousSnapshot = this.snapshot;
16784
+ if (previousSnapshot === null) {
16785
+ throw new Error("FileSnapshotReconciler is not initialized. Call initialize() before reconcile().");
16786
+ }
16787
+ const normalizedInvalidations = invalidations.map((invalidation) => typeof invalidation === "string" || invalidation === null ? { path: invalidation, forceChange: false } : { path: invalidation.path, forceChange: invalidation.forceChange === true });
16788
+ const scopedPaths = normalizedInvalidations.map((invalidation) => invalidation.path).filter((filePath) => filePath !== null);
16789
+ const scan = scopedPaths.length === 0 || scopedPaths.length !== normalizedInvalidations.length ? await buildFileSnapshotScan(this.projectRoot, this.config, this.configPaths) : await this.reconcilePaths(previousSnapshot, scopedPaths);
16790
+ const nextSnapshot = completeFileSnapshot(previousSnapshot, scan);
16791
+ const forcedChanges = new Set(normalizedInvalidations.filter((invalidation) => invalidation.path !== null && invalidation.forceChange).map((invalidation) => invalidation.path));
16792
+ const changes = diffFileSnapshots(previousSnapshot, nextSnapshot, forcedChanges);
16793
+ this.snapshot = nextSnapshot;
16794
+ return changes;
16795
+ });
16796
+ this.reconciliationTail = reconciliation.then(() => void 0, () => void 0);
16797
+ return reconciliation;
16798
+ }
16799
+ async reconcilePaths(previousSnapshot, invalidatedPaths) {
16800
+ const scopes = this.getScopes(invalidatedPaths);
16801
+ const entries = new Map(previousSnapshot);
16802
+ const unreadablePrefixes = /* @__PURE__ */ new Set();
16803
+ for (const scope of scopes) {
16804
+ for (const previousPath of entries.keys()) {
16805
+ if (isWithinPath(scope, previousPath)) entries.delete(previousPath);
16806
+ }
16807
+ const scopedScan = await buildFileSnapshotForPathScan(this.projectRoot, this.config, this.configPaths, scope);
16808
+ for (const [filePath, entry] of scopedScan.entries) entries.set(filePath, entry);
16809
+ for (const unreadablePrefix of scopedScan.unreadablePrefixes) unreadablePrefixes.add(unreadablePrefix);
16810
+ }
16811
+ return { entries, unreadablePrefixes };
16812
+ }
16813
+ getScopes(invalidatedPaths) {
16814
+ const uniquePaths = [...new Set(invalidatedPaths)].sort((left, right) => left.length - right.length);
16815
+ return uniquePaths.filter((candidate, index) => !uniquePaths.slice(0, index).some(
16816
+ (ancestor) => isWithinPath(ancestor, candidate)
16817
+ ));
16818
+ }
16819
+ };
16820
+
16821
+ // src/watcher/file-watcher.ts
16633
16822
  var FileWatcher = class {
16634
16823
  watcher = null;
16635
16824
  projectRoot;
16636
16825
  config;
16637
16826
  configPath;
16827
+ backend;
16638
16828
  projectConfigPaths;
16639
16829
  pendingChanges = /* @__PURE__ */ new Map();
16640
16830
  debounceTimer = null;
@@ -16644,44 +16834,74 @@ var FileWatcher = class {
16644
16834
  resolveReady = null;
16645
16835
  pollingFallbackAttempted = false;
16646
16836
  pendingClose = null;
16837
+ startupReadySignals = 1;
16838
+ nativeWatcher = null;
16839
+ nativeReconciler = null;
16840
+ nativeSetupGeneration = 0;
16841
+ nativeStarting = false;
16842
+ nativeInitializing = false;
16843
+ nativeReconcileTimer = null;
16844
+ nativeInvalidatedPaths = /* @__PURE__ */ new Map();
16845
+ configPathStates = /* @__PURE__ */ new Map();
16647
16846
  constructor(projectRoot, config, host, options = {}) {
16648
16847
  this.projectRoot = projectRoot;
16649
16848
  this.config = config;
16849
+ this.backend = options.backend ?? "auto";
16650
16850
  this.configPath = options.configPath;
16651
16851
  this.projectConfigPaths = options.configPath ? [options.configPath] : getProjectConfigCandidatePaths(projectRoot, host);
16652
16852
  }
16653
16853
  start(handler) {
16654
- if (this.watcher) {
16854
+ if (this.watcher || this.nativeWatcher || this.nativeStarting) {
16655
16855
  return;
16656
16856
  }
16657
16857
  this.onChanges = handler;
16658
16858
  this.pollingFallbackAttempted = false;
16659
16859
  this.resetReady();
16860
+ if (this.shouldUseNativeWatcher()) {
16861
+ if (this.hasExternalConfigWatchTarget()) {
16862
+ this.setStartupReadySignals(2);
16863
+ this.startExternalConfigWatcher();
16864
+ }
16865
+ this.nativeStarting = true;
16866
+ void this.createNativeWatcher();
16867
+ return;
16868
+ }
16660
16869
  this.createWatcher();
16661
16870
  }
16662
16871
  resetReady() {
16663
- this.readyPromise = new Promise((resolve15) => {
16664
- this.resolveReady = resolve15;
16872
+ this.readyPromise = new Promise((resolve17) => {
16873
+ this.resolveReady = resolve17;
16665
16874
  });
16875
+ this.startupReadySignals = 1;
16666
16876
  }
16667
- createWatcher(usePolling = false) {
16668
- const ignoreFilter = createIgnoreFilter(this.projectRoot);
16669
- let watchTargets = this.projectRoot;
16670
- if (this.configPath) {
16671
- watchTargets = [this.projectRoot, this.configPath];
16672
- } else {
16673
- const externalConfigTargets = this.projectConfigPaths.filter((projectConfigPath) => {
16674
- const relativeConfigPath = path21.relative(this.projectRoot, projectConfigPath);
16675
- return this.isOutsideProjectPath(relativeConfigPath);
16676
- }).map((projectConfigPath) => (0, import_fs14.existsSync)(projectConfigPath) ? projectConfigPath : this.getNearestExistingDirectory(path21.dirname(projectConfigPath)));
16677
- const uniqueExternalConfigTargets = [...new Set(externalConfigTargets)];
16678
- if (uniqueExternalConfigTargets.length > 0) {
16679
- watchTargets = [this.projectRoot, ...uniqueExternalConfigTargets];
16680
- }
16877
+ setStartupReadySignals(expectedSignals) {
16878
+ if (!this.readyPromise) {
16879
+ return;
16880
+ }
16881
+ this.startupReadySignals = Math.max(0, expectedSignals);
16882
+ }
16883
+ reportStartupReadySignal() {
16884
+ if (!this.readyPromise || !this.resolveReady) {
16885
+ return;
16681
16886
  }
16887
+ if (this.startupReadySignals <= 0) {
16888
+ return;
16889
+ }
16890
+ this.startupReadySignals -= 1;
16891
+ if (this.startupReadySignals !== 0) {
16892
+ return;
16893
+ }
16894
+ this.resolveReady();
16895
+ this.resolveReady = null;
16896
+ }
16897
+ createWatcher(watchTargets, usePolling = false, reportsStartupReady = true) {
16898
+ let reportedStartupReady = false;
16899
+ this.configPathStates = this.getConfigPathStates();
16900
+ const ignoreFilter = createIgnoreFilter(this.projectRoot);
16901
+ const resolvedWatchTargets = watchTargets ?? this.getFullChokidarWatchTargets();
16682
16902
  const watcherOptions = {
16683
16903
  ignored: (filePath) => {
16684
- const relativePath = path21.relative(this.projectRoot, filePath);
16904
+ const relativePath = path23.relative(this.projectRoot, filePath);
16685
16905
  if (!relativePath) return false;
16686
16906
  if (this.isProjectConfigPathOrAncestor(relativePath)) {
16687
16907
  return false;
@@ -16689,10 +16909,10 @@ var FileWatcher = class {
16689
16909
  if (this.isOutsideProjectPath(relativePath)) {
16690
16910
  return true;
16691
16911
  }
16692
- if (hasFilteredPathSegment(relativePath, path21.sep)) {
16912
+ if (hasFilteredPathSegment(relativePath, path23.sep)) {
16693
16913
  return true;
16694
16914
  }
16695
- if (isRestrictedDirectory(relativePath, path21.sep)) {
16915
+ if (isRestrictedDirectory(relativePath, path23.sep)) {
16696
16916
  return true;
16697
16917
  }
16698
16918
  if (ignoreFilter.ignores(relativePath)) {
@@ -16725,10 +16945,13 @@ var FileWatcher = class {
16725
16945
  watcher = new FSWatcher(watcherOptions);
16726
16946
  }
16727
16947
  this.watcher = watcher;
16728
- watcher.once("ready", () => {
16948
+ watcher.on("ready", () => {
16729
16949
  if (this.watcher !== watcher) return;
16730
- this.resolveReady?.();
16731
- this.resolveReady = null;
16950
+ this.reconcileConfigPathStates();
16951
+ if (reportsStartupReady) {
16952
+ this.reportStartupReadySignal();
16953
+ reportedStartupReady = true;
16954
+ }
16732
16955
  });
16733
16956
  watcher.on("error", (error) => {
16734
16957
  const err = error instanceof Error ? error : null;
@@ -16742,10 +16965,13 @@ var FileWatcher = class {
16742
16965
  console.error("[codebase-index] Failed to close exhausted file watcher:", closeError);
16743
16966
  });
16744
16967
  if (this.onChanges) {
16968
+ const replacementReportsStartupReady = reportsStartupReady || reportedStartupReady;
16745
16969
  if (!this.resolveReady) {
16746
16970
  this.resetReady();
16971
+ } else if (reportedStartupReady) {
16972
+ this.startupReadySignals += 1;
16747
16973
  }
16748
- this.createWatcher(true);
16974
+ this.createWatcher(resolvedWatchTargets, true, replacementReportsStartupReady);
16749
16975
  } else {
16750
16976
  this.watcher = null;
16751
16977
  }
@@ -16756,13 +16982,166 @@ var FileWatcher = class {
16756
16982
  watcher.on("add", (filePath) => this.handleChange(watcher, "add", filePath));
16757
16983
  watcher.on("change", (filePath) => this.handleChange(watcher, "change", filePath));
16758
16984
  watcher.on("unlink", (filePath) => this.handleChange(watcher, "unlink", filePath));
16759
- watcher.add(watchTargets);
16985
+ watcher.add(resolvedWatchTargets);
16986
+ }
16987
+ shouldUseNativeWatcher() {
16988
+ if (this.backend === "chokidar") {
16989
+ return false;
16990
+ }
16991
+ return true;
16992
+ }
16993
+ getFullChokidarWatchTargets() {
16994
+ if (this.configPath) {
16995
+ return [this.projectRoot, this.configPath];
16996
+ }
16997
+ const externalConfigTargets = this.getExternalConfigWatchTargets();
16998
+ if (externalConfigTargets.length === 0) {
16999
+ return this.projectRoot;
17000
+ }
17001
+ return [this.projectRoot, ...externalConfigTargets];
17002
+ }
17003
+ getExternalConfigWatchTargets() {
17004
+ return [...new Set(
17005
+ this.projectConfigPaths.filter((projectConfigPath) => {
17006
+ const relativeConfigPath = path23.relative(this.projectRoot, projectConfigPath);
17007
+ return this.isOutsideProjectPath(relativeConfigPath);
17008
+ }).map((projectConfigPath) => {
17009
+ if ((0, import_fs14.existsSync)(projectConfigPath)) {
17010
+ return projectConfigPath;
17011
+ }
17012
+ return this.getNearestExistingDirectory(path23.dirname(projectConfigPath));
17013
+ })
17014
+ )];
17015
+ }
17016
+ hasExternalConfigWatchTarget() {
17017
+ return this.getExternalConfigWatchTargets().length > 0;
17018
+ }
17019
+ startExternalConfigWatcher(usePolling = false) {
17020
+ const externalTargets = this.getExternalConfigWatchTargets();
17021
+ if (externalTargets.length === 0) {
17022
+ return;
17023
+ }
17024
+ this.createWatcher(externalTargets, usePolling);
17025
+ }
17026
+ async createNativeWatcher() {
17027
+ const generation = ++this.nativeSetupGeneration;
17028
+ const reconciler = new FileSnapshotReconciler(this.projectRoot, this.config, this.projectConfigPaths);
17029
+ const watcher = new NativeRecursiveWatcher(
17030
+ this.projectRoot,
17031
+ (filePath) => this.scheduleNativeReconciliation(generation, filePath),
17032
+ { onError: (error) => void this.fallbackFromNativeWatcher(generation, error) }
17033
+ );
17034
+ this.nativeReconciler = reconciler;
17035
+ this.nativeWatcher = watcher;
17036
+ this.nativeInitializing = true;
17037
+ try {
17038
+ watcher.start();
17039
+ if (!this.isCurrentNativeSetup(generation)) {
17040
+ await watcher.stop();
17041
+ return;
17042
+ }
17043
+ await reconciler.initialize();
17044
+ if (!this.isCurrentNativeSetup(generation) || this.nativeWatcher !== watcher) {
17045
+ await watcher.stop();
17046
+ return;
17047
+ }
17048
+ this.nativeStarting = false;
17049
+ this.nativeInitializing = false;
17050
+ await this.reconcileNativeWatcherWithPendingInvalidations(generation);
17051
+ this.reportStartupReadySignal();
17052
+ } catch (error) {
17053
+ if (!this.isCurrentNativeSetup(generation)) return;
17054
+ this.nativeInitializing = false;
17055
+ if (this.nativeWatcher) {
17056
+ await this.fallbackFromNativeWatcher(generation, error);
17057
+ return;
17058
+ }
17059
+ this.nativeStarting = false;
17060
+ const externalWatcher = this.watcher;
17061
+ this.watcher = null;
17062
+ this.nativeReconciler = null;
17063
+ await externalWatcher?.close();
17064
+ console.warn("[codebase-index] Native recursive watcher unavailable; using Chokidar fallback.", error);
17065
+ this.setStartupReadySignals(1);
17066
+ this.createWatcher();
17067
+ }
17068
+ }
17069
+ isCurrentNativeSetup(generation) {
17070
+ return this.nativeSetupGeneration === generation && this.onChanges !== null;
17071
+ }
17072
+ scheduleNativeReconciliation(generation, filePath) {
17073
+ if (!this.isCurrentNativeSetup(generation)) return;
17074
+ const requiresFullReconciliation = filePath === path23.join(this.projectRoot, ".gitignore");
17075
+ const invalidatedPath = requiresFullReconciliation ? null : filePath;
17076
+ this.nativeInvalidatedPaths.set(invalidatedPath, invalidatedPath !== null);
17077
+ if (this.nativeReconcileTimer) {
17078
+ clearTimeout(this.nativeReconcileTimer);
17079
+ }
17080
+ this.nativeReconcileTimer = setTimeout(() => {
17081
+ this.nativeReconcileTimer = null;
17082
+ void this.reconcileNativeWatcherFromQueue(generation);
17083
+ }, 100);
17084
+ }
17085
+ reconcileNativeWatcherFromQueue(generation) {
17086
+ if (!this.isCurrentNativeSetup(generation) || this.nativeInitializing) return;
17087
+ const invalidatedPaths = this.popNativeInvalidations();
17088
+ if (invalidatedPaths.length === 0) return;
17089
+ void this.reconcileNativeWatcher(generation, invalidatedPaths);
17090
+ }
17091
+ async reconcileNativeWatcher(generation, invalidatedPaths) {
17092
+ if (!this.isCurrentNativeSetup(generation) || !this.nativeReconciler) return;
17093
+ try {
17094
+ const reconciler = this.nativeReconciler;
17095
+ const changes = await reconciler.reconcile(invalidatedPaths);
17096
+ if (!this.isCurrentNativeSetup(generation) || this.nativeReconciler !== reconciler) return;
17097
+ this.recordChanges(changes);
17098
+ } catch (error) {
17099
+ await this.fallbackFromNativeWatcher(generation, error);
17100
+ }
17101
+ }
17102
+ async reconcileNativeWatcherWithPendingInvalidations(generation) {
17103
+ const invalidatedPaths = this.popNativeInvalidations();
17104
+ if (invalidatedPaths.length === 0) return;
17105
+ await this.reconcileNativeWatcher(generation, invalidatedPaths);
17106
+ }
17107
+ popNativeInvalidations() {
17108
+ if (this.nativeInvalidatedPaths.size === 0) return [];
17109
+ const invalidations = [...this.nativeInvalidatedPaths].map(([invalidatedPath, forceChange]) => ({
17110
+ path: invalidatedPath,
17111
+ forceChange
17112
+ }));
17113
+ this.nativeInvalidatedPaths.clear();
17114
+ return invalidations;
17115
+ }
17116
+ async fallbackFromNativeWatcher(generation, error) {
17117
+ if (!this.isCurrentNativeSetup(generation)) return;
17118
+ const watcher = this.nativeWatcher;
17119
+ const externalWatcher = this.watcher;
17120
+ this.nativeWatcher = null;
17121
+ this.watcher = null;
17122
+ this.nativeReconciler = null;
17123
+ this.nativeStarting = false;
17124
+ this.nativeInitializing = false;
17125
+ this.nativeSetupGeneration += 1;
17126
+ if (this.nativeReconcileTimer) {
17127
+ clearTimeout(this.nativeReconcileTimer);
17128
+ this.nativeReconcileTimer = null;
17129
+ }
17130
+ this.nativeInvalidatedPaths.clear();
17131
+ this.setStartupReadySignals(1);
17132
+ console.warn("[codebase-index] Native recursive watcher failed; using Chokidar fallback.", error);
17133
+ await watcher?.stop();
17134
+ await externalWatcher?.close();
17135
+ if (this.onChanges) {
17136
+ this.createWatcher();
17137
+ }
16760
17138
  }
16761
17139
  handleChange(watcher, type, filePath) {
16762
17140
  if (this.watcher !== watcher) {
16763
17141
  return;
16764
17142
  }
16765
17143
  if (this.isProjectConfigPath(filePath)) {
17144
+ this.updateConfigPathState(filePath);
16766
17145
  this.pendingChanges.set(filePath, type);
16767
17146
  this.scheduleFlush();
16768
17147
  return;
@@ -16777,27 +17156,33 @@ var FileWatcher = class {
16777
17156
  )) {
16778
17157
  return;
16779
17158
  }
16780
- this.pendingChanges.set(filePath, type);
17159
+ this.recordChanges([{ path: filePath, type }]);
17160
+ }
17161
+ recordChanges(changes) {
17162
+ if (changes.length === 0) return;
17163
+ for (const change of changes) {
17164
+ this.pendingChanges.set(change.path, change.type);
17165
+ }
16781
17166
  this.scheduleFlush();
16782
17167
  }
16783
17168
  isProjectConfigPath(filePath) {
16784
- const relativePath = path21.relative(this.projectRoot, filePath);
16785
- const normalizedRelativePath = path21.normalize(relativePath);
17169
+ const relativePath = path23.relative(this.projectRoot, filePath);
17170
+ const normalizedRelativePath = path23.normalize(relativePath);
16786
17171
  return this.getProjectConfigRelativePaths().some((configPath) => configPath === normalizedRelativePath);
16787
17172
  }
16788
17173
  isProjectConfigPathOrAncestor(relativePath) {
16789
- const normalizedRelativePath = path21.normalize(relativePath);
17174
+ const normalizedRelativePath = path23.normalize(relativePath);
16790
17175
  return this.getProjectConfigRelativePaths().some(
16791
- (configPath) => configPath === normalizedRelativePath || configPath.startsWith(`${normalizedRelativePath}${path21.sep}`)
17176
+ (configPath) => configPath === normalizedRelativePath || configPath.startsWith(`${normalizedRelativePath}${path23.sep}`)
16792
17177
  );
16793
17178
  }
16794
17179
  isOutsideProjectPath(relativePath) {
16795
- return relativePath === ".." || relativePath.startsWith(`..${path21.sep}`) || path21.isAbsolute(relativePath);
17180
+ return relativePath === ".." || relativePath.startsWith(`..${path23.sep}`) || path23.isAbsolute(relativePath);
16796
17181
  }
16797
17182
  getNearestExistingDirectory(directoryPath) {
16798
17183
  let candidate = directoryPath;
16799
17184
  while (!(0, import_fs14.existsSync)(candidate)) {
16800
- const parent = path21.dirname(candidate);
17185
+ const parent = path23.dirname(candidate);
16801
17186
  if (parent === candidate) break;
16802
17187
  candidate = parent;
16803
17188
  }
@@ -16805,9 +17190,51 @@ var FileWatcher = class {
16805
17190
  }
16806
17191
  getProjectConfigRelativePaths() {
16807
17192
  return this.projectConfigPaths.map(
16808
- (configPath) => path21.normalize(path21.relative(this.projectRoot, configPath))
17193
+ (configPath) => path23.normalize(path23.relative(this.projectRoot, configPath))
16809
17194
  );
16810
17195
  }
17196
+ getConfigPathStates() {
17197
+ const states = /* @__PURE__ */ new Map();
17198
+ for (const configPath of this.projectConfigPaths) {
17199
+ const state = this.getConfigPathState(configPath);
17200
+ if (state) states.set(configPath, state);
17201
+ }
17202
+ return states;
17203
+ }
17204
+ getConfigPathState(configPath) {
17205
+ try {
17206
+ const stats = (0, import_fs14.statSync)(configPath);
17207
+ return stats.isFile() ? { mtimeMs: stats.mtimeMs, size: stats.size } : void 0;
17208
+ } catch (error) {
17209
+ void error;
17210
+ return void 0;
17211
+ }
17212
+ }
17213
+ updateConfigPathState(configPath) {
17214
+ const state = this.getConfigPathState(configPath);
17215
+ if (state) {
17216
+ this.configPathStates.set(configPath, state);
17217
+ } else {
17218
+ this.configPathStates.delete(configPath);
17219
+ }
17220
+ }
17221
+ reconcileConfigPathStates() {
17222
+ const nextStates = this.getConfigPathStates();
17223
+ const changes = [];
17224
+ for (const configPath of this.projectConfigPaths) {
17225
+ const previous = this.configPathStates.get(configPath);
17226
+ const next = nextStates.get(configPath);
17227
+ if (!previous && next) {
17228
+ changes.push({ path: configPath, type: "add" });
17229
+ } else if (previous && !next) {
17230
+ changes.push({ path: configPath, type: "unlink" });
17231
+ } else if (previous && next && (previous.size !== next.size || previous.mtimeMs !== next.mtimeMs)) {
17232
+ changes.push({ path: configPath, type: "change" });
17233
+ }
17234
+ }
17235
+ this.configPathStates = nextStates;
17236
+ this.recordChanges(changes);
17237
+ }
16811
17238
  scheduleFlush() {
16812
17239
  if (this.debounceTimer) {
16813
17240
  clearTimeout(this.debounceTimer);
@@ -16821,7 +17248,7 @@ var FileWatcher = class {
16821
17248
  return;
16822
17249
  }
16823
17250
  const changes = Array.from(this.pendingChanges.entries()).map(
16824
- ([path28, type]) => ({ path: path28, type })
17251
+ ([path30, type]) => ({ path: path30, type })
16825
17252
  );
16826
17253
  this.pendingChanges.clear();
16827
17254
  try {
@@ -16835,20 +17262,31 @@ var FileWatcher = class {
16835
17262
  clearTimeout(this.debounceTimer);
16836
17263
  this.debounceTimer = null;
16837
17264
  }
17265
+ if (this.nativeReconcileTimer) {
17266
+ clearTimeout(this.nativeReconcileTimer);
17267
+ this.nativeReconcileTimer = null;
17268
+ }
17269
+ this.nativeInvalidatedPaths.clear();
16838
17270
  const watcher = this.watcher;
17271
+ const nativeWatcher = this.nativeWatcher;
16839
17272
  const pendingClose = this.pendingClose;
16840
17273
  const resolveReady = this.resolveReady;
16841
17274
  this.watcher = null;
17275
+ this.nativeWatcher = null;
17276
+ this.nativeReconciler = null;
17277
+ this.nativeStarting = false;
17278
+ this.nativeInitializing = false;
17279
+ this.nativeSetupGeneration += 1;
16842
17280
  this.pendingClose = null;
16843
17281
  this.resolveReady = null;
16844
17282
  this.readyPromise = null;
16845
17283
  this.pendingChanges.clear();
16846
17284
  this.onChanges = null;
16847
- await Promise.all([watcher?.close(), pendingClose]);
17285
+ await Promise.all([watcher?.close(), nativeWatcher?.stop(), pendingClose]);
16848
17286
  resolveReady?.();
16849
17287
  }
16850
17288
  isRunning() {
16851
- return this.watcher !== null;
17289
+ return this.watcher !== null || this.nativeWatcher !== null || this.nativeStarting;
16852
17290
  }
16853
17291
  async waitUntilReady() {
16854
17292
  await (this.readyPromise ?? Promise.resolve());
@@ -16856,7 +17294,7 @@ var FileWatcher = class {
16856
17294
  };
16857
17295
 
16858
17296
  // src/watcher/git-head-watcher.ts
16859
- var path22 = __toESM(require("path"), 1);
17297
+ var path24 = __toESM(require("path"), 1);
16860
17298
  var GitHeadWatcher = class {
16861
17299
  watcher = null;
16862
17300
  projectRoot;
@@ -16878,13 +17316,13 @@ var GitHeadWatcher = class {
16878
17316
  this.readyPromise = Promise.resolve();
16879
17317
  return;
16880
17318
  }
16881
- this.readyPromise = new Promise((resolve15) => {
16882
- this.resolveReady = resolve15;
17319
+ this.readyPromise = new Promise((resolve17) => {
17320
+ this.resolveReady = resolve17;
16883
17321
  });
16884
17322
  this.onBranchChange = handler;
16885
17323
  this.currentBranch = getCurrentBranch(this.projectRoot);
16886
17324
  const headPath = getHeadPath(this.projectRoot);
16887
- const refsPath = path22.join(this.projectRoot, ".git", "refs", "heads");
17325
+ const refsPath = path24.join(this.projectRoot, ".git", "refs", "heads");
16888
17326
  this.watcher = chokidar_default.watch([headPath, refsPath], {
16889
17327
  persistent: true,
16890
17328
  ignoreInitial: true,
@@ -17740,7 +18178,7 @@ async function resolveCodebaseContextUnmeasured(projectRoot, host, input) {
17740
18178
  const directory = input.directory ?? void 0;
17741
18179
  const tokenBudget = input.tokenBudget ?? void 0;
17742
18180
  if (from && to) {
17743
- const path28 = await getCallGraphPath(
18181
+ const path30 = await getCallGraphPath(
17744
18182
  projectRoot,
17745
18183
  host,
17746
18184
  from,
@@ -17749,25 +18187,25 @@ async function resolveCodebaseContextUnmeasured(projectRoot, host, input) {
17749
18187
  fromFilePath,
17750
18188
  toFilePath
17751
18189
  );
17752
- const pathText = formatCallGraphPathResult(path28);
17753
- if (path28.path.length > 0) {
18190
+ const pathText = formatCallGraphPathResult(path30);
18191
+ if (path30.path.length > 0) {
17754
18192
  const fitted2 = fitTextToContextBudget(
17755
18193
  pathText,
17756
18194
  tokenBudget
17757
18195
  );
17758
18196
  return {
17759
18197
  text: fitted2.text,
17760
- details: fittedDetails("path", fitted2, path28.path.length)
18198
+ details: fittedDetails("path", fitted2, path30.path.length)
17761
18199
  };
17762
18200
  }
17763
- if (path28.from.status !== "resolved" || path28.to.status !== "resolved") {
18201
+ if (path30.from.status !== "resolved" || path30.to.status !== "resolved") {
17764
18202
  const fitted2 = fitTextToContextBudget(pathText, tokenBudget);
17765
18203
  return {
17766
18204
  text: fitted2.text,
17767
18205
  details: fittedDetails("path", fitted2, 0)
17768
18206
  };
17769
18207
  }
17770
- const resolvedFrom = path28.from;
18208
+ const resolvedFrom = path30.from;
17771
18209
  const { callers } = await getCallGraphData(projectRoot, host, {
17772
18210
  name: to,
17773
18211
  direction: "callers",
@@ -17926,7 +18364,7 @@ async function executeCallGraph(projectRoot, host, args) {
17926
18364
  return { text: formatCallGraphResult(await getCallGraphData(projectRoot, host, args)) };
17927
18365
  }
17928
18366
  async function executeCallGraphPath(projectRoot, host, args) {
17929
- const path28 = await getCallGraphPath(
18367
+ const path30 = await getCallGraphPath(
17930
18368
  projectRoot,
17931
18369
  host,
17932
18370
  args.from,
@@ -17935,7 +18373,7 @@ async function executeCallGraphPath(projectRoot, host, args) {
17935
18373
  args.fromFilePath,
17936
18374
  args.toFilePath
17937
18375
  );
17938
- return { text: formatCallGraphPathResult(path28) };
18376
+ return { text: formatCallGraphPathResult(path30) };
17939
18377
  }
17940
18378
  async function executeCodeCommunities(projectRoot, host, args) {
17941
18379
  const result = await getCodeCommunities(projectRoot, host, args);
@@ -17945,11 +18383,11 @@ async function executeCodeCommunities(projectRoot, host, args) {
17945
18383
  // src/adapters/opencode/tools.ts
17946
18384
  var import_fs15 = require("fs");
17947
18385
  var os7 = __toESM(require("os"), 1);
17948
- var path25 = __toESM(require("path"), 1);
18386
+ var path27 = __toESM(require("path"), 1);
17949
18387
 
17950
18388
  // src/tools/visualize/activity.ts
17951
18389
  var import_child_process5 = require("child_process");
17952
- var path23 = __toESM(require("path"), 1);
18390
+ var path25 = __toESM(require("path"), 1);
17953
18391
  function attachRecentActivity(data, projectRoot) {
17954
18392
  const activity = readGitActivity(projectRoot);
17955
18393
  const changes = activity.size > 0 ? buildGitChanges(data, activity, projectRoot) : buildGraphChanges(data);
@@ -18111,7 +18549,7 @@ function normalizePath3(filePath) {
18111
18549
  return filePath.replace(/\\/g, "/");
18112
18550
  }
18113
18551
  function toGitRelativePath(projectRoot, filePath) {
18114
- const relativePath = path23.isAbsolute(filePath) ? path23.relative(projectRoot, filePath) : filePath;
18552
+ const relativePath = path25.isAbsolute(filePath) ? path25.relative(projectRoot, filePath) : filePath;
18115
18553
  return normalizePath3(relativePath);
18116
18554
  }
18117
18555
 
@@ -18369,7 +18807,7 @@ render();
18369
18807
  }
18370
18808
 
18371
18809
  // src/tools/visualize/transform.ts
18372
- var path24 = __toESM(require("path"), 1);
18810
+ var path26 = __toESM(require("path"), 1);
18373
18811
 
18374
18812
  // src/tools/visualize/modules.ts
18375
18813
  var MAX_MODULES = 18;
@@ -18502,8 +18940,8 @@ function compactModules(prefixToNodes) {
18502
18940
  function deriveModules(nodes) {
18503
18941
  const initial = /* @__PURE__ */ new Map();
18504
18942
  for (const node of nodes) {
18505
- const relative12 = stripToProjectRelative(node.filePath);
18506
- const prefix = modulePrefixFromRelativePath(relative12);
18943
+ const relative14 = stripToProjectRelative(node.filePath);
18944
+ const prefix = modulePrefixFromRelativePath(relative14);
18507
18945
  if (!initial.has(prefix)) initial.set(prefix, []);
18508
18946
  initial.get(prefix)?.push(node);
18509
18947
  }
@@ -18629,7 +19067,7 @@ function transformForVisualization(symbols, edges, options = {}) {
18629
19067
  filePath: s.filePath,
18630
19068
  kind: s.kind,
18631
19069
  line: s.startLine,
18632
- directory: path24.dirname(s.filePath),
19070
+ directory: path26.dirname(s.filePath),
18633
19071
  moduleId: "",
18634
19072
  moduleLabel: ""
18635
19073
  }));
@@ -18949,7 +19387,7 @@ var index_visualize = tool({
18949
19387
  return "No connected symbols found for visualization. Try including orphans with includeOrphans=true, or check that the call graph has resolved edges.";
18950
19388
  }
18951
19389
  const html = generateVisualizationHtml(vizData);
18952
- const outputPath = path25.join(os7.tmpdir(), `call-graph-${Date.now()}.html`);
19390
+ const outputPath = path27.join(os7.tmpdir(), `call-graph-${Date.now()}.html`);
18953
19391
  (0, import_fs15.writeFileSync)(outputPath, html, "utf-8");
18954
19392
  let result = `Temporal call graph visualization generated: ${outputPath}
18955
19393
 
@@ -19056,7 +19494,7 @@ var PI_TOOL_NAMES = [
19056
19494
 
19057
19495
  // src/commands/loader.ts
19058
19496
  var import_fs16 = require("fs");
19059
- var path26 = __toESM(require("path"), 1);
19497
+ var path28 = __toESM(require("path"), 1);
19060
19498
  function parseFrontmatter(content) {
19061
19499
  const frontmatterRegex = /^---\s*\n([\s\S]*?)\n---\s*\n([\s\S]*)$/;
19062
19500
  const match = content.match(frontmatterRegex);
@@ -19082,7 +19520,7 @@ function loadCommandsFromDirectory(commandsDir) {
19082
19520
  }
19083
19521
  const files = (0, import_fs16.readdirSync)(commandsDir).filter((f) => f.endsWith(".md"));
19084
19522
  for (const file of files) {
19085
- const filePath = path26.join(commandsDir, file);
19523
+ const filePath = path28.join(commandsDir, file);
19086
19524
  let content;
19087
19525
  try {
19088
19526
  content = (0, import_fs16.readFileSync)(filePath, "utf-8");
@@ -19091,7 +19529,7 @@ function loadCommandsFromDirectory(commandsDir) {
19091
19529
  throw new Error(`Failed to load command file ${filePath}: ${message}`);
19092
19530
  }
19093
19531
  const { frontmatter, body } = parseFrontmatter(content);
19094
- const name = path26.basename(file, ".md");
19532
+ const name = path28.basename(file, ".md");
19095
19533
  const description = frontmatter.description || `Run the ${name} command`;
19096
19534
  commands.set(name, {
19097
19535
  description,
@@ -19426,23 +19864,41 @@ var RoutingHintController = class {
19426
19864
  // src/adapters/opencode.ts
19427
19865
  var import_meta2 = {};
19428
19866
  var activeWatchers = /* @__PURE__ */ new Map();
19429
- function replaceActiveWatcher(projectRoot, nextWatcher) {
19430
- const existing = activeWatchers.get(projectRoot);
19431
- if (existing) {
19432
- existing.stop();
19433
- activeWatchers.delete(projectRoot);
19434
- }
19435
- if (nextWatcher) {
19436
- activeWatchers.set(projectRoot, nextWatcher);
19867
+ var watcherReplacementChains = /* @__PURE__ */ new Map();
19868
+ async function replaceActiveWatcher(projectRoot, createNextWatcher) {
19869
+ const chain = (watcherReplacementChains.get(projectRoot) ?? Promise.resolve()).catch(() => void 0).then(async () => {
19870
+ const existing = activeWatchers.get(projectRoot);
19871
+ if (existing) {
19872
+ try {
19873
+ await existing.stop();
19874
+ } catch (error) {
19875
+ console.error("[codebase-index] Failed to stop replaced watcher:", error);
19876
+ throw error;
19877
+ }
19878
+ if (activeWatchers.get(projectRoot) === existing) {
19879
+ activeWatchers.delete(projectRoot);
19880
+ }
19881
+ }
19882
+ if (createNextWatcher) {
19883
+ activeWatchers.set(projectRoot, createNextWatcher());
19884
+ }
19885
+ });
19886
+ watcherReplacementChains.set(projectRoot, chain);
19887
+ try {
19888
+ await chain;
19889
+ } finally {
19890
+ if (watcherReplacementChains.get(projectRoot) === chain) {
19891
+ watcherReplacementChains.delete(projectRoot);
19892
+ }
19437
19893
  }
19438
19894
  }
19439
19895
  function getCommandsDir() {
19440
19896
  let currentDir = process.cwd();
19441
19897
  if (typeof import_meta2 !== "undefined" && import_meta2.url) {
19442
- currentDir = path27.dirname((0, import_url.fileURLToPath)(import_meta2.url));
19898
+ currentDir = path29.dirname((0, import_url.fileURLToPath)(import_meta2.url));
19443
19899
  }
19444
- const packageRoot = path27.basename(currentDir) === "adapters" ? path27.join(currentDir, "..", "..") : path27.join(currentDir, "..");
19445
- return path27.join(packageRoot, "commands");
19900
+ const packageRoot = path29.basename(currentDir) === "adapters" ? path29.join(currentDir, "..", "..") : path29.join(currentDir, "..");
19901
+ return path29.join(packageRoot, "commands");
19446
19902
  }
19447
19903
  function appendRoutingHints(output, hints, preferredRole) {
19448
19904
  const preferredBucket = preferredRole === "developer" ? output.developer : output.system;
@@ -19483,9 +19939,12 @@ var plugin = async ({ directory, worktree }) => {
19483
19939
  startAutoIndex(projectRoot, "opencode", "startup");
19484
19940
  }
19485
19941
  if (config.indexing.watchFiles && isValidProject) {
19486
- replaceActiveWatcher(projectRoot, createWatcherWithIndexer(getProjectIndexer, projectRoot, config, "opencode"));
19942
+ await replaceActiveWatcher(
19943
+ projectRoot,
19944
+ () => createWatcherWithIndexer(getProjectIndexer, projectRoot, config, "opencode")
19945
+ );
19487
19946
  } else {
19488
- replaceActiveWatcher(projectRoot, null);
19947
+ await replaceActiveWatcher(projectRoot, null);
19489
19948
  }
19490
19949
  return {
19491
19950
  tool: {