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.js CHANGED
@@ -328,7 +328,7 @@ var require_ignore = __commonJS({
328
328
  // path matching.
329
329
  // - check `string` either `MODE_IGNORE` or `MODE_CHECK_IGNORE`
330
330
  // @returns {TestResult} true if a file is ignored
331
- test(path28, checkUnignored, mode) {
331
+ test(path30, checkUnignored, mode) {
332
332
  let ignored = false;
333
333
  let unignored = false;
334
334
  let matchedRule;
@@ -337,7 +337,7 @@ var require_ignore = __commonJS({
337
337
  if (unignored === negative && ignored !== unignored || negative && !ignored && !unignored && !checkUnignored) {
338
338
  return;
339
339
  }
340
- const matched = rule[mode].test(path28);
340
+ const matched = rule[mode].test(path30);
341
341
  if (!matched) {
342
342
  return;
343
343
  }
@@ -358,17 +358,17 @@ var require_ignore = __commonJS({
358
358
  var throwError = (message, Ctor) => {
359
359
  throw new Ctor(message);
360
360
  };
361
- var checkPath = (path28, originalPath, doThrow) => {
362
- if (!isString(path28)) {
361
+ var checkPath = (path30, originalPath, doThrow) => {
362
+ if (!isString(path30)) {
363
363
  return doThrow(
364
364
  `path must be a string, but got \`${originalPath}\``,
365
365
  TypeError
366
366
  );
367
367
  }
368
- if (!path28) {
368
+ if (!path30) {
369
369
  return doThrow(`path must not be empty`, TypeError);
370
370
  }
371
- if (checkPath.isNotRelative(path28)) {
371
+ if (checkPath.isNotRelative(path30)) {
372
372
  const r = "`path.relative()`d";
373
373
  return doThrow(
374
374
  `path should be a ${r} string, but got "${originalPath}"`,
@@ -377,7 +377,7 @@ var require_ignore = __commonJS({
377
377
  }
378
378
  return true;
379
379
  };
380
- var isNotRelative = (path28) => REGEX_TEST_INVALID_PATH.test(path28);
380
+ var isNotRelative = (path30) => REGEX_TEST_INVALID_PATH.test(path30);
381
381
  checkPath.isNotRelative = isNotRelative;
382
382
  checkPath.convert = (p) => p;
383
383
  var Ignore2 = class {
@@ -407,19 +407,19 @@ var require_ignore = __commonJS({
407
407
  }
408
408
  // @returns {TestResult}
409
409
  _test(originalPath, cache, checkUnignored, slices) {
410
- const path28 = originalPath && checkPath.convert(originalPath);
410
+ const path30 = originalPath && checkPath.convert(originalPath);
411
411
  checkPath(
412
- path28,
412
+ path30,
413
413
  originalPath,
414
414
  this._strictPathCheck ? throwError : RETURN_FALSE
415
415
  );
416
- return this._t(path28, cache, checkUnignored, slices);
416
+ return this._t(path30, cache, checkUnignored, slices);
417
417
  }
418
- checkIgnore(path28) {
419
- if (!REGEX_TEST_TRAILING_SLASH.test(path28)) {
420
- return this.test(path28);
418
+ checkIgnore(path30) {
419
+ if (!REGEX_TEST_TRAILING_SLASH.test(path30)) {
420
+ return this.test(path30);
421
421
  }
422
- const slices = path28.split(SLASH2).filter(Boolean);
422
+ const slices = path30.split(SLASH2).filter(Boolean);
423
423
  slices.pop();
424
424
  if (slices.length) {
425
425
  const parent = this._t(
@@ -432,18 +432,18 @@ var require_ignore = __commonJS({
432
432
  return parent;
433
433
  }
434
434
  }
435
- return this._rules.test(path28, false, MODE_CHECK_IGNORE);
435
+ return this._rules.test(path30, false, MODE_CHECK_IGNORE);
436
436
  }
437
- _t(path28, cache, checkUnignored, slices) {
438
- if (path28 in cache) {
439
- return cache[path28];
437
+ _t(path30, cache, checkUnignored, slices) {
438
+ if (path30 in cache) {
439
+ return cache[path30];
440
440
  }
441
441
  if (!slices) {
442
- slices = path28.split(SLASH2).filter(Boolean);
442
+ slices = path30.split(SLASH2).filter(Boolean);
443
443
  }
444
444
  slices.pop();
445
445
  if (!slices.length) {
446
- return cache[path28] = this._rules.test(path28, checkUnignored, MODE_IGNORE);
446
+ return cache[path30] = this._rules.test(path30, checkUnignored, MODE_IGNORE);
447
447
  }
448
448
  const parent = this._t(
449
449
  slices.join(SLASH2) + SLASH2,
@@ -451,29 +451,29 @@ var require_ignore = __commonJS({
451
451
  checkUnignored,
452
452
  slices
453
453
  );
454
- return cache[path28] = parent.ignored ? parent : this._rules.test(path28, checkUnignored, MODE_IGNORE);
454
+ return cache[path30] = parent.ignored ? parent : this._rules.test(path30, checkUnignored, MODE_IGNORE);
455
455
  }
456
- ignores(path28) {
457
- return this._test(path28, this._ignoreCache, false).ignored;
456
+ ignores(path30) {
457
+ return this._test(path30, this._ignoreCache, false).ignored;
458
458
  }
459
459
  createFilter() {
460
- return (path28) => !this.ignores(path28);
460
+ return (path30) => !this.ignores(path30);
461
461
  }
462
462
  filter(paths) {
463
463
  return makeArray(paths).filter(this.createFilter());
464
464
  }
465
465
  // @returns {TestResult}
466
- test(path28) {
467
- return this._test(path28, this._testCache, true);
466
+ test(path30) {
467
+ return this._test(path30, this._testCache, true);
468
468
  }
469
469
  };
470
470
  var factory = (options) => new Ignore2(options);
471
- var isPathValid = (path28) => checkPath(path28 && checkPath.convert(path28), path28, RETURN_FALSE);
471
+ var isPathValid = (path30) => checkPath(path30 && checkPath.convert(path30), path30, RETURN_FALSE);
472
472
  var setupWindows = () => {
473
473
  const makePosix = (str) => /^\\\\\?\\/.test(str) || /["<>|\u0000-\u001F]+/u.test(str) ? str : str.replace(/\\/g, "/");
474
474
  checkPath.convert = makePosix;
475
475
  const REGEX_TEST_WINDOWS_PATH_ABSOLUTE = /^[a-z]:\//i;
476
- checkPath.isNotRelative = (path28) => REGEX_TEST_WINDOWS_PATH_ABSOLUTE.test(path28) || isNotRelative(path28);
476
+ checkPath.isNotRelative = (path30) => REGEX_TEST_WINDOWS_PATH_ABSOLUTE.test(path30) || isNotRelative(path30);
477
477
  };
478
478
  if (
479
479
  // Detect `process` so that it can run in browsers.
@@ -651,7 +651,7 @@ var require_eventemitter3 = __commonJS({
651
651
  });
652
652
 
653
653
  // src/adapters/opencode.ts
654
- import * as path27 from "path";
654
+ import * as path29 from "path";
655
655
  import { fileURLToPath as fileURLToPath2 } from "url";
656
656
 
657
657
  // src/config/constants.ts
@@ -1129,11 +1129,11 @@ function resolveGitDir(repoRoot) {
1129
1129
  return null;
1130
1130
  }
1131
1131
  try {
1132
- const stat4 = statSync2(gitPath);
1133
- if (stat4.isDirectory()) {
1132
+ const stat5 = statSync2(gitPath);
1133
+ if (stat5.isDirectory()) {
1134
1134
  return gitPath;
1135
1135
  }
1136
- if (stat4.isFile()) {
1136
+ if (stat5.isFile()) {
1137
1137
  const content = readFileSync2(gitPath, "utf-8").trim();
1138
1138
  const match = content.match(/^gitdir:\s*(.+)$/);
1139
1139
  if (match) {
@@ -2202,7 +2202,7 @@ function analyzeQueryIntent(query) {
2202
2202
  }
2203
2203
  function isTestPath(filePath) {
2204
2204
  const normalized = normalizePath(filePath);
2205
- return /(?:^|\/)(?:test|tests|__tests__|spec|specs)(?:\/|$)/u.test(normalized) || /\.(?:test|spec)\.[^/]+$/u.test(normalized);
2205
+ return /(?:^|\/)(?:test|tests|__tests__|spec|specs)(?:\/|$)/u.test(normalized) || /(?:\.(?:test|spec)|_(?:test|spec))\.[^/]+$/u.test(normalized) || /(?:^|\/)(?:test|spec)_[^/]+\.[^/]+$/u.test(normalized);
2206
2206
  }
2207
2207
  function isFixturePath(filePath) {
2208
2208
  const normalized = normalizePath(filePath);
@@ -2577,8 +2577,8 @@ function formatExactSearchHandoff(results) {
2577
2577
  }
2578
2578
  function formatContextEvidence(result, index) {
2579
2579
  const symbol = result.name ? ` ${JSON.stringify(compactEvidenceValue(result.name, 80))}` : "";
2580
- const path28 = compactEvidenceValue(result.filePath, 120);
2581
- return `[${index}] ${result.chunkType}${symbol} in ${path28}:${result.startLine}-${result.endLine} (score ${result.score.toFixed(2)})`;
2580
+ const path30 = compactEvidenceValue(result.filePath, 120);
2581
+ return `[${index}] ${result.chunkType}${symbol} in ${path30}:${result.startLine}-${result.endLine} (score ${result.score.toFixed(2)})`;
2582
2582
  }
2583
2583
  function formatContextPack(heading, selected, candidateCount, duplicateCount, limitOmittedCount, budgetOmittedCount, includeExactSearchHandoff) {
2584
2584
  const lines = selected.map((result, index) => formatContextEvidence(result, index + 1));
@@ -3741,8 +3741,8 @@ async function* walkDirectory(dir, projectRoot, includePatterns, excludePatterns
3741
3741
  if (entry.isDirectory()) {
3742
3742
  subdirs.push({ fullPath, relativePath });
3743
3743
  } else if (entry.isFile()) {
3744
- const stat4 = await fsPromises.stat(fullPath);
3745
- if (stat4.size > maxFileSize) {
3744
+ const stat5 = await fsPromises.stat(fullPath);
3745
+ if (stat5.size > maxFileSize) {
3746
3746
  skipped.push({ path: relativePath, reason: "too_large" });
3747
3747
  continue;
3748
3748
  }
@@ -3760,7 +3760,7 @@ async function* walkDirectory(dir, projectRoot, includePatterns, excludePatterns
3760
3760
  }
3761
3761
  }
3762
3762
  if (matched) {
3763
- filesInDir.push({ path: fullPath, size: stat4.size });
3763
+ filesInDir.push({ path: fullPath, size: stat5.size });
3764
3764
  }
3765
3765
  }
3766
3766
  }
@@ -3817,8 +3817,8 @@ async function collectFiles(projectRoot, includePatterns, excludePatterns, maxFi
3817
3817
  }
3818
3818
  for (const resolvedKbRoot of normalizedRoots) {
3819
3819
  try {
3820
- const stat4 = await fsPromises.stat(resolvedKbRoot);
3821
- if (!stat4.isDirectory()) {
3820
+ const stat5 = await fsPromises.stat(resolvedKbRoot);
3821
+ if (!stat5.isDirectory()) {
3822
3822
  skipped.push({ path: resolvedKbRoot, reason: "excluded" });
3823
3823
  continue;
3824
3824
  }
@@ -3852,7 +3852,7 @@ function getErrorMessage(error) {
3852
3852
  return error instanceof Error ? error.message : String(error);
3853
3853
  }
3854
3854
  function runCommand(file, args, options) {
3855
- return new Promise((resolve15, reject) => {
3855
+ return new Promise((resolve17, reject) => {
3856
3856
  childProcess.execFile(
3857
3857
  file,
3858
3858
  args,
@@ -3862,7 +3862,7 @@ function runCommand(file, args, options) {
3862
3862
  reject(error);
3863
3863
  return;
3864
3864
  }
3865
- resolve15(stdout);
3865
+ resolve17(stdout);
3866
3866
  }
3867
3867
  );
3868
3868
  });
@@ -4007,10 +4007,10 @@ function safeFailureMessage(error) {
4007
4007
  }
4008
4008
  function cancellableDelay(delayMs, signal) {
4009
4009
  if (signal.aborted) return Promise.reject(new AutoIndexCancelledError());
4010
- return new Promise((resolve15, reject) => {
4010
+ return new Promise((resolve17, reject) => {
4011
4011
  const timer = setTimeout(() => {
4012
4012
  signal.removeEventListener("abort", onAbort);
4013
- resolve15();
4013
+ resolve17();
4014
4014
  }, delayMs);
4015
4015
  timer.unref?.();
4016
4016
  const onAbort = () => {
@@ -4022,15 +4022,15 @@ function cancellableDelay(delayMs, signal) {
4022
4022
  }
4023
4023
  function withTimeout(promise, timeoutMs) {
4024
4024
  if (timeoutMs <= 0) return Promise.resolve(void 0);
4025
- return new Promise((resolve15) => {
4026
- const timer = setTimeout(() => resolve15(void 0), timeoutMs);
4025
+ return new Promise((resolve17) => {
4026
+ const timer = setTimeout(() => resolve17(void 0), timeoutMs);
4027
4027
  timer.unref?.();
4028
4028
  void promise.then((value) => {
4029
4029
  clearTimeout(timer);
4030
- resolve15(value);
4030
+ resolve17(value);
4031
4031
  }, () => {
4032
4032
  clearTimeout(timer);
4033
- resolve15(void 0);
4033
+ resolve17(void 0);
4034
4034
  });
4035
4035
  });
4036
4036
  }
@@ -4412,17 +4412,17 @@ var AutoIndexCoordinator = class {
4412
4412
  }
4413
4413
  }
4414
4414
  waitForBatteryRetry(delayMs) {
4415
- return new Promise((resolve15) => {
4415
+ return new Promise((resolve17) => {
4416
4416
  const timer = setTimeout(() => {
4417
4417
  if (this.batteryRetryTimer === timer) {
4418
4418
  this.batteryRetryTimer = null;
4419
4419
  this.resolveBatteryRetry = null;
4420
4420
  }
4421
- resolve15();
4421
+ resolve17();
4422
4422
  }, delayMs);
4423
4423
  timer.unref?.();
4424
4424
  this.batteryRetryTimer = timer;
4425
- this.resolveBatteryRetry = resolve15;
4425
+ this.resolveBatteryRetry = resolve17;
4426
4426
  });
4427
4427
  }
4428
4428
  cancelBatteryRetry() {
@@ -4430,9 +4430,9 @@ var AutoIndexCoordinator = class {
4430
4430
  clearTimeout(this.batteryRetryTimer);
4431
4431
  this.batteryRetryTimer = null;
4432
4432
  }
4433
- const resolve15 = this.resolveBatteryRetry;
4433
+ const resolve17 = this.resolveBatteryRetry;
4434
4434
  this.resolveBatteryRetry = null;
4435
- resolve15?.();
4435
+ resolve17?.();
4436
4436
  }
4437
4437
  finishBatteryCheck(batteryCheck) {
4438
4438
  if (this.batteryCheck !== batteryCheck) return;
@@ -4637,7 +4637,7 @@ function pTimeout(promise, options) {
4637
4637
  } = options;
4638
4638
  let timer;
4639
4639
  let abortHandler;
4640
- const wrappedPromise = new Promise((resolve15, reject) => {
4640
+ const wrappedPromise = new Promise((resolve17, reject) => {
4641
4641
  if (typeof milliseconds !== "number" || Math.sign(milliseconds) !== 1) {
4642
4642
  throw new TypeError(`Expected \`milliseconds\` to be a positive number, got \`${milliseconds}\``);
4643
4643
  }
@@ -4651,7 +4651,7 @@ function pTimeout(promise, options) {
4651
4651
  };
4652
4652
  signal.addEventListener("abort", abortHandler, { once: true });
4653
4653
  }
4654
- promise.then(resolve15, reject);
4654
+ promise.then(resolve17, reject);
4655
4655
  if (milliseconds === Number.POSITIVE_INFINITY) {
4656
4656
  return;
4657
4657
  }
@@ -4659,7 +4659,7 @@ function pTimeout(promise, options) {
4659
4659
  timer = customTimers.setTimeout.call(void 0, () => {
4660
4660
  if (fallback) {
4661
4661
  try {
4662
- resolve15(fallback());
4662
+ resolve17(fallback());
4663
4663
  } catch (error) {
4664
4664
  reject(error);
4665
4665
  }
@@ -4669,7 +4669,7 @@ function pTimeout(promise, options) {
4669
4669
  promise.cancel();
4670
4670
  }
4671
4671
  if (message === false) {
4672
- resolve15();
4672
+ resolve17();
4673
4673
  } else if (message instanceof Error) {
4674
4674
  reject(message);
4675
4675
  } else {
@@ -5071,7 +5071,7 @@ var PQueue = class extends import_index.default {
5071
5071
  // Assign unique ID if not provided
5072
5072
  id: options.id ?? (this.#idAssigner++).toString()
5073
5073
  };
5074
- return new Promise((resolve15, reject) => {
5074
+ return new Promise((resolve17, reject) => {
5075
5075
  const taskSymbol = /* @__PURE__ */ Symbol(`task-${options.id}`);
5076
5076
  let cleanupQueueAbortHandler = () => void 0;
5077
5077
  const run = async () => {
@@ -5111,7 +5111,7 @@ var PQueue = class extends import_index.default {
5111
5111
  })]);
5112
5112
  }
5113
5113
  const result = await operation;
5114
- resolve15(result);
5114
+ resolve17(result);
5115
5115
  this.emit("completed", result);
5116
5116
  } catch (error) {
5117
5117
  reject(error);
@@ -5299,13 +5299,13 @@ var PQueue = class extends import_index.default {
5299
5299
  });
5300
5300
  }
5301
5301
  async #onEvent(event, filter) {
5302
- return new Promise((resolve15) => {
5302
+ return new Promise((resolve17) => {
5303
5303
  const listener = () => {
5304
5304
  if (filter && !filter()) {
5305
5305
  return;
5306
5306
  }
5307
5307
  this.off(event, listener);
5308
- resolve15();
5308
+ resolve17();
5309
5309
  };
5310
5310
  this.on(event, listener);
5311
5311
  });
@@ -5591,7 +5591,7 @@ async function onAttemptFailure({ error, attemptNumber, retriesConsumed, startTi
5591
5591
  const finalDelay = Math.min(delayTime, remainingTime);
5592
5592
  options.signal?.throwIfAborted();
5593
5593
  if (finalDelay > 0) {
5594
- await new Promise((resolve15, reject) => {
5594
+ await new Promise((resolve17, reject) => {
5595
5595
  const onAbort = () => {
5596
5596
  clearTimeout(timeoutToken);
5597
5597
  options.signal?.removeEventListener("abort", onAbort);
@@ -5599,7 +5599,7 @@ async function onAttemptFailure({ error, attemptNumber, retriesConsumed, startTi
5599
5599
  };
5600
5600
  const timeoutToken = setTimeout(() => {
5601
5601
  options.signal?.removeEventListener("abort", onAbort);
5602
- resolve15();
5602
+ resolve17();
5603
5603
  }, finalDelay);
5604
5604
  if (options.unref) {
5605
5605
  timeoutToken.unref?.();
@@ -6405,85 +6405,6 @@ function createEmbeddingProvider(configuredProviderInfo) {
6405
6405
  }
6406
6406
  }
6407
6407
 
6408
- // src/rerank/index.ts
6409
- function createReranker(config) {
6410
- if (!config.enabled) {
6411
- return new NoOpReranker();
6412
- }
6413
- return new SiliconFlowReranker(config);
6414
- }
6415
- var NoOpReranker = class {
6416
- isAvailable() {
6417
- return false;
6418
- }
6419
- async rerank(_query, documents, _topN) {
6420
- return {
6421
- results: documents.map((_, index) => ({ index, relevanceScore: 0 }))
6422
- };
6423
- }
6424
- };
6425
- var SiliconFlowReranker = class {
6426
- config;
6427
- constructor(config) {
6428
- this.config = config;
6429
- }
6430
- isAvailable() {
6431
- return this.config.enabled && !!this.config.baseUrl && !!this.config.model;
6432
- }
6433
- async rerank(query, documents, topN) {
6434
- if (documents.length === 0) {
6435
- return { results: [] };
6436
- }
6437
- const headers = {
6438
- "Content-Type": "application/json"
6439
- };
6440
- if (this.config.apiKey) {
6441
- headers["Authorization"] = `Bearer ${this.config.apiKey}`;
6442
- }
6443
- const baseUrl = this.config.baseUrl;
6444
- if (!baseUrl) {
6445
- throw new Error("Reranker baseUrl is required. Configure reranker.baseUrl in your codebase-index.json.");
6446
- }
6447
- const timeoutMs = this.config.timeoutMs ?? 3e4;
6448
- const controller = new AbortController();
6449
- const timeout = setTimeout(() => controller.abort(), timeoutMs);
6450
- try {
6451
- const response = await fetch(`${baseUrl}/rerank`, {
6452
- method: "POST",
6453
- headers,
6454
- body: JSON.stringify({
6455
- model: this.config.model,
6456
- query,
6457
- documents,
6458
- top_n: topN ?? this.config.topN ?? 20,
6459
- return_documents: false
6460
- }),
6461
- signal: controller.signal
6462
- });
6463
- clearTimeout(timeout);
6464
- if (!response.ok) {
6465
- const errorText = await response.text();
6466
- throw new Error(`Rerank API error: ${response.status} - ${errorText}`);
6467
- }
6468
- const data = await response.json();
6469
- return {
6470
- results: data.results.map((r) => ({
6471
- index: r.index,
6472
- relevanceScore: r.relevance_score,
6473
- document: r.document?.text
6474
- })),
6475
- tokensUsed: data.meta?.tokens?.input_tokens
6476
- };
6477
- } catch (error) {
6478
- clearTimeout(timeout);
6479
- if (error instanceof Error && error.name === "AbortError") {
6480
- throw new Error(`Rerank API request timed out after ${timeoutMs}ms`);
6481
- }
6482
- throw error;
6483
- }
6484
- }
6485
- };
6486
-
6487
6408
  // src/utils/cost.ts
6488
6409
  function estimateChunksFromFiles(files) {
6489
6410
  let totalChunks = 0;
@@ -8043,8 +7964,8 @@ async function isWorktreeRegistered(projectRoot, worktreePath) {
8043
7964
  return false;
8044
7965
  }
8045
7966
  function isPathWithinRoot(filePath, rootPath) {
8046
- const relative12 = path15.relative(path15.resolve(rootPath), path15.resolve(filePath));
8047
- return relative12 === "" || !relative12.startsWith(`..${path15.sep}`) && relative12 !== ".." && !path15.isAbsolute(relative12);
7967
+ const relative14 = path15.relative(path15.resolve(rootPath), path15.resolve(filePath));
7968
+ return relative14 === "" || !relative14.startsWith(`..${path15.sep}`) && relative14 !== ".." && !path15.isAbsolute(relative14);
8048
7969
  }
8049
7970
  async function pruneExactMissingWorktreeRegistration(projectRoot, worktreePath) {
8050
7971
  if (await pathExists(worktreePath)) return false;
@@ -8421,11 +8342,11 @@ function normalizeFiles(rawFiles, projectRoot) {
8421
8342
  for (const raw of rawFiles) {
8422
8343
  if (raw.length === 0) continue;
8423
8344
  const absolute = path16.resolve(root, raw);
8424
- const relative12 = path16.relative(root, absolute);
8425
- if (path16.isAbsolute(raw) || relative12 === ".." || relative12.startsWith(`..${path16.sep}`) || path16.isAbsolute(relative12)) {
8345
+ const relative14 = path16.relative(root, absolute);
8346
+ if (path16.isAbsolute(raw) || relative14 === ".." || relative14.startsWith(`..${path16.sep}`) || path16.isAbsolute(relative14)) {
8426
8347
  throw new Error(`Changed file escapes the project root: ${JSON.stringify(raw)}`);
8427
8348
  }
8428
- const cleaned = relative12.startsWith(`.${path16.sep}`) ? relative12.slice(2) : relative12;
8349
+ const cleaned = relative14.startsWith(`.${path16.sep}`) ? relative14.slice(2) : relative14;
8429
8350
  if (!seen.has(cleaned)) {
8430
8351
  seen.add(cleaned);
8431
8352
  result.push(cleaned);
@@ -8657,7 +8578,8 @@ function rankHybridResults(query, semanticResults, keywordResults, options) {
8657
8578
  return cached;
8658
8579
  }
8659
8580
  }
8660
- const overfetchLimit = Math.max(options.limit * 4, options.limit);
8581
+ const overfetchFactor = prioritizeSourcePaths ? 12 : 4;
8582
+ const overfetchLimit = Math.max(options.limit * overfetchFactor, options.limit);
8661
8583
  const fused = options.fusionStrategy === "rrf" ? fuseResultsRrf(semanticResults, keywordResults, options.rrfK, overfetchLimit) : fuseResultsWeighted(semanticResults, keywordResults, options.hybridWeight, overfetchLimit);
8662
8584
  const rerankPoolLimit = Math.max(overfetchLimit, options.rerankTopN * 3, options.limit * 6);
8663
8585
  const rerankPool = fused.slice(0, rerankPoolLimit);
@@ -10168,7 +10090,6 @@ var Indexer = class _Indexer {
10168
10090
  database = null;
10169
10091
  provider = null;
10170
10092
  configuredProviderInfo = null;
10171
- reranker = null;
10172
10093
  fileHashCache = /* @__PURE__ */ new Map();
10173
10094
  fileHashCachePath = "";
10174
10095
  failedBatchesPath = "";
@@ -10328,7 +10249,6 @@ var Indexer = class _Indexer {
10328
10249
  this.database = null;
10329
10250
  this.provider = null;
10330
10251
  this.configuredProviderInfo = null;
10331
- this.reranker = null;
10332
10252
  this.indexCompatibility = null;
10333
10253
  this.initializationMode = "none";
10334
10254
  this.readIssues = [];
@@ -11058,7 +10978,7 @@ var Indexer = class _Indexer {
11058
10978
  await options.queue.onSizeLessThan(Math.max(1, options.providerRateLimits.concurrency));
11059
10979
  const task = options.queue.add(async () => {
11060
10980
  if (options.rateLimitState.backoffMs > 0) {
11061
- await new Promise((resolve15) => setTimeout(resolve15, options.rateLimitState.backoffMs));
10981
+ await new Promise((resolve17) => setTimeout(resolve17, options.rateLimitState.backoffMs));
11062
10982
  }
11063
10983
  try {
11064
10984
  const embeddingResult = await pRetry(
@@ -11625,15 +11545,6 @@ var Indexer = class _Indexer {
11625
11545
  rerankerEnabled: this.config.reranker?.enabled ?? false
11626
11546
  });
11627
11547
  this.provider = createEmbeddingProvider(this.configuredProviderInfo);
11628
- if (this.config.reranker?.enabled) {
11629
- this.reranker = createReranker(this.config.reranker);
11630
- if (this.reranker.isAvailable()) {
11631
- this.logger.info("Reranker initialized", {
11632
- model: this.config.reranker.model,
11633
- baseUrl: this.config.reranker.baseUrl
11634
- });
11635
- }
11636
- }
11637
11548
  const dimensions = this.configuredProviderInfo.modelInfo.dimensions;
11638
11549
  const storePath = path19.join(this.indexPath, "vectors");
11639
11550
  const vectorMetadataPath = `${storePath}.meta.json`;
@@ -13038,6 +12949,7 @@ var Indexer = class _Indexer {
13038
12949
  const filterByBranch = options?.filterByBranch ?? true;
13039
12950
  const sourceIntent = options?.definitionIntent === true || classifyQueryIntentRaw(query) === "source";
13040
12951
  const identifierHints = extractIdentifierHints(query);
12952
+ const candidateLimit = maxResults * (sourceIntent ? 12 : 4);
13041
12953
  this.logger.search("debug", "Starting search", {
13042
12954
  query,
13043
12955
  maxResults,
@@ -13074,7 +12986,7 @@ var Indexer = class _Indexer {
13074
12986
  const semanticCandidates = embedding ? this.searchSemanticCandidates(
13075
12987
  store,
13076
12988
  embedding,
13077
- maxResults * 4,
12989
+ candidateLimit,
13078
12990
  branchChunkIds,
13079
12991
  shouldPrefilterByBranch
13080
12992
  ) : [];
@@ -13082,7 +12994,7 @@ var Indexer = class _Indexer {
13082
12994
  const keywordStartTime = performance2.now();
13083
12995
  const keywordCandidates = await this.keywordSearch(
13084
12996
  query,
13085
- maxResults * 4,
12997
+ candidateLimit,
13086
12998
  store,
13087
12999
  invertedIndex,
13088
13000
  branchChunkIds,
@@ -13840,9 +13752,9 @@ var Indexer = class _Indexer {
13840
13752
  this.requireReadableComponents(readIssues, "database");
13841
13753
  let shortest = [];
13842
13754
  for (const branchKey of this.getBranchCatalogKeys()) {
13843
- const path28 = database.findShortestPath(fromName, toName, branchKey, maxDepth);
13844
- if (path28.length > 0 && (shortest.length === 0 || path28.length < shortest.length)) {
13845
- shortest = path28;
13755
+ const path30 = database.findShortestPath(fromName, toName, branchKey, maxDepth);
13756
+ if (path30.length > 0 && (shortest.length === 0 || path30.length < shortest.length)) {
13757
+ shortest = path30;
13846
13758
  }
13847
13759
  }
13848
13760
  return shortest.map((hop) => this.resolveFilePathRecord(hop));
@@ -13890,13 +13802,13 @@ var Indexer = class _Indexer {
13890
13802
  }
13891
13803
  }
13892
13804
  if (!found) continue;
13893
- const path28 = [];
13805
+ const path30 = [];
13894
13806
  let currentSymbolId = toSymbolId;
13895
13807
  while (true) {
13896
13808
  const symbol = symbolsById.get(currentSymbolId);
13897
13809
  if (!symbol) break;
13898
13810
  const parent = parentBySymbolId.get(currentSymbolId);
13899
- path28.push({
13811
+ path30.push({
13900
13812
  symbolId: symbol.id,
13901
13813
  symbolName: symbol.name,
13902
13814
  filePath: symbol.filePath,
@@ -13906,9 +13818,9 @@ var Indexer = class _Indexer {
13906
13818
  if (!parent) break;
13907
13819
  currentSymbolId = parent.parentId;
13908
13820
  }
13909
- path28.reverse();
13910
- if (path28.length > 0 && (shortest.length === 0 || path28.length < shortest.length)) {
13911
- shortest = path28;
13821
+ path30.reverse();
13822
+ if (path30.length > 0 && (shortest.length === 0 || path30.length < shortest.length)) {
13823
+ shortest = path30;
13912
13824
  }
13913
13825
  }
13914
13826
  return shortest.map((hop) => this.resolveFilePathRecord(hop));
@@ -14244,7 +14156,6 @@ var Indexer = class _Indexer {
14244
14156
  this.store = null;
14245
14157
  this.invertedIndex = null;
14246
14158
  this.provider = null;
14247
- this.reranker = null;
14248
14159
  this.configuredProviderInfo = null;
14249
14160
  this.indexCompatibility = null;
14250
14161
  this.initializationMode = "none";
@@ -14569,12 +14480,12 @@ async function getCallGraphPath(projectRoot, host, from, to, maxDepth, fromFileP
14569
14480
  if (fromResolution.status !== "resolved" || toResolution.status !== "resolved") {
14570
14481
  return { from: fromResolution, to: toResolution, path: [] };
14571
14482
  }
14572
- const path28 = await indexer.findCallPathBySymbolIds(
14483
+ const path30 = await indexer.findCallPathBySymbolIds(
14573
14484
  fromResolution.symbolId,
14574
14485
  toResolution.symbolId,
14575
14486
  maxDepth
14576
14487
  );
14577
- return { from: fromResolution, to: toResolution, path: path28 };
14488
+ return { from: fromResolution, to: toResolution, path: path30 };
14578
14489
  }
14579
14490
  async function runIndexCodebase(projectRoot, host, args, onProgress) {
14580
14491
  const root = getProjectRoot(projectRoot, host);
@@ -14800,8 +14711,8 @@ function addKnowledgeBase(projectRoot, host, knowledgeBasePath) {
14800
14711
  }
14801
14712
  }
14802
14713
  try {
14803
- const stat4 = statSync5(normalizedPath2);
14804
- if (!stat4.isDirectory()) {
14714
+ const stat5 = statSync5(normalizedPath2);
14715
+ if (!stat5.isDirectory()) {
14805
14716
  return `Error: Path is not a directory: ${normalizedPath2}`;
14806
14717
  }
14807
14718
  } catch (error) {
@@ -14849,8 +14760,8 @@ function listKnowledgeBases(projectRoot, host) {
14849
14760
  `;
14850
14761
  if (exists) {
14851
14762
  try {
14852
- const stat4 = statSync5(resolvedPath);
14853
- result += ` Type: ${stat4.isDirectory() ? "Directory" : "File"}
14763
+ const stat5 = statSync5(resolvedPath);
14764
+ result += ` Type: ${stat5.isDirectory() ? "Directory" : "File"}
14854
14765
  `;
14855
14766
  } catch {
14856
14767
  }
@@ -14891,7 +14802,7 @@ Run /index to rebuild the index without the removed knowledge base.`;
14891
14802
  }
14892
14803
 
14893
14804
  // src/watcher/file-watcher.ts
14894
- import { existsSync as existsSync13 } from "fs";
14805
+ import { existsSync as existsSync13, statSync as statSync6 } from "fs";
14895
14806
 
14896
14807
  // node_modules/chokidar/index.js
14897
14808
  import { EventEmitter as EventEmitter2 } from "events";
@@ -14983,7 +14894,7 @@ var ReaddirpStream = class extends Readable {
14983
14894
  this._directoryFilter = normalizeFilter(opts.directoryFilter);
14984
14895
  const statMethod = opts.lstat ? lstat : stat;
14985
14896
  if (wantBigintFsStats) {
14986
- this._stat = (path28) => statMethod(path28, { bigint: true });
14897
+ this._stat = (path30) => statMethod(path30, { bigint: true });
14987
14898
  } else {
14988
14899
  this._stat = statMethod;
14989
14900
  }
@@ -15008,8 +14919,8 @@ var ReaddirpStream = class extends Readable {
15008
14919
  const par = this.parent;
15009
14920
  const fil = par && par.files;
15010
14921
  if (fil && fil.length > 0) {
15011
- const { path: path28, depth } = par;
15012
- const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path28));
14922
+ const { path: path30, depth } = par;
14923
+ const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path30));
15013
14924
  const awaited = await Promise.all(slice);
15014
14925
  for (const entry of awaited) {
15015
14926
  if (!entry)
@@ -15049,20 +14960,20 @@ var ReaddirpStream = class extends Readable {
15049
14960
  this.reading = false;
15050
14961
  }
15051
14962
  }
15052
- async _exploreDir(path28, depth) {
14963
+ async _exploreDir(path30, depth) {
15053
14964
  let files;
15054
14965
  try {
15055
- files = await readdir(path28, this._rdOptions);
14966
+ files = await readdir(path30, this._rdOptions);
15056
14967
  } catch (error) {
15057
14968
  this._onError(error);
15058
14969
  }
15059
- return { files, depth, path: path28 };
14970
+ return { files, depth, path: path30 };
15060
14971
  }
15061
- async _formatEntry(dirent, path28) {
14972
+ async _formatEntry(dirent, path30) {
15062
14973
  let entry;
15063
14974
  const basename9 = this._isDirent ? dirent.name : dirent;
15064
14975
  try {
15065
- const fullPath = presolve(pjoin(path28, basename9));
14976
+ const fullPath = presolve(pjoin(path30, basename9));
15066
14977
  entry = { path: prelative(this._root, fullPath), fullPath, basename: basename9 };
15067
14978
  entry[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath);
15068
14979
  } catch (err) {
@@ -15462,16 +15373,16 @@ var delFromSet = (main, prop, item) => {
15462
15373
  };
15463
15374
  var isEmptySet = (val) => val instanceof Set ? val.size === 0 : !val;
15464
15375
  var FsWatchInstances = /* @__PURE__ */ new Map();
15465
- function createFsWatchInstance(path28, options, listener, errHandler, emitRaw) {
15376
+ function createFsWatchInstance(path30, options, listener, errHandler, emitRaw) {
15466
15377
  const handleEvent = (rawEvent, evPath) => {
15467
- listener(path28);
15468
- emitRaw(rawEvent, evPath, { watchedPath: path28 });
15469
- if (evPath && path28 !== evPath) {
15470
- fsWatchBroadcast(sp.resolve(path28, evPath), KEY_LISTENERS, sp.join(path28, evPath));
15378
+ listener(path30);
15379
+ emitRaw(rawEvent, evPath, { watchedPath: path30 });
15380
+ if (evPath && path30 !== evPath) {
15381
+ fsWatchBroadcast(sp.resolve(path30, evPath), KEY_LISTENERS, sp.join(path30, evPath));
15471
15382
  }
15472
15383
  };
15473
15384
  try {
15474
- return fs_watch(path28, {
15385
+ return fs_watch(path30, {
15475
15386
  persistent: options.persistent
15476
15387
  }, handleEvent);
15477
15388
  } catch (error) {
@@ -15487,12 +15398,12 @@ var fsWatchBroadcast = (fullPath, listenerType, val1, val2, val3) => {
15487
15398
  listener(val1, val2, val3);
15488
15399
  });
15489
15400
  };
15490
- var setFsWatchListener = (path28, fullPath, options, handlers) => {
15401
+ var setFsWatchListener = (path30, fullPath, options, handlers) => {
15491
15402
  const { listener, errHandler, rawEmitter } = handlers;
15492
15403
  let cont = FsWatchInstances.get(fullPath);
15493
15404
  let watcher;
15494
15405
  if (!options.persistent) {
15495
- watcher = createFsWatchInstance(path28, options, listener, errHandler, rawEmitter);
15406
+ watcher = createFsWatchInstance(path30, options, listener, errHandler, rawEmitter);
15496
15407
  if (!watcher)
15497
15408
  return;
15498
15409
  return watcher.close.bind(watcher);
@@ -15503,7 +15414,7 @@ var setFsWatchListener = (path28, fullPath, options, handlers) => {
15503
15414
  addAndConvert(cont, KEY_RAW, rawEmitter);
15504
15415
  } else {
15505
15416
  watcher = createFsWatchInstance(
15506
- path28,
15417
+ path30,
15507
15418
  options,
15508
15419
  fsWatchBroadcast.bind(null, fullPath, KEY_LISTENERS),
15509
15420
  errHandler,
@@ -15518,7 +15429,7 @@ var setFsWatchListener = (path28, fullPath, options, handlers) => {
15518
15429
  cont.watcherUnusable = true;
15519
15430
  if (isWindows && error.code === "EPERM") {
15520
15431
  try {
15521
- const fd = await open(path28, "r");
15432
+ const fd = await open(path30, "r");
15522
15433
  await fd.close();
15523
15434
  broadcastErr(error);
15524
15435
  } catch (err) {
@@ -15549,7 +15460,7 @@ var setFsWatchListener = (path28, fullPath, options, handlers) => {
15549
15460
  };
15550
15461
  };
15551
15462
  var FsWatchFileInstances = /* @__PURE__ */ new Map();
15552
- var setFsWatchFileListener = (path28, fullPath, options, handlers) => {
15463
+ var setFsWatchFileListener = (path30, fullPath, options, handlers) => {
15553
15464
  const { listener, rawEmitter } = handlers;
15554
15465
  let cont = FsWatchFileInstances.get(fullPath);
15555
15466
  const copts = cont && cont.options;
@@ -15571,7 +15482,7 @@ var setFsWatchFileListener = (path28, fullPath, options, handlers) => {
15571
15482
  });
15572
15483
  const currmtime = curr.mtimeMs;
15573
15484
  if (curr.size !== prev.size || currmtime > prev.mtimeMs || currmtime === 0) {
15574
- foreach(cont.listeners, (listener2) => listener2(path28, curr));
15485
+ foreach(cont.listeners, (listener2) => listener2(path30, curr));
15575
15486
  }
15576
15487
  })
15577
15488
  };
@@ -15601,13 +15512,13 @@ var NodeFsHandler = class {
15601
15512
  * @param listener on fs change
15602
15513
  * @returns closer for the watcher instance
15603
15514
  */
15604
- _watchWithNodeFs(path28, listener) {
15515
+ _watchWithNodeFs(path30, listener) {
15605
15516
  const opts = this.fsw.options;
15606
- const directory = sp.dirname(path28);
15607
- const basename9 = sp.basename(path28);
15517
+ const directory = sp.dirname(path30);
15518
+ const basename9 = sp.basename(path30);
15608
15519
  const parent = this.fsw._getWatchedDir(directory);
15609
15520
  parent.add(basename9);
15610
- const absolutePath = sp.resolve(path28);
15521
+ const absolutePath = sp.resolve(path30);
15611
15522
  const options = {
15612
15523
  persistent: opts.persistent
15613
15524
  };
@@ -15617,12 +15528,12 @@ var NodeFsHandler = class {
15617
15528
  if (opts.usePolling) {
15618
15529
  const enableBin = opts.interval !== opts.binaryInterval;
15619
15530
  options.interval = enableBin && isBinaryPath(basename9) ? opts.binaryInterval : opts.interval;
15620
- closer = setFsWatchFileListener(path28, absolutePath, options, {
15531
+ closer = setFsWatchFileListener(path30, absolutePath, options, {
15621
15532
  listener,
15622
15533
  rawEmitter: this.fsw._emitRaw
15623
15534
  });
15624
15535
  } else {
15625
- closer = setFsWatchListener(path28, absolutePath, options, {
15536
+ closer = setFsWatchListener(path30, absolutePath, options, {
15626
15537
  listener,
15627
15538
  errHandler: this._boundHandleError,
15628
15539
  rawEmitter: this.fsw._emitRaw
@@ -15644,7 +15555,7 @@ var NodeFsHandler = class {
15644
15555
  let prevStats = stats;
15645
15556
  if (parent.has(basename9))
15646
15557
  return;
15647
- const listener = async (path28, newStats) => {
15558
+ const listener = async (path30, newStats) => {
15648
15559
  if (!this.fsw._throttle(THROTTLE_MODE_WATCH, file, 5))
15649
15560
  return;
15650
15561
  if (!newStats || newStats.mtimeMs === 0) {
@@ -15658,11 +15569,11 @@ var NodeFsHandler = class {
15658
15569
  this.fsw._emit(EV.CHANGE, file, newStats2);
15659
15570
  }
15660
15571
  if ((isMacos || isLinux || isFreeBSD) && prevStats.ino !== newStats2.ino) {
15661
- this.fsw._closeFile(path28);
15572
+ this.fsw._closeFile(path30);
15662
15573
  prevStats = newStats2;
15663
15574
  const closer2 = this._watchWithNodeFs(file, listener);
15664
15575
  if (closer2)
15665
- this.fsw._addPathCloser(path28, closer2);
15576
+ this.fsw._addPathCloser(path30, closer2);
15666
15577
  } else {
15667
15578
  prevStats = newStats2;
15668
15579
  }
@@ -15694,7 +15605,7 @@ var NodeFsHandler = class {
15694
15605
  * @param item basename of this item
15695
15606
  * @returns true if no more processing is needed for this entry.
15696
15607
  */
15697
- async _handleSymlink(entry, directory, path28, item) {
15608
+ async _handleSymlink(entry, directory, path30, item) {
15698
15609
  if (this.fsw.closed) {
15699
15610
  return;
15700
15611
  }
@@ -15704,7 +15615,7 @@ var NodeFsHandler = class {
15704
15615
  this.fsw._incrReadyCount();
15705
15616
  let linkPath;
15706
15617
  try {
15707
- linkPath = await fsrealpath(path28);
15618
+ linkPath = await fsrealpath(path30);
15708
15619
  } catch (e) {
15709
15620
  this.fsw._emitReady();
15710
15621
  return true;
@@ -15714,12 +15625,12 @@ var NodeFsHandler = class {
15714
15625
  if (dir.has(item)) {
15715
15626
  if (this.fsw._symlinkPaths.get(full) !== linkPath) {
15716
15627
  this.fsw._symlinkPaths.set(full, linkPath);
15717
- this.fsw._emit(EV.CHANGE, path28, entry.stats);
15628
+ this.fsw._emit(EV.CHANGE, path30, entry.stats);
15718
15629
  }
15719
15630
  } else {
15720
15631
  dir.add(item);
15721
15632
  this.fsw._symlinkPaths.set(full, linkPath);
15722
- this.fsw._emit(EV.ADD, path28, entry.stats);
15633
+ this.fsw._emit(EV.ADD, path30, entry.stats);
15723
15634
  }
15724
15635
  this.fsw._emitReady();
15725
15636
  return true;
@@ -15749,9 +15660,9 @@ var NodeFsHandler = class {
15749
15660
  return;
15750
15661
  }
15751
15662
  const item = entry.path;
15752
- let path28 = sp.join(directory, item);
15663
+ let path30 = sp.join(directory, item);
15753
15664
  current.add(item);
15754
- if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path28, item)) {
15665
+ if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path30, item)) {
15755
15666
  return;
15756
15667
  }
15757
15668
  if (this.fsw.closed) {
@@ -15760,11 +15671,11 @@ var NodeFsHandler = class {
15760
15671
  }
15761
15672
  if (item === target || !target && !previous.has(item)) {
15762
15673
  this.fsw._incrReadyCount();
15763
- path28 = sp.join(dir, sp.relative(dir, path28));
15764
- this._addToNodeFs(path28, initialAdd, wh, depth + 1);
15674
+ path30 = sp.join(dir, sp.relative(dir, path30));
15675
+ this._addToNodeFs(path30, initialAdd, wh, depth + 1);
15765
15676
  }
15766
15677
  }).on(EV.ERROR, this._boundHandleError);
15767
- return new Promise((resolve15, reject) => {
15678
+ return new Promise((resolve17, reject) => {
15768
15679
  if (!stream)
15769
15680
  return reject();
15770
15681
  stream.once(STR_END, () => {
@@ -15773,7 +15684,7 @@ var NodeFsHandler = class {
15773
15684
  return;
15774
15685
  }
15775
15686
  const wasThrottled = throttler ? throttler.clear() : false;
15776
- resolve15(void 0);
15687
+ resolve17(void 0);
15777
15688
  previous.getChildren().filter((item) => {
15778
15689
  return item !== directory && !current.has(item);
15779
15690
  }).forEach((item) => {
@@ -15830,13 +15741,13 @@ var NodeFsHandler = class {
15830
15741
  * @param depth Child path actually targeted for watch
15831
15742
  * @param target Child path actually targeted for watch
15832
15743
  */
15833
- async _addToNodeFs(path28, initialAdd, priorWh, depth, target) {
15744
+ async _addToNodeFs(path30, initialAdd, priorWh, depth, target) {
15834
15745
  const ready = this.fsw._emitReady;
15835
- if (this.fsw._isIgnored(path28) || this.fsw.closed) {
15746
+ if (this.fsw._isIgnored(path30) || this.fsw.closed) {
15836
15747
  ready();
15837
15748
  return false;
15838
15749
  }
15839
- const wh = this.fsw._getWatchHelpers(path28);
15750
+ const wh = this.fsw._getWatchHelpers(path30);
15840
15751
  if (priorWh) {
15841
15752
  wh.filterPath = (entry) => priorWh.filterPath(entry);
15842
15753
  wh.filterDir = (entry) => priorWh.filterDir(entry);
@@ -15852,8 +15763,8 @@ var NodeFsHandler = class {
15852
15763
  const follow = this.fsw.options.followSymlinks;
15853
15764
  let closer;
15854
15765
  if (stats.isDirectory()) {
15855
- const absPath = sp.resolve(path28);
15856
- const targetPath = follow ? await fsrealpath(path28) : path28;
15766
+ const absPath = sp.resolve(path30);
15767
+ const targetPath = follow ? await fsrealpath(path30) : path30;
15857
15768
  if (this.fsw.closed)
15858
15769
  return;
15859
15770
  closer = await this._handleDir(wh.watchPath, stats, initialAdd, depth, target, wh, targetPath);
@@ -15863,29 +15774,29 @@ var NodeFsHandler = class {
15863
15774
  this.fsw._symlinkPaths.set(absPath, targetPath);
15864
15775
  }
15865
15776
  } else if (stats.isSymbolicLink()) {
15866
- const targetPath = follow ? await fsrealpath(path28) : path28;
15777
+ const targetPath = follow ? await fsrealpath(path30) : path30;
15867
15778
  if (this.fsw.closed)
15868
15779
  return;
15869
15780
  const parent = sp.dirname(wh.watchPath);
15870
15781
  this.fsw._getWatchedDir(parent).add(wh.watchPath);
15871
15782
  this.fsw._emit(EV.ADD, wh.watchPath, stats);
15872
- closer = await this._handleDir(parent, stats, initialAdd, depth, path28, wh, targetPath);
15783
+ closer = await this._handleDir(parent, stats, initialAdd, depth, path30, wh, targetPath);
15873
15784
  if (this.fsw.closed)
15874
15785
  return;
15875
15786
  if (targetPath !== void 0) {
15876
- this.fsw._symlinkPaths.set(sp.resolve(path28), targetPath);
15787
+ this.fsw._symlinkPaths.set(sp.resolve(path30), targetPath);
15877
15788
  }
15878
15789
  } else {
15879
15790
  closer = this._handleFile(wh.watchPath, stats, initialAdd);
15880
15791
  }
15881
15792
  ready();
15882
15793
  if (closer)
15883
- this.fsw._addPathCloser(path28, closer);
15794
+ this.fsw._addPathCloser(path30, closer);
15884
15795
  return false;
15885
15796
  } catch (error) {
15886
15797
  if (this.fsw._handleError(error)) {
15887
15798
  ready();
15888
- return path28;
15799
+ return path30;
15889
15800
  }
15890
15801
  }
15891
15802
  }
@@ -15917,35 +15828,35 @@ function createPattern(matcher) {
15917
15828
  if (matcher.path === string)
15918
15829
  return true;
15919
15830
  if (matcher.recursive) {
15920
- const relative12 = sp2.relative(matcher.path, string);
15921
- if (!relative12) {
15831
+ const relative14 = sp2.relative(matcher.path, string);
15832
+ if (!relative14) {
15922
15833
  return false;
15923
15834
  }
15924
- return !relative12.startsWith("..") && !sp2.isAbsolute(relative12);
15835
+ return !relative14.startsWith("..") && !sp2.isAbsolute(relative14);
15925
15836
  }
15926
15837
  return false;
15927
15838
  };
15928
15839
  }
15929
15840
  return () => false;
15930
15841
  }
15931
- function normalizePath2(path28) {
15932
- if (typeof path28 !== "string")
15842
+ function normalizePath2(path30) {
15843
+ if (typeof path30 !== "string")
15933
15844
  throw new Error("string expected");
15934
- path28 = sp2.normalize(path28);
15935
- path28 = path28.replace(/\\/g, "/");
15845
+ path30 = sp2.normalize(path30);
15846
+ path30 = path30.replace(/\\/g, "/");
15936
15847
  let prepend = false;
15937
- if (path28.startsWith("//"))
15848
+ if (path30.startsWith("//"))
15938
15849
  prepend = true;
15939
- path28 = path28.replace(DOUBLE_SLASH_RE, "/");
15850
+ path30 = path30.replace(DOUBLE_SLASH_RE, "/");
15940
15851
  if (prepend)
15941
- path28 = "/" + path28;
15942
- return path28;
15852
+ path30 = "/" + path30;
15853
+ return path30;
15943
15854
  }
15944
15855
  function matchPatterns(patterns, testString, stats) {
15945
- const path28 = normalizePath2(testString);
15856
+ const path30 = normalizePath2(testString);
15946
15857
  for (let index = 0; index < patterns.length; index++) {
15947
15858
  const pattern = patterns[index];
15948
- if (pattern(path28, stats)) {
15859
+ if (pattern(path30, stats)) {
15949
15860
  return true;
15950
15861
  }
15951
15862
  }
@@ -15983,19 +15894,19 @@ var toUnix = (string) => {
15983
15894
  }
15984
15895
  return str;
15985
15896
  };
15986
- var normalizePathToUnix = (path28) => toUnix(sp2.normalize(toUnix(path28)));
15987
- var normalizeIgnored = (cwd = "") => (path28) => {
15988
- if (typeof path28 === "string") {
15989
- return normalizePathToUnix(sp2.isAbsolute(path28) ? path28 : sp2.join(cwd, path28));
15897
+ var normalizePathToUnix = (path30) => toUnix(sp2.normalize(toUnix(path30)));
15898
+ var normalizeIgnored = (cwd = "") => (path30) => {
15899
+ if (typeof path30 === "string") {
15900
+ return normalizePathToUnix(sp2.isAbsolute(path30) ? path30 : sp2.join(cwd, path30));
15990
15901
  } else {
15991
- return path28;
15902
+ return path30;
15992
15903
  }
15993
15904
  };
15994
- var getAbsolutePath = (path28, cwd) => {
15995
- if (sp2.isAbsolute(path28)) {
15996
- return path28;
15905
+ var getAbsolutePath = (path30, cwd) => {
15906
+ if (sp2.isAbsolute(path30)) {
15907
+ return path30;
15997
15908
  }
15998
- return sp2.join(cwd, path28);
15909
+ return sp2.join(cwd, path30);
15999
15910
  };
16000
15911
  var EMPTY_SET = Object.freeze(/* @__PURE__ */ new Set());
16001
15912
  var DirEntry = class {
@@ -16060,10 +15971,10 @@ var WatchHelper = class {
16060
15971
  dirParts;
16061
15972
  followSymlinks;
16062
15973
  statMethod;
16063
- constructor(path28, follow, fsw) {
15974
+ constructor(path30, follow, fsw) {
16064
15975
  this.fsw = fsw;
16065
- const watchPath = path28;
16066
- this.path = path28 = path28.replace(REPLACER_RE, "");
15976
+ const watchPath = path30;
15977
+ this.path = path30 = path30.replace(REPLACER_RE, "");
16067
15978
  this.watchPath = watchPath;
16068
15979
  this.fullWatchPath = sp2.resolve(watchPath);
16069
15980
  this.dirParts = [];
@@ -16203,20 +16114,20 @@ var FSWatcher = class extends EventEmitter2 {
16203
16114
  this._closePromise = void 0;
16204
16115
  let paths = unifyPaths(paths_);
16205
16116
  if (cwd) {
16206
- paths = paths.map((path28) => {
16207
- const absPath = getAbsolutePath(path28, cwd);
16117
+ paths = paths.map((path30) => {
16118
+ const absPath = getAbsolutePath(path30, cwd);
16208
16119
  return absPath;
16209
16120
  });
16210
16121
  }
16211
- paths.forEach((path28) => {
16212
- this._removeIgnoredPath(path28);
16122
+ paths.forEach((path30) => {
16123
+ this._removeIgnoredPath(path30);
16213
16124
  });
16214
16125
  this._userIgnored = void 0;
16215
16126
  if (!this._readyCount)
16216
16127
  this._readyCount = 0;
16217
16128
  this._readyCount += paths.length;
16218
- Promise.all(paths.map(async (path28) => {
16219
- const res = await this._nodeFsHandler._addToNodeFs(path28, !_internal, void 0, 0, _origAdd);
16129
+ Promise.all(paths.map(async (path30) => {
16130
+ const res = await this._nodeFsHandler._addToNodeFs(path30, !_internal, void 0, 0, _origAdd);
16220
16131
  if (res)
16221
16132
  this._emitReady();
16222
16133
  return res;
@@ -16238,17 +16149,17 @@ var FSWatcher = class extends EventEmitter2 {
16238
16149
  return this;
16239
16150
  const paths = unifyPaths(paths_);
16240
16151
  const { cwd } = this.options;
16241
- paths.forEach((path28) => {
16242
- if (!sp2.isAbsolute(path28) && !this._closers.has(path28)) {
16152
+ paths.forEach((path30) => {
16153
+ if (!sp2.isAbsolute(path30) && !this._closers.has(path30)) {
16243
16154
  if (cwd)
16244
- path28 = sp2.join(cwd, path28);
16245
- path28 = sp2.resolve(path28);
16155
+ path30 = sp2.join(cwd, path30);
16156
+ path30 = sp2.resolve(path30);
16246
16157
  }
16247
- this._closePath(path28);
16248
- this._addIgnoredPath(path28);
16249
- if (this._watched.has(path28)) {
16158
+ this._closePath(path30);
16159
+ this._addIgnoredPath(path30);
16160
+ if (this._watched.has(path30)) {
16250
16161
  this._addIgnoredPath({
16251
- path: path28,
16162
+ path: path30,
16252
16163
  recursive: true
16253
16164
  });
16254
16165
  }
@@ -16312,38 +16223,38 @@ var FSWatcher = class extends EventEmitter2 {
16312
16223
  * @param stats arguments to be passed with event
16313
16224
  * @returns the error if defined, otherwise the value of the FSWatcher instance's `closed` flag
16314
16225
  */
16315
- async _emit(event, path28, stats) {
16226
+ async _emit(event, path30, stats) {
16316
16227
  if (this.closed)
16317
16228
  return;
16318
16229
  const opts = this.options;
16319
16230
  if (isWindows)
16320
- path28 = sp2.normalize(path28);
16231
+ path30 = sp2.normalize(path30);
16321
16232
  if (opts.cwd)
16322
- path28 = sp2.relative(opts.cwd, path28);
16323
- const args = [path28];
16233
+ path30 = sp2.relative(opts.cwd, path30);
16234
+ const args = [path30];
16324
16235
  if (stats != null)
16325
16236
  args.push(stats);
16326
16237
  const awf = opts.awaitWriteFinish;
16327
16238
  let pw;
16328
- if (awf && (pw = this._pendingWrites.get(path28))) {
16239
+ if (awf && (pw = this._pendingWrites.get(path30))) {
16329
16240
  pw.lastChange = /* @__PURE__ */ new Date();
16330
16241
  return this;
16331
16242
  }
16332
16243
  if (opts.atomic) {
16333
16244
  if (event === EVENTS.UNLINK) {
16334
- this._pendingUnlinks.set(path28, [event, ...args]);
16245
+ this._pendingUnlinks.set(path30, [event, ...args]);
16335
16246
  setTimeout(() => {
16336
- this._pendingUnlinks.forEach((entry, path29) => {
16247
+ this._pendingUnlinks.forEach((entry, path31) => {
16337
16248
  this.emit(...entry);
16338
16249
  this.emit(EVENTS.ALL, ...entry);
16339
- this._pendingUnlinks.delete(path29);
16250
+ this._pendingUnlinks.delete(path31);
16340
16251
  });
16341
16252
  }, typeof opts.atomic === "number" ? opts.atomic : 100);
16342
16253
  return this;
16343
16254
  }
16344
- if (event === EVENTS.ADD && this._pendingUnlinks.has(path28)) {
16255
+ if (event === EVENTS.ADD && this._pendingUnlinks.has(path30)) {
16345
16256
  event = EVENTS.CHANGE;
16346
- this._pendingUnlinks.delete(path28);
16257
+ this._pendingUnlinks.delete(path30);
16347
16258
  }
16348
16259
  }
16349
16260
  if (awf && (event === EVENTS.ADD || event === EVENTS.CHANGE) && this._readyEmitted) {
@@ -16361,16 +16272,16 @@ var FSWatcher = class extends EventEmitter2 {
16361
16272
  this.emitWithAll(event, args);
16362
16273
  }
16363
16274
  };
16364
- this._awaitWriteFinish(path28, awf.stabilityThreshold, event, awfEmit);
16275
+ this._awaitWriteFinish(path30, awf.stabilityThreshold, event, awfEmit);
16365
16276
  return this;
16366
16277
  }
16367
16278
  if (event === EVENTS.CHANGE) {
16368
- const isThrottled = !this._throttle(EVENTS.CHANGE, path28, 50);
16279
+ const isThrottled = !this._throttle(EVENTS.CHANGE, path30, 50);
16369
16280
  if (isThrottled)
16370
16281
  return this;
16371
16282
  }
16372
16283
  if (opts.alwaysStat && stats === void 0 && (event === EVENTS.ADD || event === EVENTS.ADD_DIR || event === EVENTS.CHANGE)) {
16373
- const fullPath = opts.cwd ? sp2.join(opts.cwd, path28) : path28;
16284
+ const fullPath = opts.cwd ? sp2.join(opts.cwd, path30) : path30;
16374
16285
  let stats2;
16375
16286
  try {
16376
16287
  stats2 = await stat3(fullPath);
@@ -16401,23 +16312,23 @@ var FSWatcher = class extends EventEmitter2 {
16401
16312
  * @param timeout duration of time to suppress duplicate actions
16402
16313
  * @returns tracking object or false if action should be suppressed
16403
16314
  */
16404
- _throttle(actionType, path28, timeout) {
16315
+ _throttle(actionType, path30, timeout) {
16405
16316
  if (!this._throttled.has(actionType)) {
16406
16317
  this._throttled.set(actionType, /* @__PURE__ */ new Map());
16407
16318
  }
16408
16319
  const action = this._throttled.get(actionType);
16409
16320
  if (!action)
16410
16321
  throw new Error("invalid throttle");
16411
- const actionPath = action.get(path28);
16322
+ const actionPath = action.get(path30);
16412
16323
  if (actionPath) {
16413
16324
  actionPath.count++;
16414
16325
  return false;
16415
16326
  }
16416
16327
  let timeoutObject;
16417
16328
  const clear = () => {
16418
- const item = action.get(path28);
16329
+ const item = action.get(path30);
16419
16330
  const count = item ? item.count : 0;
16420
- action.delete(path28);
16331
+ action.delete(path30);
16421
16332
  clearTimeout(timeoutObject);
16422
16333
  if (item)
16423
16334
  clearTimeout(item.timeoutObject);
@@ -16425,7 +16336,7 @@ var FSWatcher = class extends EventEmitter2 {
16425
16336
  };
16426
16337
  timeoutObject = setTimeout(clear, timeout);
16427
16338
  const thr = { timeoutObject, clear, count: 0 };
16428
- action.set(path28, thr);
16339
+ action.set(path30, thr);
16429
16340
  return thr;
16430
16341
  }
16431
16342
  _incrReadyCount() {
@@ -16439,44 +16350,44 @@ var FSWatcher = class extends EventEmitter2 {
16439
16350
  * @param event
16440
16351
  * @param awfEmit Callback to be called when ready for event to be emitted.
16441
16352
  */
16442
- _awaitWriteFinish(path28, threshold, event, awfEmit) {
16353
+ _awaitWriteFinish(path30, threshold, event, awfEmit) {
16443
16354
  const awf = this.options.awaitWriteFinish;
16444
16355
  if (typeof awf !== "object")
16445
16356
  return;
16446
16357
  const pollInterval = awf.pollInterval;
16447
16358
  let timeoutHandler;
16448
- let fullPath = path28;
16449
- if (this.options.cwd && !sp2.isAbsolute(path28)) {
16450
- fullPath = sp2.join(this.options.cwd, path28);
16359
+ let fullPath = path30;
16360
+ if (this.options.cwd && !sp2.isAbsolute(path30)) {
16361
+ fullPath = sp2.join(this.options.cwd, path30);
16451
16362
  }
16452
16363
  const now2 = /* @__PURE__ */ new Date();
16453
16364
  const writes = this._pendingWrites;
16454
16365
  function awaitWriteFinishFn(prevStat) {
16455
16366
  statcb(fullPath, (err, curStat) => {
16456
- if (err || !writes.has(path28)) {
16367
+ if (err || !writes.has(path30)) {
16457
16368
  if (err && err.code !== "ENOENT")
16458
16369
  awfEmit(err);
16459
16370
  return;
16460
16371
  }
16461
16372
  const now3 = Number(/* @__PURE__ */ new Date());
16462
16373
  if (prevStat && curStat.size !== prevStat.size) {
16463
- writes.get(path28).lastChange = now3;
16374
+ writes.get(path30).lastChange = now3;
16464
16375
  }
16465
- const pw = writes.get(path28);
16376
+ const pw = writes.get(path30);
16466
16377
  const df = now3 - pw.lastChange;
16467
16378
  if (df >= threshold) {
16468
- writes.delete(path28);
16379
+ writes.delete(path30);
16469
16380
  awfEmit(void 0, curStat);
16470
16381
  } else {
16471
16382
  timeoutHandler = setTimeout(awaitWriteFinishFn, pollInterval, curStat);
16472
16383
  }
16473
16384
  });
16474
16385
  }
16475
- if (!writes.has(path28)) {
16476
- writes.set(path28, {
16386
+ if (!writes.has(path30)) {
16387
+ writes.set(path30, {
16477
16388
  lastChange: now2,
16478
16389
  cancelWait: () => {
16479
- writes.delete(path28);
16390
+ writes.delete(path30);
16480
16391
  clearTimeout(timeoutHandler);
16481
16392
  return event;
16482
16393
  }
@@ -16487,8 +16398,8 @@ var FSWatcher = class extends EventEmitter2 {
16487
16398
  /**
16488
16399
  * Determines whether user has asked to ignore this path.
16489
16400
  */
16490
- _isIgnored(path28, stats) {
16491
- if (this.options.atomic && DOT_RE.test(path28))
16401
+ _isIgnored(path30, stats) {
16402
+ if (this.options.atomic && DOT_RE.test(path30))
16492
16403
  return true;
16493
16404
  if (!this._userIgnored) {
16494
16405
  const { cwd } = this.options;
@@ -16498,17 +16409,17 @@ var FSWatcher = class extends EventEmitter2 {
16498
16409
  const list = [...ignoredPaths.map(normalizeIgnored(cwd)), ...ignored];
16499
16410
  this._userIgnored = anymatch(list, void 0);
16500
16411
  }
16501
- return this._userIgnored(path28, stats);
16412
+ return this._userIgnored(path30, stats);
16502
16413
  }
16503
- _isntIgnored(path28, stat4) {
16504
- return !this._isIgnored(path28, stat4);
16414
+ _isntIgnored(path30, stat5) {
16415
+ return !this._isIgnored(path30, stat5);
16505
16416
  }
16506
16417
  /**
16507
16418
  * Provides a set of common helpers and properties relating to symlink handling.
16508
16419
  * @param path file or directory pattern being watched
16509
16420
  */
16510
- _getWatchHelpers(path28) {
16511
- return new WatchHelper(path28, this.options.followSymlinks, this);
16421
+ _getWatchHelpers(path30) {
16422
+ return new WatchHelper(path30, this.options.followSymlinks, this);
16512
16423
  }
16513
16424
  // Directory helpers
16514
16425
  // -----------------
@@ -16540,63 +16451,63 @@ var FSWatcher = class extends EventEmitter2 {
16540
16451
  * @param item base path of item/directory
16541
16452
  */
16542
16453
  _remove(directory, item, isDirectory) {
16543
- const path28 = sp2.join(directory, item);
16544
- const fullPath = sp2.resolve(path28);
16545
- isDirectory = isDirectory != null ? isDirectory : this._watched.has(path28) || this._watched.has(fullPath);
16546
- if (!this._throttle("remove", path28, 100))
16454
+ const path30 = sp2.join(directory, item);
16455
+ const fullPath = sp2.resolve(path30);
16456
+ isDirectory = isDirectory != null ? isDirectory : this._watched.has(path30) || this._watched.has(fullPath);
16457
+ if (!this._throttle("remove", path30, 100))
16547
16458
  return;
16548
16459
  if (!isDirectory && this._watched.size === 1) {
16549
16460
  this.add(directory, item, true);
16550
16461
  }
16551
- const wp = this._getWatchedDir(path28);
16462
+ const wp = this._getWatchedDir(path30);
16552
16463
  const nestedDirectoryChildren = wp.getChildren();
16553
- nestedDirectoryChildren.forEach((nested) => this._remove(path28, nested));
16464
+ nestedDirectoryChildren.forEach((nested) => this._remove(path30, nested));
16554
16465
  const parent = this._getWatchedDir(directory);
16555
16466
  const wasTracked = parent.has(item);
16556
16467
  parent.remove(item);
16557
16468
  if (this._symlinkPaths.has(fullPath)) {
16558
16469
  this._symlinkPaths.delete(fullPath);
16559
16470
  }
16560
- let relPath = path28;
16471
+ let relPath = path30;
16561
16472
  if (this.options.cwd)
16562
- relPath = sp2.relative(this.options.cwd, path28);
16473
+ relPath = sp2.relative(this.options.cwd, path30);
16563
16474
  if (this.options.awaitWriteFinish && this._pendingWrites.has(relPath)) {
16564
16475
  const event = this._pendingWrites.get(relPath).cancelWait();
16565
16476
  if (event === EVENTS.ADD)
16566
16477
  return;
16567
16478
  }
16568
- this._watched.delete(path28);
16479
+ this._watched.delete(path30);
16569
16480
  this._watched.delete(fullPath);
16570
16481
  const eventName = isDirectory ? EVENTS.UNLINK_DIR : EVENTS.UNLINK;
16571
- if (wasTracked && !this._isIgnored(path28))
16572
- this._emit(eventName, path28);
16573
- this._closePath(path28);
16482
+ if (wasTracked && !this._isIgnored(path30))
16483
+ this._emit(eventName, path30);
16484
+ this._closePath(path30);
16574
16485
  }
16575
16486
  /**
16576
16487
  * Closes all watchers for a path
16577
16488
  */
16578
- _closePath(path28) {
16579
- this._closeFile(path28);
16580
- const dir = sp2.dirname(path28);
16581
- this._getWatchedDir(dir).remove(sp2.basename(path28));
16489
+ _closePath(path30) {
16490
+ this._closeFile(path30);
16491
+ const dir = sp2.dirname(path30);
16492
+ this._getWatchedDir(dir).remove(sp2.basename(path30));
16582
16493
  }
16583
16494
  /**
16584
16495
  * Closes only file-specific watchers
16585
16496
  */
16586
- _closeFile(path28) {
16587
- const closers = this._closers.get(path28);
16497
+ _closeFile(path30) {
16498
+ const closers = this._closers.get(path30);
16588
16499
  if (!closers)
16589
16500
  return;
16590
16501
  closers.forEach((closer) => closer());
16591
- this._closers.delete(path28);
16502
+ this._closers.delete(path30);
16592
16503
  }
16593
- _addPathCloser(path28, closer) {
16504
+ _addPathCloser(path30, closer) {
16594
16505
  if (!closer)
16595
16506
  return;
16596
- let list = this._closers.get(path28);
16507
+ let list = this._closers.get(path30);
16597
16508
  if (!list) {
16598
16509
  list = [];
16599
- this._closers.set(path28, list);
16510
+ this._closers.set(path30, list);
16600
16511
  }
16601
16512
  list.push(closer);
16602
16513
  }
@@ -16626,12 +16537,291 @@ function watch(paths, options = {}) {
16626
16537
  var chokidar_default = { watch, FSWatcher };
16627
16538
 
16628
16539
  // src/watcher/file-watcher.ts
16540
+ import * as path23 from "path";
16541
+
16542
+ // src/watcher/native-recursive-watcher.ts
16543
+ import { watch as watch2 } from "fs";
16629
16544
  import * as path21 from "path";
16545
+ var NativeRecursiveWatcher = class {
16546
+ constructor(root, onChange, options = {}) {
16547
+ this.root = root;
16548
+ this.onChange = onChange;
16549
+ this.watchFactory = options.watchFactory ?? this.defaultWatchFactory;
16550
+ this.onError = options.onError;
16551
+ }
16552
+ root;
16553
+ onChange;
16554
+ watcher = null;
16555
+ listenerToken = 0;
16556
+ watchFactory;
16557
+ onError;
16558
+ start() {
16559
+ if (this.watcher) return;
16560
+ const token = ++this.listenerToken;
16561
+ const listener = (_eventType, filename) => {
16562
+ if (this.watcher === null || this.listenerToken !== token) return;
16563
+ const absolutePath = this.toAbsolutePath(filename);
16564
+ const nextResult = this.onChange(absolutePath);
16565
+ if (nextResult instanceof Promise) {
16566
+ void nextResult.catch((error) => {
16567
+ console.error("[codebase-index] Error handling native watcher event:", error);
16568
+ });
16569
+ }
16570
+ };
16571
+ const watcher = this.watchFactory(this.root, listener, {
16572
+ persistent: true,
16573
+ recursive: true
16574
+ });
16575
+ watcher.on?.("error", (error) => {
16576
+ if (this.watcher === watcher && this.listenerToken === token) {
16577
+ this.onError?.(error);
16578
+ }
16579
+ });
16580
+ this.watcher = watcher;
16581
+ }
16582
+ async stop() {
16583
+ const watcher = this.watcher;
16584
+ this.watcher = null;
16585
+ this.listenerToken += 1;
16586
+ if (!watcher) return;
16587
+ await watcher.close();
16588
+ }
16589
+ toAbsolutePath(filename) {
16590
+ if (filename == null) return null;
16591
+ const normalizedFilename = typeof filename === "string" ? filename : filename.toString();
16592
+ const absolutePath = path21.resolve(this.root, normalizedFilename);
16593
+ const relativePath = path21.relative(this.root, absolutePath);
16594
+ const outsideRoot = relativePath === ".." || relativePath.startsWith(`..${path21.sep}`) || path21.isAbsolute(relativePath);
16595
+ return outsideRoot ? null : absolutePath;
16596
+ }
16597
+ defaultWatchFactory = (root, listener, options) => watch2(root, options, listener);
16598
+ };
16599
+
16600
+ // src/watcher/snapshot.ts
16601
+ import * as fsPromises4 from "fs/promises";
16602
+ import * as path22 from "path";
16603
+ async function buildFileSnapshotScan(projectRoot, config, configPaths = []) {
16604
+ const normalizedProjectRoot = path22.resolve(projectRoot);
16605
+ const ignoreFilter = createIgnoreFilter(normalizedProjectRoot);
16606
+ const includePatterns = [...config.include, ...config.additionalInclude ?? []];
16607
+ const maxDepth = config.indexing?.maxDepth ?? -1;
16608
+ const snapshot = /* @__PURE__ */ new Map();
16609
+ const unreadablePrefixes = /* @__PURE__ */ new Set();
16610
+ const includeFile = async (filePath) => {
16611
+ const normalizedPath2 = path22.resolve(filePath);
16612
+ if (!shouldIncludeFile(normalizedPath2, normalizedProjectRoot, includePatterns, config.exclude, ignoreFilter)) return;
16613
+ const stat5 = await readStatIfFile(normalizedPath2, unreadablePrefixes);
16614
+ if (stat5) snapshot.set(normalizedPath2, { size: stat5.size, mtimeMs: stat5.mtimeMs });
16615
+ };
16616
+ const walk = async (directoryPath, depth) => {
16617
+ let entries;
16618
+ try {
16619
+ entries = await fsPromises4.readdir(directoryPath, { withFileTypes: true });
16620
+ } catch (error) {
16621
+ if (isMissingFsError(error)) return;
16622
+ if (isPermissionFsError(error)) {
16623
+ unreadablePrefixes.add(path22.resolve(directoryPath));
16624
+ return;
16625
+ }
16626
+ throw error;
16627
+ }
16628
+ for (const entry of entries) {
16629
+ const fullPath = path22.join(directoryPath, entry.name);
16630
+ const relativePath = path22.relative(normalizedProjectRoot, fullPath);
16631
+ if (entry.isDirectory()) {
16632
+ if (hasFilteredPathSegment(relativePath, path22.sep) || isRestrictedDirectory(relativePath, path22.sep)) continue;
16633
+ if (ignoreFilter.ignores(relativePath)) continue;
16634
+ if (maxDepth === -1 || depth < maxDepth) await walk(fullPath, depth + 1);
16635
+ } else if (entry.isFile()) {
16636
+ await includeFile(fullPath);
16637
+ }
16638
+ }
16639
+ };
16640
+ await walk(normalizedProjectRoot, 0);
16641
+ await includeExplicitConfigPaths(snapshot, unreadablePrefixes, configPaths);
16642
+ return { entries: snapshot, unreadablePrefixes };
16643
+ }
16644
+ async function buildFileSnapshotForPathScan(projectRoot, config, configPaths, targetPath) {
16645
+ const normalizedProjectRoot = path22.resolve(projectRoot);
16646
+ const normalizedTargetPath = path22.resolve(targetPath);
16647
+ if (!isWithinPath(normalizedProjectRoot, normalizedTargetPath)) {
16648
+ return { entries: /* @__PURE__ */ new Map(), unreadablePrefixes: /* @__PURE__ */ new Set() };
16649
+ }
16650
+ const ignoreFilter = createIgnoreFilter(normalizedProjectRoot);
16651
+ const includePatterns = [...config.include, ...config.additionalInclude ?? []];
16652
+ const maxDepth = config.indexing?.maxDepth ?? -1;
16653
+ const explicitConfigPaths = new Set(configPaths.map((configPath) => path22.resolve(configPath)));
16654
+ const snapshot = /* @__PURE__ */ new Map();
16655
+ const unreadablePrefixes = /* @__PURE__ */ new Set();
16656
+ const includeFile = async (filePath) => {
16657
+ const normalizedPath2 = path22.resolve(filePath);
16658
+ if (!explicitConfigPaths.has(normalizedPath2) && !shouldIncludeFile(
16659
+ normalizedPath2,
16660
+ normalizedProjectRoot,
16661
+ includePatterns,
16662
+ config.exclude,
16663
+ ignoreFilter
16664
+ )) return;
16665
+ const stat5 = await readStatIfFile(normalizedPath2, unreadablePrefixes);
16666
+ if (stat5) snapshot.set(normalizedPath2, { size: stat5.size, mtimeMs: stat5.mtimeMs });
16667
+ };
16668
+ const walk = async (directoryPath, depth) => {
16669
+ let entries;
16670
+ try {
16671
+ entries = await fsPromises4.readdir(directoryPath, { withFileTypes: true });
16672
+ } catch (error) {
16673
+ if (isMissingFsError(error)) return;
16674
+ if (isPermissionFsError(error)) {
16675
+ unreadablePrefixes.add(path22.resolve(directoryPath));
16676
+ return;
16677
+ }
16678
+ throw error;
16679
+ }
16680
+ for (const entry of entries) {
16681
+ const fullPath = path22.join(directoryPath, entry.name);
16682
+ const relativePath = path22.relative(normalizedProjectRoot, fullPath);
16683
+ if (entry.isDirectory()) {
16684
+ if (hasFilteredPathSegment(relativePath, path22.sep) || isRestrictedDirectory(relativePath, path22.sep)) continue;
16685
+ if (ignoreFilter.ignores(relativePath)) continue;
16686
+ if (maxDepth === -1 || depth < maxDepth) await walk(fullPath, depth + 1);
16687
+ } else if (entry.isFile()) {
16688
+ await includeFile(fullPath);
16689
+ }
16690
+ }
16691
+ };
16692
+ const targetStat = await readStatIfFile(normalizedTargetPath, unreadablePrefixes);
16693
+ if (targetStat) await includeFile(normalizedTargetPath);
16694
+ else await walk(normalizedTargetPath, 0);
16695
+ await includeExplicitConfigPathsInPath(snapshot, unreadablePrefixes, configPaths, normalizedTargetPath);
16696
+ return { entries: snapshot, unreadablePrefixes };
16697
+ }
16698
+ function completeFileSnapshot(previous, scan) {
16699
+ const completed = new Map(scan.entries);
16700
+ for (const unreadablePrefix of scan.unreadablePrefixes) {
16701
+ for (const [entryPath, entry] of previous) {
16702
+ if (isWithinPath(unreadablePrefix, entryPath) && !completed.has(entryPath)) completed.set(entryPath, entry);
16703
+ }
16704
+ }
16705
+ return completed;
16706
+ }
16707
+ async function includeExplicitConfigPaths(snapshot, unreadablePrefixes, configPaths) {
16708
+ for (const configPath of [...new Set(configPaths.map((value) => path22.resolve(value)))]) {
16709
+ if (snapshot.has(configPath)) continue;
16710
+ const stat5 = await readStatIfFile(configPath, unreadablePrefixes);
16711
+ if (stat5) snapshot.set(configPath, { size: stat5.size, mtimeMs: stat5.mtimeMs });
16712
+ }
16713
+ }
16714
+ async function includeExplicitConfigPathsInPath(snapshot, unreadablePrefixes, configPaths, targetPath) {
16715
+ await includeExplicitConfigPaths(
16716
+ snapshot,
16717
+ unreadablePrefixes,
16718
+ configPaths.filter((configPath) => isWithinPath(targetPath, path22.resolve(configPath)))
16719
+ );
16720
+ }
16721
+ function isWithinPath(parentPath, childPath) {
16722
+ const relativePath = path22.relative(parentPath, childPath);
16723
+ return relativePath === "" || !relativePath.startsWith(`..${path22.sep}`) && relativePath !== ".." && !path22.isAbsolute(relativePath);
16724
+ }
16725
+ async function readStatIfFile(filePath, unreadablePrefixes) {
16726
+ try {
16727
+ const stat5 = await fsPromises4.stat(filePath);
16728
+ return stat5.isFile() ? stat5 : null;
16729
+ } catch (error) {
16730
+ if (isMissingFsError(error)) return null;
16731
+ if (isPermissionFsError(error)) {
16732
+ unreadablePrefixes.add(path22.resolve(filePath));
16733
+ return null;
16734
+ }
16735
+ throw error;
16736
+ }
16737
+ }
16738
+ function isMissingFsError(error) {
16739
+ return error instanceof Error && ["ENOENT", "ENOTDIR"].includes(error.code ?? "");
16740
+ }
16741
+ function isPermissionFsError(error) {
16742
+ return error instanceof Error && ["EACCES", "EPERM"].includes(error.code ?? "");
16743
+ }
16744
+ var diffTypeOrder = { add: 0, change: 1, unlink: 2 };
16745
+ function diffFileSnapshots(previous, current, forcedChanges = /* @__PURE__ */ new Set()) {
16746
+ const changes = [];
16747
+ for (const [filePath, previousEntry] of previous) {
16748
+ const currentEntry = current.get(filePath);
16749
+ if (!currentEntry) changes.push({ type: "unlink", path: filePath });
16750
+ else if (forcedChanges.has(filePath) || currentEntry.size !== previousEntry.size || currentEntry.mtimeMs !== previousEntry.mtimeMs) {
16751
+ changes.push({ type: "change", path: filePath });
16752
+ }
16753
+ }
16754
+ for (const [filePath] of current) {
16755
+ if (!previous.has(filePath)) changes.push({ type: "add", path: filePath });
16756
+ }
16757
+ return changes.sort((left, right) => left.path.localeCompare(right.path) || diffTypeOrder[left.type] - diffTypeOrder[right.type]);
16758
+ }
16759
+
16760
+ // src/watcher/snapshot-reconciler.ts
16761
+ var FileSnapshotReconciler = class {
16762
+ constructor(projectRoot, config, configPaths) {
16763
+ this.projectRoot = projectRoot;
16764
+ this.config = config;
16765
+ this.configPaths = configPaths;
16766
+ }
16767
+ projectRoot;
16768
+ config;
16769
+ configPaths;
16770
+ snapshot = null;
16771
+ reconciliationTail = Promise.resolve();
16772
+ async initialize() {
16773
+ this.snapshot = (await buildFileSnapshotScan(this.projectRoot, this.config, this.configPaths)).entries;
16774
+ }
16775
+ async reconcile(invalidations = []) {
16776
+ if (this.snapshot === null) {
16777
+ throw new Error("FileSnapshotReconciler is not initialized. Call initialize() before reconcile().");
16778
+ }
16779
+ const reconciliation = this.reconciliationTail.then(async () => {
16780
+ const previousSnapshot = this.snapshot;
16781
+ if (previousSnapshot === null) {
16782
+ throw new Error("FileSnapshotReconciler is not initialized. Call initialize() before reconcile().");
16783
+ }
16784
+ const normalizedInvalidations = invalidations.map((invalidation) => typeof invalidation === "string" || invalidation === null ? { path: invalidation, forceChange: false } : { path: invalidation.path, forceChange: invalidation.forceChange === true });
16785
+ const scopedPaths = normalizedInvalidations.map((invalidation) => invalidation.path).filter((filePath) => filePath !== null);
16786
+ const scan = scopedPaths.length === 0 || scopedPaths.length !== normalizedInvalidations.length ? await buildFileSnapshotScan(this.projectRoot, this.config, this.configPaths) : await this.reconcilePaths(previousSnapshot, scopedPaths);
16787
+ const nextSnapshot = completeFileSnapshot(previousSnapshot, scan);
16788
+ const forcedChanges = new Set(normalizedInvalidations.filter((invalidation) => invalidation.path !== null && invalidation.forceChange).map((invalidation) => invalidation.path));
16789
+ const changes = diffFileSnapshots(previousSnapshot, nextSnapshot, forcedChanges);
16790
+ this.snapshot = nextSnapshot;
16791
+ return changes;
16792
+ });
16793
+ this.reconciliationTail = reconciliation.then(() => void 0, () => void 0);
16794
+ return reconciliation;
16795
+ }
16796
+ async reconcilePaths(previousSnapshot, invalidatedPaths) {
16797
+ const scopes = this.getScopes(invalidatedPaths);
16798
+ const entries = new Map(previousSnapshot);
16799
+ const unreadablePrefixes = /* @__PURE__ */ new Set();
16800
+ for (const scope of scopes) {
16801
+ for (const previousPath of entries.keys()) {
16802
+ if (isWithinPath(scope, previousPath)) entries.delete(previousPath);
16803
+ }
16804
+ const scopedScan = await buildFileSnapshotForPathScan(this.projectRoot, this.config, this.configPaths, scope);
16805
+ for (const [filePath, entry] of scopedScan.entries) entries.set(filePath, entry);
16806
+ for (const unreadablePrefix of scopedScan.unreadablePrefixes) unreadablePrefixes.add(unreadablePrefix);
16807
+ }
16808
+ return { entries, unreadablePrefixes };
16809
+ }
16810
+ getScopes(invalidatedPaths) {
16811
+ const uniquePaths = [...new Set(invalidatedPaths)].sort((left, right) => left.length - right.length);
16812
+ return uniquePaths.filter((candidate, index) => !uniquePaths.slice(0, index).some(
16813
+ (ancestor) => isWithinPath(ancestor, candidate)
16814
+ ));
16815
+ }
16816
+ };
16817
+
16818
+ // src/watcher/file-watcher.ts
16630
16819
  var FileWatcher = class {
16631
16820
  watcher = null;
16632
16821
  projectRoot;
16633
16822
  config;
16634
16823
  configPath;
16824
+ backend;
16635
16825
  projectConfigPaths;
16636
16826
  pendingChanges = /* @__PURE__ */ new Map();
16637
16827
  debounceTimer = null;
@@ -16641,44 +16831,74 @@ var FileWatcher = class {
16641
16831
  resolveReady = null;
16642
16832
  pollingFallbackAttempted = false;
16643
16833
  pendingClose = null;
16834
+ startupReadySignals = 1;
16835
+ nativeWatcher = null;
16836
+ nativeReconciler = null;
16837
+ nativeSetupGeneration = 0;
16838
+ nativeStarting = false;
16839
+ nativeInitializing = false;
16840
+ nativeReconcileTimer = null;
16841
+ nativeInvalidatedPaths = /* @__PURE__ */ new Map();
16842
+ configPathStates = /* @__PURE__ */ new Map();
16644
16843
  constructor(projectRoot, config, host, options = {}) {
16645
16844
  this.projectRoot = projectRoot;
16646
16845
  this.config = config;
16846
+ this.backend = options.backend ?? "auto";
16647
16847
  this.configPath = options.configPath;
16648
16848
  this.projectConfigPaths = options.configPath ? [options.configPath] : getProjectConfigCandidatePaths(projectRoot, host);
16649
16849
  }
16650
16850
  start(handler) {
16651
- if (this.watcher) {
16851
+ if (this.watcher || this.nativeWatcher || this.nativeStarting) {
16652
16852
  return;
16653
16853
  }
16654
16854
  this.onChanges = handler;
16655
16855
  this.pollingFallbackAttempted = false;
16656
16856
  this.resetReady();
16857
+ if (this.shouldUseNativeWatcher()) {
16858
+ if (this.hasExternalConfigWatchTarget()) {
16859
+ this.setStartupReadySignals(2);
16860
+ this.startExternalConfigWatcher();
16861
+ }
16862
+ this.nativeStarting = true;
16863
+ void this.createNativeWatcher();
16864
+ return;
16865
+ }
16657
16866
  this.createWatcher();
16658
16867
  }
16659
16868
  resetReady() {
16660
- this.readyPromise = new Promise((resolve15) => {
16661
- this.resolveReady = resolve15;
16869
+ this.readyPromise = new Promise((resolve17) => {
16870
+ this.resolveReady = resolve17;
16662
16871
  });
16872
+ this.startupReadySignals = 1;
16663
16873
  }
16664
- createWatcher(usePolling = false) {
16665
- const ignoreFilter = createIgnoreFilter(this.projectRoot);
16666
- let watchTargets = this.projectRoot;
16667
- if (this.configPath) {
16668
- watchTargets = [this.projectRoot, this.configPath];
16669
- } else {
16670
- const externalConfigTargets = this.projectConfigPaths.filter((projectConfigPath) => {
16671
- const relativeConfigPath = path21.relative(this.projectRoot, projectConfigPath);
16672
- return this.isOutsideProjectPath(relativeConfigPath);
16673
- }).map((projectConfigPath) => existsSync13(projectConfigPath) ? projectConfigPath : this.getNearestExistingDirectory(path21.dirname(projectConfigPath)));
16674
- const uniqueExternalConfigTargets = [...new Set(externalConfigTargets)];
16675
- if (uniqueExternalConfigTargets.length > 0) {
16676
- watchTargets = [this.projectRoot, ...uniqueExternalConfigTargets];
16677
- }
16874
+ setStartupReadySignals(expectedSignals) {
16875
+ if (!this.readyPromise) {
16876
+ return;
16877
+ }
16878
+ this.startupReadySignals = Math.max(0, expectedSignals);
16879
+ }
16880
+ reportStartupReadySignal() {
16881
+ if (!this.readyPromise || !this.resolveReady) {
16882
+ return;
16678
16883
  }
16884
+ if (this.startupReadySignals <= 0) {
16885
+ return;
16886
+ }
16887
+ this.startupReadySignals -= 1;
16888
+ if (this.startupReadySignals !== 0) {
16889
+ return;
16890
+ }
16891
+ this.resolveReady();
16892
+ this.resolveReady = null;
16893
+ }
16894
+ createWatcher(watchTargets, usePolling = false, reportsStartupReady = true) {
16895
+ let reportedStartupReady = false;
16896
+ this.configPathStates = this.getConfigPathStates();
16897
+ const ignoreFilter = createIgnoreFilter(this.projectRoot);
16898
+ const resolvedWatchTargets = watchTargets ?? this.getFullChokidarWatchTargets();
16679
16899
  const watcherOptions = {
16680
16900
  ignored: (filePath) => {
16681
- const relativePath = path21.relative(this.projectRoot, filePath);
16901
+ const relativePath = path23.relative(this.projectRoot, filePath);
16682
16902
  if (!relativePath) return false;
16683
16903
  if (this.isProjectConfigPathOrAncestor(relativePath)) {
16684
16904
  return false;
@@ -16686,10 +16906,10 @@ var FileWatcher = class {
16686
16906
  if (this.isOutsideProjectPath(relativePath)) {
16687
16907
  return true;
16688
16908
  }
16689
- if (hasFilteredPathSegment(relativePath, path21.sep)) {
16909
+ if (hasFilteredPathSegment(relativePath, path23.sep)) {
16690
16910
  return true;
16691
16911
  }
16692
- if (isRestrictedDirectory(relativePath, path21.sep)) {
16912
+ if (isRestrictedDirectory(relativePath, path23.sep)) {
16693
16913
  return true;
16694
16914
  }
16695
16915
  if (ignoreFilter.ignores(relativePath)) {
@@ -16722,10 +16942,13 @@ var FileWatcher = class {
16722
16942
  watcher = new FSWatcher(watcherOptions);
16723
16943
  }
16724
16944
  this.watcher = watcher;
16725
- watcher.once("ready", () => {
16945
+ watcher.on("ready", () => {
16726
16946
  if (this.watcher !== watcher) return;
16727
- this.resolveReady?.();
16728
- this.resolveReady = null;
16947
+ this.reconcileConfigPathStates();
16948
+ if (reportsStartupReady) {
16949
+ this.reportStartupReadySignal();
16950
+ reportedStartupReady = true;
16951
+ }
16729
16952
  });
16730
16953
  watcher.on("error", (error) => {
16731
16954
  const err = error instanceof Error ? error : null;
@@ -16739,10 +16962,13 @@ var FileWatcher = class {
16739
16962
  console.error("[codebase-index] Failed to close exhausted file watcher:", closeError);
16740
16963
  });
16741
16964
  if (this.onChanges) {
16965
+ const replacementReportsStartupReady = reportsStartupReady || reportedStartupReady;
16742
16966
  if (!this.resolveReady) {
16743
16967
  this.resetReady();
16968
+ } else if (reportedStartupReady) {
16969
+ this.startupReadySignals += 1;
16744
16970
  }
16745
- this.createWatcher(true);
16971
+ this.createWatcher(resolvedWatchTargets, true, replacementReportsStartupReady);
16746
16972
  } else {
16747
16973
  this.watcher = null;
16748
16974
  }
@@ -16753,13 +16979,166 @@ var FileWatcher = class {
16753
16979
  watcher.on("add", (filePath) => this.handleChange(watcher, "add", filePath));
16754
16980
  watcher.on("change", (filePath) => this.handleChange(watcher, "change", filePath));
16755
16981
  watcher.on("unlink", (filePath) => this.handleChange(watcher, "unlink", filePath));
16756
- watcher.add(watchTargets);
16982
+ watcher.add(resolvedWatchTargets);
16983
+ }
16984
+ shouldUseNativeWatcher() {
16985
+ if (this.backend === "chokidar") {
16986
+ return false;
16987
+ }
16988
+ return true;
16989
+ }
16990
+ getFullChokidarWatchTargets() {
16991
+ if (this.configPath) {
16992
+ return [this.projectRoot, this.configPath];
16993
+ }
16994
+ const externalConfigTargets = this.getExternalConfigWatchTargets();
16995
+ if (externalConfigTargets.length === 0) {
16996
+ return this.projectRoot;
16997
+ }
16998
+ return [this.projectRoot, ...externalConfigTargets];
16999
+ }
17000
+ getExternalConfigWatchTargets() {
17001
+ return [...new Set(
17002
+ this.projectConfigPaths.filter((projectConfigPath) => {
17003
+ const relativeConfigPath = path23.relative(this.projectRoot, projectConfigPath);
17004
+ return this.isOutsideProjectPath(relativeConfigPath);
17005
+ }).map((projectConfigPath) => {
17006
+ if (existsSync13(projectConfigPath)) {
17007
+ return projectConfigPath;
17008
+ }
17009
+ return this.getNearestExistingDirectory(path23.dirname(projectConfigPath));
17010
+ })
17011
+ )];
17012
+ }
17013
+ hasExternalConfigWatchTarget() {
17014
+ return this.getExternalConfigWatchTargets().length > 0;
17015
+ }
17016
+ startExternalConfigWatcher(usePolling = false) {
17017
+ const externalTargets = this.getExternalConfigWatchTargets();
17018
+ if (externalTargets.length === 0) {
17019
+ return;
17020
+ }
17021
+ this.createWatcher(externalTargets, usePolling);
17022
+ }
17023
+ async createNativeWatcher() {
17024
+ const generation = ++this.nativeSetupGeneration;
17025
+ const reconciler = new FileSnapshotReconciler(this.projectRoot, this.config, this.projectConfigPaths);
17026
+ const watcher = new NativeRecursiveWatcher(
17027
+ this.projectRoot,
17028
+ (filePath) => this.scheduleNativeReconciliation(generation, filePath),
17029
+ { onError: (error) => void this.fallbackFromNativeWatcher(generation, error) }
17030
+ );
17031
+ this.nativeReconciler = reconciler;
17032
+ this.nativeWatcher = watcher;
17033
+ this.nativeInitializing = true;
17034
+ try {
17035
+ watcher.start();
17036
+ if (!this.isCurrentNativeSetup(generation)) {
17037
+ await watcher.stop();
17038
+ return;
17039
+ }
17040
+ await reconciler.initialize();
17041
+ if (!this.isCurrentNativeSetup(generation) || this.nativeWatcher !== watcher) {
17042
+ await watcher.stop();
17043
+ return;
17044
+ }
17045
+ this.nativeStarting = false;
17046
+ this.nativeInitializing = false;
17047
+ await this.reconcileNativeWatcherWithPendingInvalidations(generation);
17048
+ this.reportStartupReadySignal();
17049
+ } catch (error) {
17050
+ if (!this.isCurrentNativeSetup(generation)) return;
17051
+ this.nativeInitializing = false;
17052
+ if (this.nativeWatcher) {
17053
+ await this.fallbackFromNativeWatcher(generation, error);
17054
+ return;
17055
+ }
17056
+ this.nativeStarting = false;
17057
+ const externalWatcher = this.watcher;
17058
+ this.watcher = null;
17059
+ this.nativeReconciler = null;
17060
+ await externalWatcher?.close();
17061
+ console.warn("[codebase-index] Native recursive watcher unavailable; using Chokidar fallback.", error);
17062
+ this.setStartupReadySignals(1);
17063
+ this.createWatcher();
17064
+ }
17065
+ }
17066
+ isCurrentNativeSetup(generation) {
17067
+ return this.nativeSetupGeneration === generation && this.onChanges !== null;
17068
+ }
17069
+ scheduleNativeReconciliation(generation, filePath) {
17070
+ if (!this.isCurrentNativeSetup(generation)) return;
17071
+ const requiresFullReconciliation = filePath === path23.join(this.projectRoot, ".gitignore");
17072
+ const invalidatedPath = requiresFullReconciliation ? null : filePath;
17073
+ this.nativeInvalidatedPaths.set(invalidatedPath, invalidatedPath !== null);
17074
+ if (this.nativeReconcileTimer) {
17075
+ clearTimeout(this.nativeReconcileTimer);
17076
+ }
17077
+ this.nativeReconcileTimer = setTimeout(() => {
17078
+ this.nativeReconcileTimer = null;
17079
+ void this.reconcileNativeWatcherFromQueue(generation);
17080
+ }, 100);
17081
+ }
17082
+ reconcileNativeWatcherFromQueue(generation) {
17083
+ if (!this.isCurrentNativeSetup(generation) || this.nativeInitializing) return;
17084
+ const invalidatedPaths = this.popNativeInvalidations();
17085
+ if (invalidatedPaths.length === 0) return;
17086
+ void this.reconcileNativeWatcher(generation, invalidatedPaths);
17087
+ }
17088
+ async reconcileNativeWatcher(generation, invalidatedPaths) {
17089
+ if (!this.isCurrentNativeSetup(generation) || !this.nativeReconciler) return;
17090
+ try {
17091
+ const reconciler = this.nativeReconciler;
17092
+ const changes = await reconciler.reconcile(invalidatedPaths);
17093
+ if (!this.isCurrentNativeSetup(generation) || this.nativeReconciler !== reconciler) return;
17094
+ this.recordChanges(changes);
17095
+ } catch (error) {
17096
+ await this.fallbackFromNativeWatcher(generation, error);
17097
+ }
17098
+ }
17099
+ async reconcileNativeWatcherWithPendingInvalidations(generation) {
17100
+ const invalidatedPaths = this.popNativeInvalidations();
17101
+ if (invalidatedPaths.length === 0) return;
17102
+ await this.reconcileNativeWatcher(generation, invalidatedPaths);
17103
+ }
17104
+ popNativeInvalidations() {
17105
+ if (this.nativeInvalidatedPaths.size === 0) return [];
17106
+ const invalidations = [...this.nativeInvalidatedPaths].map(([invalidatedPath, forceChange]) => ({
17107
+ path: invalidatedPath,
17108
+ forceChange
17109
+ }));
17110
+ this.nativeInvalidatedPaths.clear();
17111
+ return invalidations;
17112
+ }
17113
+ async fallbackFromNativeWatcher(generation, error) {
17114
+ if (!this.isCurrentNativeSetup(generation)) return;
17115
+ const watcher = this.nativeWatcher;
17116
+ const externalWatcher = this.watcher;
17117
+ this.nativeWatcher = null;
17118
+ this.watcher = null;
17119
+ this.nativeReconciler = null;
17120
+ this.nativeStarting = false;
17121
+ this.nativeInitializing = false;
17122
+ this.nativeSetupGeneration += 1;
17123
+ if (this.nativeReconcileTimer) {
17124
+ clearTimeout(this.nativeReconcileTimer);
17125
+ this.nativeReconcileTimer = null;
17126
+ }
17127
+ this.nativeInvalidatedPaths.clear();
17128
+ this.setStartupReadySignals(1);
17129
+ console.warn("[codebase-index] Native recursive watcher failed; using Chokidar fallback.", error);
17130
+ await watcher?.stop();
17131
+ await externalWatcher?.close();
17132
+ if (this.onChanges) {
17133
+ this.createWatcher();
17134
+ }
16757
17135
  }
16758
17136
  handleChange(watcher, type, filePath) {
16759
17137
  if (this.watcher !== watcher) {
16760
17138
  return;
16761
17139
  }
16762
17140
  if (this.isProjectConfigPath(filePath)) {
17141
+ this.updateConfigPathState(filePath);
16763
17142
  this.pendingChanges.set(filePath, type);
16764
17143
  this.scheduleFlush();
16765
17144
  return;
@@ -16774,27 +17153,33 @@ var FileWatcher = class {
16774
17153
  )) {
16775
17154
  return;
16776
17155
  }
16777
- this.pendingChanges.set(filePath, type);
17156
+ this.recordChanges([{ path: filePath, type }]);
17157
+ }
17158
+ recordChanges(changes) {
17159
+ if (changes.length === 0) return;
17160
+ for (const change of changes) {
17161
+ this.pendingChanges.set(change.path, change.type);
17162
+ }
16778
17163
  this.scheduleFlush();
16779
17164
  }
16780
17165
  isProjectConfigPath(filePath) {
16781
- const relativePath = path21.relative(this.projectRoot, filePath);
16782
- const normalizedRelativePath = path21.normalize(relativePath);
17166
+ const relativePath = path23.relative(this.projectRoot, filePath);
17167
+ const normalizedRelativePath = path23.normalize(relativePath);
16783
17168
  return this.getProjectConfigRelativePaths().some((configPath) => configPath === normalizedRelativePath);
16784
17169
  }
16785
17170
  isProjectConfigPathOrAncestor(relativePath) {
16786
- const normalizedRelativePath = path21.normalize(relativePath);
17171
+ const normalizedRelativePath = path23.normalize(relativePath);
16787
17172
  return this.getProjectConfigRelativePaths().some(
16788
- (configPath) => configPath === normalizedRelativePath || configPath.startsWith(`${normalizedRelativePath}${path21.sep}`)
17173
+ (configPath) => configPath === normalizedRelativePath || configPath.startsWith(`${normalizedRelativePath}${path23.sep}`)
16789
17174
  );
16790
17175
  }
16791
17176
  isOutsideProjectPath(relativePath) {
16792
- return relativePath === ".." || relativePath.startsWith(`..${path21.sep}`) || path21.isAbsolute(relativePath);
17177
+ return relativePath === ".." || relativePath.startsWith(`..${path23.sep}`) || path23.isAbsolute(relativePath);
16793
17178
  }
16794
17179
  getNearestExistingDirectory(directoryPath) {
16795
17180
  let candidate = directoryPath;
16796
17181
  while (!existsSync13(candidate)) {
16797
- const parent = path21.dirname(candidate);
17182
+ const parent = path23.dirname(candidate);
16798
17183
  if (parent === candidate) break;
16799
17184
  candidate = parent;
16800
17185
  }
@@ -16802,9 +17187,51 @@ var FileWatcher = class {
16802
17187
  }
16803
17188
  getProjectConfigRelativePaths() {
16804
17189
  return this.projectConfigPaths.map(
16805
- (configPath) => path21.normalize(path21.relative(this.projectRoot, configPath))
17190
+ (configPath) => path23.normalize(path23.relative(this.projectRoot, configPath))
16806
17191
  );
16807
17192
  }
17193
+ getConfigPathStates() {
17194
+ const states = /* @__PURE__ */ new Map();
17195
+ for (const configPath of this.projectConfigPaths) {
17196
+ const state = this.getConfigPathState(configPath);
17197
+ if (state) states.set(configPath, state);
17198
+ }
17199
+ return states;
17200
+ }
17201
+ getConfigPathState(configPath) {
17202
+ try {
17203
+ const stats = statSync6(configPath);
17204
+ return stats.isFile() ? { mtimeMs: stats.mtimeMs, size: stats.size } : void 0;
17205
+ } catch (error) {
17206
+ void error;
17207
+ return void 0;
17208
+ }
17209
+ }
17210
+ updateConfigPathState(configPath) {
17211
+ const state = this.getConfigPathState(configPath);
17212
+ if (state) {
17213
+ this.configPathStates.set(configPath, state);
17214
+ } else {
17215
+ this.configPathStates.delete(configPath);
17216
+ }
17217
+ }
17218
+ reconcileConfigPathStates() {
17219
+ const nextStates = this.getConfigPathStates();
17220
+ const changes = [];
17221
+ for (const configPath of this.projectConfigPaths) {
17222
+ const previous = this.configPathStates.get(configPath);
17223
+ const next = nextStates.get(configPath);
17224
+ if (!previous && next) {
17225
+ changes.push({ path: configPath, type: "add" });
17226
+ } else if (previous && !next) {
17227
+ changes.push({ path: configPath, type: "unlink" });
17228
+ } else if (previous && next && (previous.size !== next.size || previous.mtimeMs !== next.mtimeMs)) {
17229
+ changes.push({ path: configPath, type: "change" });
17230
+ }
17231
+ }
17232
+ this.configPathStates = nextStates;
17233
+ this.recordChanges(changes);
17234
+ }
16808
17235
  scheduleFlush() {
16809
17236
  if (this.debounceTimer) {
16810
17237
  clearTimeout(this.debounceTimer);
@@ -16818,7 +17245,7 @@ var FileWatcher = class {
16818
17245
  return;
16819
17246
  }
16820
17247
  const changes = Array.from(this.pendingChanges.entries()).map(
16821
- ([path28, type]) => ({ path: path28, type })
17248
+ ([path30, type]) => ({ path: path30, type })
16822
17249
  );
16823
17250
  this.pendingChanges.clear();
16824
17251
  try {
@@ -16832,20 +17259,31 @@ var FileWatcher = class {
16832
17259
  clearTimeout(this.debounceTimer);
16833
17260
  this.debounceTimer = null;
16834
17261
  }
17262
+ if (this.nativeReconcileTimer) {
17263
+ clearTimeout(this.nativeReconcileTimer);
17264
+ this.nativeReconcileTimer = null;
17265
+ }
17266
+ this.nativeInvalidatedPaths.clear();
16835
17267
  const watcher = this.watcher;
17268
+ const nativeWatcher = this.nativeWatcher;
16836
17269
  const pendingClose = this.pendingClose;
16837
17270
  const resolveReady = this.resolveReady;
16838
17271
  this.watcher = null;
17272
+ this.nativeWatcher = null;
17273
+ this.nativeReconciler = null;
17274
+ this.nativeStarting = false;
17275
+ this.nativeInitializing = false;
17276
+ this.nativeSetupGeneration += 1;
16839
17277
  this.pendingClose = null;
16840
17278
  this.resolveReady = null;
16841
17279
  this.readyPromise = null;
16842
17280
  this.pendingChanges.clear();
16843
17281
  this.onChanges = null;
16844
- await Promise.all([watcher?.close(), pendingClose]);
17282
+ await Promise.all([watcher?.close(), nativeWatcher?.stop(), pendingClose]);
16845
17283
  resolveReady?.();
16846
17284
  }
16847
17285
  isRunning() {
16848
- return this.watcher !== null;
17286
+ return this.watcher !== null || this.nativeWatcher !== null || this.nativeStarting;
16849
17287
  }
16850
17288
  async waitUntilReady() {
16851
17289
  await (this.readyPromise ?? Promise.resolve());
@@ -16853,7 +17291,7 @@ var FileWatcher = class {
16853
17291
  };
16854
17292
 
16855
17293
  // src/watcher/git-head-watcher.ts
16856
- import * as path22 from "path";
17294
+ import * as path24 from "path";
16857
17295
  var GitHeadWatcher = class {
16858
17296
  watcher = null;
16859
17297
  projectRoot;
@@ -16875,13 +17313,13 @@ var GitHeadWatcher = class {
16875
17313
  this.readyPromise = Promise.resolve();
16876
17314
  return;
16877
17315
  }
16878
- this.readyPromise = new Promise((resolve15) => {
16879
- this.resolveReady = resolve15;
17316
+ this.readyPromise = new Promise((resolve17) => {
17317
+ this.resolveReady = resolve17;
16880
17318
  });
16881
17319
  this.onBranchChange = handler;
16882
17320
  this.currentBranch = getCurrentBranch(this.projectRoot);
16883
17321
  const headPath = getHeadPath(this.projectRoot);
16884
- const refsPath = path22.join(this.projectRoot, ".git", "refs", "heads");
17322
+ const refsPath = path24.join(this.projectRoot, ".git", "refs", "heads");
16885
17323
  this.watcher = chokidar_default.watch([headPath, refsPath], {
16886
17324
  persistent: true,
16887
17325
  ignoreInitial: true,
@@ -17737,7 +18175,7 @@ async function resolveCodebaseContextUnmeasured(projectRoot, host, input) {
17737
18175
  const directory = input.directory ?? void 0;
17738
18176
  const tokenBudget = input.tokenBudget ?? void 0;
17739
18177
  if (from && to) {
17740
- const path28 = await getCallGraphPath(
18178
+ const path30 = await getCallGraphPath(
17741
18179
  projectRoot,
17742
18180
  host,
17743
18181
  from,
@@ -17746,25 +18184,25 @@ async function resolveCodebaseContextUnmeasured(projectRoot, host, input) {
17746
18184
  fromFilePath,
17747
18185
  toFilePath
17748
18186
  );
17749
- const pathText = formatCallGraphPathResult(path28);
17750
- if (path28.path.length > 0) {
18187
+ const pathText = formatCallGraphPathResult(path30);
18188
+ if (path30.path.length > 0) {
17751
18189
  const fitted2 = fitTextToContextBudget(
17752
18190
  pathText,
17753
18191
  tokenBudget
17754
18192
  );
17755
18193
  return {
17756
18194
  text: fitted2.text,
17757
- details: fittedDetails("path", fitted2, path28.path.length)
18195
+ details: fittedDetails("path", fitted2, path30.path.length)
17758
18196
  };
17759
18197
  }
17760
- if (path28.from.status !== "resolved" || path28.to.status !== "resolved") {
18198
+ if (path30.from.status !== "resolved" || path30.to.status !== "resolved") {
17761
18199
  const fitted2 = fitTextToContextBudget(pathText, tokenBudget);
17762
18200
  return {
17763
18201
  text: fitted2.text,
17764
18202
  details: fittedDetails("path", fitted2, 0)
17765
18203
  };
17766
18204
  }
17767
- const resolvedFrom = path28.from;
18205
+ const resolvedFrom = path30.from;
17768
18206
  const { callers } = await getCallGraphData(projectRoot, host, {
17769
18207
  name: to,
17770
18208
  direction: "callers",
@@ -17923,7 +18361,7 @@ async function executeCallGraph(projectRoot, host, args) {
17923
18361
  return { text: formatCallGraphResult(await getCallGraphData(projectRoot, host, args)) };
17924
18362
  }
17925
18363
  async function executeCallGraphPath(projectRoot, host, args) {
17926
- const path28 = await getCallGraphPath(
18364
+ const path30 = await getCallGraphPath(
17927
18365
  projectRoot,
17928
18366
  host,
17929
18367
  args.from,
@@ -17932,7 +18370,7 @@ async function executeCallGraphPath(projectRoot, host, args) {
17932
18370
  args.fromFilePath,
17933
18371
  args.toFilePath
17934
18372
  );
17935
- return { text: formatCallGraphPathResult(path28) };
18373
+ return { text: formatCallGraphPathResult(path30) };
17936
18374
  }
17937
18375
  async function executeCodeCommunities(projectRoot, host, args) {
17938
18376
  const result = await getCodeCommunities(projectRoot, host, args);
@@ -17942,11 +18380,11 @@ async function executeCodeCommunities(projectRoot, host, args) {
17942
18380
  // src/adapters/opencode/tools.ts
17943
18381
  import { writeFileSync as writeFileSync4 } from "fs";
17944
18382
  import * as os7 from "os";
17945
- import * as path25 from "path";
18383
+ import * as path27 from "path";
17946
18384
 
17947
18385
  // src/tools/visualize/activity.ts
17948
18386
  import { execFileSync } from "child_process";
17949
- import * as path23 from "path";
18387
+ import * as path25 from "path";
17950
18388
  function attachRecentActivity(data, projectRoot) {
17951
18389
  const activity = readGitActivity(projectRoot);
17952
18390
  const changes = activity.size > 0 ? buildGitChanges(data, activity, projectRoot) : buildGraphChanges(data);
@@ -18108,7 +18546,7 @@ function normalizePath3(filePath) {
18108
18546
  return filePath.replace(/\\/g, "/");
18109
18547
  }
18110
18548
  function toGitRelativePath(projectRoot, filePath) {
18111
- const relativePath = path23.isAbsolute(filePath) ? path23.relative(projectRoot, filePath) : filePath;
18549
+ const relativePath = path25.isAbsolute(filePath) ? path25.relative(projectRoot, filePath) : filePath;
18112
18550
  return normalizePath3(relativePath);
18113
18551
  }
18114
18552
 
@@ -18366,7 +18804,7 @@ render();
18366
18804
  }
18367
18805
 
18368
18806
  // src/tools/visualize/transform.ts
18369
- import * as path24 from "path";
18807
+ import * as path26 from "path";
18370
18808
 
18371
18809
  // src/tools/visualize/modules.ts
18372
18810
  var MAX_MODULES = 18;
@@ -18499,8 +18937,8 @@ function compactModules(prefixToNodes) {
18499
18937
  function deriveModules(nodes) {
18500
18938
  const initial = /* @__PURE__ */ new Map();
18501
18939
  for (const node of nodes) {
18502
- const relative12 = stripToProjectRelative(node.filePath);
18503
- const prefix = modulePrefixFromRelativePath(relative12);
18940
+ const relative14 = stripToProjectRelative(node.filePath);
18941
+ const prefix = modulePrefixFromRelativePath(relative14);
18504
18942
  if (!initial.has(prefix)) initial.set(prefix, []);
18505
18943
  initial.get(prefix)?.push(node);
18506
18944
  }
@@ -18626,7 +19064,7 @@ function transformForVisualization(symbols, edges, options = {}) {
18626
19064
  filePath: s.filePath,
18627
19065
  kind: s.kind,
18628
19066
  line: s.startLine,
18629
- directory: path24.dirname(s.filePath),
19067
+ directory: path26.dirname(s.filePath),
18630
19068
  moduleId: "",
18631
19069
  moduleLabel: ""
18632
19070
  }));
@@ -18946,7 +19384,7 @@ var index_visualize = tool({
18946
19384
  return "No connected symbols found for visualization. Try including orphans with includeOrphans=true, or check that the call graph has resolved edges.";
18947
19385
  }
18948
19386
  const html = generateVisualizationHtml(vizData);
18949
- const outputPath = path25.join(os7.tmpdir(), `call-graph-${Date.now()}.html`);
19387
+ const outputPath = path27.join(os7.tmpdir(), `call-graph-${Date.now()}.html`);
18950
19388
  writeFileSync4(outputPath, html, "utf-8");
18951
19389
  let result = `Temporal call graph visualization generated: ${outputPath}
18952
19390
 
@@ -19053,7 +19491,7 @@ var PI_TOOL_NAMES = [
19053
19491
 
19054
19492
  // src/commands/loader.ts
19055
19493
  import { existsSync as existsSync14, readdirSync as readdirSync3, readFileSync as readFileSync9 } from "fs";
19056
- import * as path26 from "path";
19494
+ import * as path28 from "path";
19057
19495
  function parseFrontmatter(content) {
19058
19496
  const frontmatterRegex = /^---\s*\n([\s\S]*?)\n---\s*\n([\s\S]*)$/;
19059
19497
  const match = content.match(frontmatterRegex);
@@ -19079,7 +19517,7 @@ function loadCommandsFromDirectory(commandsDir) {
19079
19517
  }
19080
19518
  const files = readdirSync3(commandsDir).filter((f) => f.endsWith(".md"));
19081
19519
  for (const file of files) {
19082
- const filePath = path26.join(commandsDir, file);
19520
+ const filePath = path28.join(commandsDir, file);
19083
19521
  let content;
19084
19522
  try {
19085
19523
  content = readFileSync9(filePath, "utf-8");
@@ -19088,7 +19526,7 @@ function loadCommandsFromDirectory(commandsDir) {
19088
19526
  throw new Error(`Failed to load command file ${filePath}: ${message}`);
19089
19527
  }
19090
19528
  const { frontmatter, body } = parseFrontmatter(content);
19091
- const name = path26.basename(file, ".md");
19529
+ const name = path28.basename(file, ".md");
19092
19530
  const description = frontmatter.description || `Run the ${name} command`;
19093
19531
  commands.set(name, {
19094
19532
  description,
@@ -19422,23 +19860,41 @@ var RoutingHintController = class {
19422
19860
 
19423
19861
  // src/adapters/opencode.ts
19424
19862
  var activeWatchers = /* @__PURE__ */ new Map();
19425
- function replaceActiveWatcher(projectRoot, nextWatcher) {
19426
- const existing = activeWatchers.get(projectRoot);
19427
- if (existing) {
19428
- existing.stop();
19429
- activeWatchers.delete(projectRoot);
19430
- }
19431
- if (nextWatcher) {
19432
- activeWatchers.set(projectRoot, nextWatcher);
19863
+ var watcherReplacementChains = /* @__PURE__ */ new Map();
19864
+ async function replaceActiveWatcher(projectRoot, createNextWatcher) {
19865
+ const chain = (watcherReplacementChains.get(projectRoot) ?? Promise.resolve()).catch(() => void 0).then(async () => {
19866
+ const existing = activeWatchers.get(projectRoot);
19867
+ if (existing) {
19868
+ try {
19869
+ await existing.stop();
19870
+ } catch (error) {
19871
+ console.error("[codebase-index] Failed to stop replaced watcher:", error);
19872
+ throw error;
19873
+ }
19874
+ if (activeWatchers.get(projectRoot) === existing) {
19875
+ activeWatchers.delete(projectRoot);
19876
+ }
19877
+ }
19878
+ if (createNextWatcher) {
19879
+ activeWatchers.set(projectRoot, createNextWatcher());
19880
+ }
19881
+ });
19882
+ watcherReplacementChains.set(projectRoot, chain);
19883
+ try {
19884
+ await chain;
19885
+ } finally {
19886
+ if (watcherReplacementChains.get(projectRoot) === chain) {
19887
+ watcherReplacementChains.delete(projectRoot);
19888
+ }
19433
19889
  }
19434
19890
  }
19435
19891
  function getCommandsDir() {
19436
19892
  let currentDir = process.cwd();
19437
19893
  if (typeof import.meta !== "undefined" && import.meta.url) {
19438
- currentDir = path27.dirname(fileURLToPath2(import.meta.url));
19894
+ currentDir = path29.dirname(fileURLToPath2(import.meta.url));
19439
19895
  }
19440
- const packageRoot = path27.basename(currentDir) === "adapters" ? path27.join(currentDir, "..", "..") : path27.join(currentDir, "..");
19441
- return path27.join(packageRoot, "commands");
19896
+ const packageRoot = path29.basename(currentDir) === "adapters" ? path29.join(currentDir, "..", "..") : path29.join(currentDir, "..");
19897
+ return path29.join(packageRoot, "commands");
19442
19898
  }
19443
19899
  function appendRoutingHints(output, hints, preferredRole) {
19444
19900
  const preferredBucket = preferredRole === "developer" ? output.developer : output.system;
@@ -19479,9 +19935,12 @@ var plugin = async ({ directory, worktree }) => {
19479
19935
  startAutoIndex(projectRoot, "opencode", "startup");
19480
19936
  }
19481
19937
  if (config.indexing.watchFiles && isValidProject) {
19482
- replaceActiveWatcher(projectRoot, createWatcherWithIndexer(getProjectIndexer, projectRoot, config, "opencode"));
19938
+ await replaceActiveWatcher(
19939
+ projectRoot,
19940
+ () => createWatcherWithIndexer(getProjectIndexer, projectRoot, config, "opencode")
19941
+ );
19483
19942
  } else {
19484
- replaceActiveWatcher(projectRoot, null);
19943
+ await replaceActiveWatcher(projectRoot, null);
19485
19944
  }
19486
19945
  return {
19487
19946
  tool: {