open-codebase-index 0.22.4 → 0.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -333,7 +333,7 @@ var require_ignore = __commonJS({
333
333
  // path matching.
334
334
  // - check `string` either `MODE_IGNORE` or `MODE_CHECK_IGNORE`
335
335
  // @returns {TestResult} true if a file is ignored
336
- test(path28, checkUnignored, mode) {
336
+ test(path30, checkUnignored, mode) {
337
337
  let ignored = false;
338
338
  let unignored = false;
339
339
  let matchedRule;
@@ -342,7 +342,7 @@ var require_ignore = __commonJS({
342
342
  if (unignored === negative && ignored !== unignored || negative && !ignored && !unignored && !checkUnignored) {
343
343
  return;
344
344
  }
345
- const matched = rule[mode].test(path28);
345
+ const matched = rule[mode].test(path30);
346
346
  if (!matched) {
347
347
  return;
348
348
  }
@@ -363,17 +363,17 @@ var require_ignore = __commonJS({
363
363
  var throwError = (message, Ctor) => {
364
364
  throw new Ctor(message);
365
365
  };
366
- var checkPath = (path28, originalPath, doThrow) => {
367
- if (!isString(path28)) {
366
+ var checkPath = (path30, originalPath, doThrow) => {
367
+ if (!isString(path30)) {
368
368
  return doThrow(
369
369
  `path must be a string, but got \`${originalPath}\``,
370
370
  TypeError
371
371
  );
372
372
  }
373
- if (!path28) {
373
+ if (!path30) {
374
374
  return doThrow(`path must not be empty`, TypeError);
375
375
  }
376
- if (checkPath.isNotRelative(path28)) {
376
+ if (checkPath.isNotRelative(path30)) {
377
377
  const r = "`path.relative()`d";
378
378
  return doThrow(
379
379
  `path should be a ${r} string, but got "${originalPath}"`,
@@ -382,7 +382,7 @@ var require_ignore = __commonJS({
382
382
  }
383
383
  return true;
384
384
  };
385
- var isNotRelative = (path28) => REGEX_TEST_INVALID_PATH.test(path28);
385
+ var isNotRelative = (path30) => REGEX_TEST_INVALID_PATH.test(path30);
386
386
  checkPath.isNotRelative = isNotRelative;
387
387
  checkPath.convert = (p) => p;
388
388
  var Ignore2 = class {
@@ -412,19 +412,19 @@ var require_ignore = __commonJS({
412
412
  }
413
413
  // @returns {TestResult}
414
414
  _test(originalPath, cache, checkUnignored, slices) {
415
- const path28 = originalPath && checkPath.convert(originalPath);
415
+ const path30 = originalPath && checkPath.convert(originalPath);
416
416
  checkPath(
417
- path28,
417
+ path30,
418
418
  originalPath,
419
419
  this._strictPathCheck ? throwError : RETURN_FALSE
420
420
  );
421
- return this._t(path28, cache, checkUnignored, slices);
421
+ return this._t(path30, cache, checkUnignored, slices);
422
422
  }
423
- checkIgnore(path28) {
424
- if (!REGEX_TEST_TRAILING_SLASH.test(path28)) {
425
- return this.test(path28);
423
+ checkIgnore(path30) {
424
+ if (!REGEX_TEST_TRAILING_SLASH.test(path30)) {
425
+ return this.test(path30);
426
426
  }
427
- const slices = path28.split(SLASH2).filter(Boolean);
427
+ const slices = path30.split(SLASH2).filter(Boolean);
428
428
  slices.pop();
429
429
  if (slices.length) {
430
430
  const parent = this._t(
@@ -437,18 +437,18 @@ var require_ignore = __commonJS({
437
437
  return parent;
438
438
  }
439
439
  }
440
- return this._rules.test(path28, false, MODE_CHECK_IGNORE);
440
+ return this._rules.test(path30, false, MODE_CHECK_IGNORE);
441
441
  }
442
- _t(path28, cache, checkUnignored, slices) {
443
- if (path28 in cache) {
444
- return cache[path28];
442
+ _t(path30, cache, checkUnignored, slices) {
443
+ if (path30 in cache) {
444
+ return cache[path30];
445
445
  }
446
446
  if (!slices) {
447
- slices = path28.split(SLASH2).filter(Boolean);
447
+ slices = path30.split(SLASH2).filter(Boolean);
448
448
  }
449
449
  slices.pop();
450
450
  if (!slices.length) {
451
- return cache[path28] = this._rules.test(path28, checkUnignored, MODE_IGNORE);
451
+ return cache[path30] = this._rules.test(path30, checkUnignored, MODE_IGNORE);
452
452
  }
453
453
  const parent = this._t(
454
454
  slices.join(SLASH2) + SLASH2,
@@ -456,29 +456,29 @@ var require_ignore = __commonJS({
456
456
  checkUnignored,
457
457
  slices
458
458
  );
459
- return cache[path28] = parent.ignored ? parent : this._rules.test(path28, checkUnignored, MODE_IGNORE);
459
+ return cache[path30] = parent.ignored ? parent : this._rules.test(path30, checkUnignored, MODE_IGNORE);
460
460
  }
461
- ignores(path28) {
462
- return this._test(path28, this._ignoreCache, false).ignored;
461
+ ignores(path30) {
462
+ return this._test(path30, this._ignoreCache, false).ignored;
463
463
  }
464
464
  createFilter() {
465
- return (path28) => !this.ignores(path28);
465
+ return (path30) => !this.ignores(path30);
466
466
  }
467
467
  filter(paths) {
468
468
  return makeArray(paths).filter(this.createFilter());
469
469
  }
470
470
  // @returns {TestResult}
471
- test(path28) {
472
- return this._test(path28, this._testCache, true);
471
+ test(path30) {
472
+ return this._test(path30, this._testCache, true);
473
473
  }
474
474
  };
475
475
  var factory = (options) => new Ignore2(options);
476
- var isPathValid = (path28) => checkPath(path28 && checkPath.convert(path28), path28, RETURN_FALSE);
476
+ var isPathValid = (path30) => checkPath(path30 && checkPath.convert(path30), path30, RETURN_FALSE);
477
477
  var setupWindows = () => {
478
478
  const makePosix = (str) => /^\\\\\?\\/.test(str) || /["<>|\u0000-\u001F]+/u.test(str) ? str : str.replace(/\\/g, "/");
479
479
  checkPath.convert = makePosix;
480
480
  const REGEX_TEST_WINDOWS_PATH_ABSOLUTE = /^[a-z]:\//i;
481
- checkPath.isNotRelative = (path28) => REGEX_TEST_WINDOWS_PATH_ABSOLUTE.test(path28) || isNotRelative(path28);
481
+ checkPath.isNotRelative = (path30) => REGEX_TEST_WINDOWS_PATH_ABSOLUTE.test(path30) || isNotRelative(path30);
482
482
  };
483
483
  if (
484
484
  // Detect `process` so that it can run in browsers.
@@ -663,7 +663,7 @@ __export(index_exports, {
663
663
  module.exports = __toCommonJS(index_exports);
664
664
 
665
665
  // src/adapters/opencode.ts
666
- var path27 = __toESM(require("path"), 1);
666
+ var path29 = __toESM(require("path"), 1);
667
667
  var import_url = require("url");
668
668
 
669
669
  // src/config/constants.ts
@@ -1141,11 +1141,11 @@ function resolveGitDir(repoRoot) {
1141
1141
  return null;
1142
1142
  }
1143
1143
  try {
1144
- const stat4 = (0, import_fs2.statSync)(gitPath);
1145
- if (stat4.isDirectory()) {
1144
+ const stat5 = (0, import_fs2.statSync)(gitPath);
1145
+ if (stat5.isDirectory()) {
1146
1146
  return gitPath;
1147
1147
  }
1148
- if (stat4.isFile()) {
1148
+ if (stat5.isFile()) {
1149
1149
  const content = (0, import_fs2.readFileSync)(gitPath, "utf-8").trim();
1150
1150
  const match = content.match(/^gitdir:\s*(.+)$/);
1151
1151
  if (match) {
@@ -2214,7 +2214,7 @@ function analyzeQueryIntent(query) {
2214
2214
  }
2215
2215
  function isTestPath(filePath) {
2216
2216
  const normalized = normalizePath(filePath);
2217
- return /(?:^|\/)(?:test|tests|__tests__|spec|specs)(?:\/|$)/u.test(normalized) || /\.(?:test|spec)\.[^/]+$/u.test(normalized);
2217
+ return /(?:^|\/)(?:test|tests|__tests__|spec|specs)(?:\/|$)/u.test(normalized) || /(?:\.(?:test|spec)|_(?:test|spec))\.[^/]+$/u.test(normalized) || /(?:^|\/)(?:test|spec)_[^/]+\.[^/]+$/u.test(normalized);
2218
2218
  }
2219
2219
  function isFixturePath(filePath) {
2220
2220
  const normalized = normalizePath(filePath);
@@ -2589,8 +2589,8 @@ function formatExactSearchHandoff(results) {
2589
2589
  }
2590
2590
  function formatContextEvidence(result, index) {
2591
2591
  const symbol = result.name ? ` ${JSON.stringify(compactEvidenceValue(result.name, 80))}` : "";
2592
- const path28 = compactEvidenceValue(result.filePath, 120);
2593
- return `[${index}] ${result.chunkType}${symbol} in ${path28}:${result.startLine}-${result.endLine} (score ${result.score.toFixed(2)})`;
2592
+ const path30 = compactEvidenceValue(result.filePath, 120);
2593
+ return `[${index}] ${result.chunkType}${symbol} in ${path30}:${result.startLine}-${result.endLine} (score ${result.score.toFixed(2)})`;
2594
2594
  }
2595
2595
  function formatContextPack(heading, selected, candidateCount, duplicateCount, limitOmittedCount, budgetOmittedCount, includeExactSearchHandoff) {
2596
2596
  const lines = selected.map((result, index) => formatContextEvidence(result, index + 1));
@@ -3743,8 +3743,8 @@ async function* walkDirectory(dir, projectRoot, includePatterns, excludePatterns
3743
3743
  if (entry.isDirectory()) {
3744
3744
  subdirs.push({ fullPath, relativePath });
3745
3745
  } else if (entry.isFile()) {
3746
- const stat4 = await import_fs6.promises.stat(fullPath);
3747
- if (stat4.size > maxFileSize) {
3746
+ const stat5 = await import_fs6.promises.stat(fullPath);
3747
+ if (stat5.size > maxFileSize) {
3748
3748
  skipped.push({ path: relativePath, reason: "too_large" });
3749
3749
  continue;
3750
3750
  }
@@ -3762,7 +3762,7 @@ async function* walkDirectory(dir, projectRoot, includePatterns, excludePatterns
3762
3762
  }
3763
3763
  }
3764
3764
  if (matched) {
3765
- filesInDir.push({ path: fullPath, size: stat4.size });
3765
+ filesInDir.push({ path: fullPath, size: stat5.size });
3766
3766
  }
3767
3767
  }
3768
3768
  }
@@ -3819,8 +3819,8 @@ async function collectFiles(projectRoot, includePatterns, excludePatterns, maxFi
3819
3819
  }
3820
3820
  for (const resolvedKbRoot of normalizedRoots) {
3821
3821
  try {
3822
- const stat4 = await import_fs6.promises.stat(resolvedKbRoot);
3823
- if (!stat4.isDirectory()) {
3822
+ const stat5 = await import_fs6.promises.stat(resolvedKbRoot);
3823
+ if (!stat5.isDirectory()) {
3824
3824
  skipped.push({ path: resolvedKbRoot, reason: "excluded" });
3825
3825
  continue;
3826
3826
  }
@@ -3854,7 +3854,7 @@ function getErrorMessage(error) {
3854
3854
  return error instanceof Error ? error.message : String(error);
3855
3855
  }
3856
3856
  function runCommand(file, args, options) {
3857
- return new Promise((resolve15, reject) => {
3857
+ return new Promise((resolve17, reject) => {
3858
3858
  childProcess.execFile(
3859
3859
  file,
3860
3860
  args,
@@ -3864,7 +3864,7 @@ function runCommand(file, args, options) {
3864
3864
  reject(error);
3865
3865
  return;
3866
3866
  }
3867
- resolve15(stdout);
3867
+ resolve17(stdout);
3868
3868
  }
3869
3869
  );
3870
3870
  });
@@ -4009,10 +4009,10 @@ function safeFailureMessage(error) {
4009
4009
  }
4010
4010
  function cancellableDelay(delayMs, signal) {
4011
4011
  if (signal.aborted) return Promise.reject(new AutoIndexCancelledError());
4012
- return new Promise((resolve15, reject) => {
4012
+ return new Promise((resolve17, reject) => {
4013
4013
  const timer = setTimeout(() => {
4014
4014
  signal.removeEventListener("abort", onAbort);
4015
- resolve15();
4015
+ resolve17();
4016
4016
  }, delayMs);
4017
4017
  timer.unref?.();
4018
4018
  const onAbort = () => {
@@ -4024,15 +4024,15 @@ function cancellableDelay(delayMs, signal) {
4024
4024
  }
4025
4025
  function withTimeout(promise, timeoutMs) {
4026
4026
  if (timeoutMs <= 0) return Promise.resolve(void 0);
4027
- return new Promise((resolve15) => {
4028
- const timer = setTimeout(() => resolve15(void 0), timeoutMs);
4027
+ return new Promise((resolve17) => {
4028
+ const timer = setTimeout(() => resolve17(void 0), timeoutMs);
4029
4029
  timer.unref?.();
4030
4030
  void promise.then((value) => {
4031
4031
  clearTimeout(timer);
4032
- resolve15(value);
4032
+ resolve17(value);
4033
4033
  }, () => {
4034
4034
  clearTimeout(timer);
4035
- resolve15(void 0);
4035
+ resolve17(void 0);
4036
4036
  });
4037
4037
  });
4038
4038
  }
@@ -4414,17 +4414,17 @@ var AutoIndexCoordinator = class {
4414
4414
  }
4415
4415
  }
4416
4416
  waitForBatteryRetry(delayMs) {
4417
- return new Promise((resolve15) => {
4417
+ return new Promise((resolve17) => {
4418
4418
  const timer = setTimeout(() => {
4419
4419
  if (this.batteryRetryTimer === timer) {
4420
4420
  this.batteryRetryTimer = null;
4421
4421
  this.resolveBatteryRetry = null;
4422
4422
  }
4423
- resolve15();
4423
+ resolve17();
4424
4424
  }, delayMs);
4425
4425
  timer.unref?.();
4426
4426
  this.batteryRetryTimer = timer;
4427
- this.resolveBatteryRetry = resolve15;
4427
+ this.resolveBatteryRetry = resolve17;
4428
4428
  });
4429
4429
  }
4430
4430
  cancelBatteryRetry() {
@@ -4432,9 +4432,9 @@ var AutoIndexCoordinator = class {
4432
4432
  clearTimeout(this.batteryRetryTimer);
4433
4433
  this.batteryRetryTimer = null;
4434
4434
  }
4435
- const resolve15 = this.resolveBatteryRetry;
4435
+ const resolve17 = this.resolveBatteryRetry;
4436
4436
  this.resolveBatteryRetry = null;
4437
- resolve15?.();
4437
+ resolve17?.();
4438
4438
  }
4439
4439
  finishBatteryCheck(batteryCheck) {
4440
4440
  if (this.batteryCheck !== batteryCheck) return;
@@ -4639,7 +4639,7 @@ function pTimeout(promise, options) {
4639
4639
  } = options;
4640
4640
  let timer;
4641
4641
  let abortHandler;
4642
- const wrappedPromise = new Promise((resolve15, reject) => {
4642
+ const wrappedPromise = new Promise((resolve17, reject) => {
4643
4643
  if (typeof milliseconds !== "number" || Math.sign(milliseconds) !== 1) {
4644
4644
  throw new TypeError(`Expected \`milliseconds\` to be a positive number, got \`${milliseconds}\``);
4645
4645
  }
@@ -4653,7 +4653,7 @@ function pTimeout(promise, options) {
4653
4653
  };
4654
4654
  signal.addEventListener("abort", abortHandler, { once: true });
4655
4655
  }
4656
- promise.then(resolve15, reject);
4656
+ promise.then(resolve17, reject);
4657
4657
  if (milliseconds === Number.POSITIVE_INFINITY) {
4658
4658
  return;
4659
4659
  }
@@ -4661,7 +4661,7 @@ function pTimeout(promise, options) {
4661
4661
  timer = customTimers.setTimeout.call(void 0, () => {
4662
4662
  if (fallback) {
4663
4663
  try {
4664
- resolve15(fallback());
4664
+ resolve17(fallback());
4665
4665
  } catch (error) {
4666
4666
  reject(error);
4667
4667
  }
@@ -4671,7 +4671,7 @@ function pTimeout(promise, options) {
4671
4671
  promise.cancel();
4672
4672
  }
4673
4673
  if (message === false) {
4674
- resolve15();
4674
+ resolve17();
4675
4675
  } else if (message instanceof Error) {
4676
4676
  reject(message);
4677
4677
  } else {
@@ -5073,7 +5073,7 @@ var PQueue = class extends import_index.default {
5073
5073
  // Assign unique ID if not provided
5074
5074
  id: options.id ?? (this.#idAssigner++).toString()
5075
5075
  };
5076
- return new Promise((resolve15, reject) => {
5076
+ return new Promise((resolve17, reject) => {
5077
5077
  const taskSymbol = /* @__PURE__ */ Symbol(`task-${options.id}`);
5078
5078
  let cleanupQueueAbortHandler = () => void 0;
5079
5079
  const run = async () => {
@@ -5113,7 +5113,7 @@ var PQueue = class extends import_index.default {
5113
5113
  })]);
5114
5114
  }
5115
5115
  const result = await operation;
5116
- resolve15(result);
5116
+ resolve17(result);
5117
5117
  this.emit("completed", result);
5118
5118
  } catch (error) {
5119
5119
  reject(error);
@@ -5301,13 +5301,13 @@ var PQueue = class extends import_index.default {
5301
5301
  });
5302
5302
  }
5303
5303
  async #onEvent(event, filter) {
5304
- return new Promise((resolve15) => {
5304
+ return new Promise((resolve17) => {
5305
5305
  const listener = () => {
5306
5306
  if (filter && !filter()) {
5307
5307
  return;
5308
5308
  }
5309
5309
  this.off(event, listener);
5310
- resolve15();
5310
+ resolve17();
5311
5311
  };
5312
5312
  this.on(event, listener);
5313
5313
  });
@@ -5593,7 +5593,7 @@ async function onAttemptFailure({ error, attemptNumber, retriesConsumed, startTi
5593
5593
  const finalDelay = Math.min(delayTime, remainingTime);
5594
5594
  options.signal?.throwIfAborted();
5595
5595
  if (finalDelay > 0) {
5596
- await new Promise((resolve15, reject) => {
5596
+ await new Promise((resolve17, reject) => {
5597
5597
  const onAbort = () => {
5598
5598
  clearTimeout(timeoutToken);
5599
5599
  options.signal?.removeEventListener("abort", onAbort);
@@ -5601,7 +5601,7 @@ async function onAttemptFailure({ error, attemptNumber, retriesConsumed, startTi
5601
5601
  };
5602
5602
  const timeoutToken = setTimeout(() => {
5603
5603
  options.signal?.removeEventListener("abort", onAbort);
5604
- resolve15();
5604
+ resolve17();
5605
5605
  }, finalDelay);
5606
5606
  if (options.unref) {
5607
5607
  timeoutToken.unref?.();
@@ -6407,85 +6407,6 @@ function createEmbeddingProvider(configuredProviderInfo) {
6407
6407
  }
6408
6408
  }
6409
6409
 
6410
- // src/rerank/index.ts
6411
- function createReranker(config) {
6412
- if (!config.enabled) {
6413
- return new NoOpReranker();
6414
- }
6415
- return new SiliconFlowReranker(config);
6416
- }
6417
- var NoOpReranker = class {
6418
- isAvailable() {
6419
- return false;
6420
- }
6421
- async rerank(_query, documents, _topN) {
6422
- return {
6423
- results: documents.map((_, index) => ({ index, relevanceScore: 0 }))
6424
- };
6425
- }
6426
- };
6427
- var SiliconFlowReranker = class {
6428
- config;
6429
- constructor(config) {
6430
- this.config = config;
6431
- }
6432
- isAvailable() {
6433
- return this.config.enabled && !!this.config.baseUrl && !!this.config.model;
6434
- }
6435
- async rerank(query, documents, topN) {
6436
- if (documents.length === 0) {
6437
- return { results: [] };
6438
- }
6439
- const headers = {
6440
- "Content-Type": "application/json"
6441
- };
6442
- if (this.config.apiKey) {
6443
- headers["Authorization"] = `Bearer ${this.config.apiKey}`;
6444
- }
6445
- const baseUrl = this.config.baseUrl;
6446
- if (!baseUrl) {
6447
- throw new Error("Reranker baseUrl is required. Configure reranker.baseUrl in your codebase-index.json.");
6448
- }
6449
- const timeoutMs = this.config.timeoutMs ?? 3e4;
6450
- const controller = new AbortController();
6451
- const timeout = setTimeout(() => controller.abort(), timeoutMs);
6452
- try {
6453
- const response = await fetch(`${baseUrl}/rerank`, {
6454
- method: "POST",
6455
- headers,
6456
- body: JSON.stringify({
6457
- model: this.config.model,
6458
- query,
6459
- documents,
6460
- top_n: topN ?? this.config.topN ?? 20,
6461
- return_documents: false
6462
- }),
6463
- signal: controller.signal
6464
- });
6465
- clearTimeout(timeout);
6466
- if (!response.ok) {
6467
- const errorText = await response.text();
6468
- throw new Error(`Rerank API error: ${response.status} - ${errorText}`);
6469
- }
6470
- const data = await response.json();
6471
- return {
6472
- results: data.results.map((r) => ({
6473
- index: r.index,
6474
- relevanceScore: r.relevance_score,
6475
- document: r.document?.text
6476
- })),
6477
- tokensUsed: data.meta?.tokens?.input_tokens
6478
- };
6479
- } catch (error) {
6480
- clearTimeout(timeout);
6481
- if (error instanceof Error && error.name === "AbortError") {
6482
- throw new Error(`Rerank API request timed out after ${timeoutMs}ms`);
6483
- }
6484
- throw error;
6485
- }
6486
- }
6487
- };
6488
-
6489
6410
  // src/utils/cost.ts
6490
6411
  function estimateChunksFromFiles(files) {
6491
6412
  let totalChunks = 0;
@@ -8046,8 +7967,8 @@ async function isWorktreeRegistered(projectRoot, worktreePath) {
8046
7967
  return false;
8047
7968
  }
8048
7969
  function isPathWithinRoot(filePath, rootPath) {
8049
- const relative12 = path15.relative(path15.resolve(rootPath), path15.resolve(filePath));
8050
- return relative12 === "" || !relative12.startsWith(`..${path15.sep}`) && relative12 !== ".." && !path15.isAbsolute(relative12);
7970
+ const relative14 = path15.relative(path15.resolve(rootPath), path15.resolve(filePath));
7971
+ return relative14 === "" || !relative14.startsWith(`..${path15.sep}`) && relative14 !== ".." && !path15.isAbsolute(relative14);
8051
7972
  }
8052
7973
  async function pruneExactMissingWorktreeRegistration(projectRoot, worktreePath) {
8053
7974
  if (await pathExists(worktreePath)) return false;
@@ -8424,11 +8345,11 @@ function normalizeFiles(rawFiles, projectRoot) {
8424
8345
  for (const raw of rawFiles) {
8425
8346
  if (raw.length === 0) continue;
8426
8347
  const absolute = path16.resolve(root, raw);
8427
- const relative12 = path16.relative(root, absolute);
8428
- if (path16.isAbsolute(raw) || relative12 === ".." || relative12.startsWith(`..${path16.sep}`) || path16.isAbsolute(relative12)) {
8348
+ const relative14 = path16.relative(root, absolute);
8349
+ if (path16.isAbsolute(raw) || relative14 === ".." || relative14.startsWith(`..${path16.sep}`) || path16.isAbsolute(relative14)) {
8429
8350
  throw new Error(`Changed file escapes the project root: ${JSON.stringify(raw)}`);
8430
8351
  }
8431
- const cleaned = relative12.startsWith(`.${path16.sep}`) ? relative12.slice(2) : relative12;
8352
+ const cleaned = relative14.startsWith(`.${path16.sep}`) ? relative14.slice(2) : relative14;
8432
8353
  if (!seen.has(cleaned)) {
8433
8354
  seen.add(cleaned);
8434
8355
  result.push(cleaned);
@@ -8660,7 +8581,8 @@ function rankHybridResults(query, semanticResults, keywordResults, options) {
8660
8581
  return cached;
8661
8582
  }
8662
8583
  }
8663
- const overfetchLimit = Math.max(options.limit * 4, options.limit);
8584
+ const overfetchFactor = prioritizeSourcePaths ? 12 : 4;
8585
+ const overfetchLimit = Math.max(options.limit * overfetchFactor, options.limit);
8664
8586
  const fused = options.fusionStrategy === "rrf" ? fuseResultsRrf(semanticResults, keywordResults, options.rrfK, overfetchLimit) : fuseResultsWeighted(semanticResults, keywordResults, options.hybridWeight, overfetchLimit);
8665
8587
  const rerankPoolLimit = Math.max(overfetchLimit, options.rerankTopN * 3, options.limit * 6);
8666
8588
  const rerankPool = fused.slice(0, rerankPoolLimit);
@@ -9046,6 +8968,29 @@ function extractPrimaryIdentifierQueryHint(query) {
9046
8968
  const best = codeTerms.find((term) => term.length >= 6);
9047
8969
  return best ?? null;
9048
8970
  }
8971
+ function pathSegmentsForAffinityMatch(filePath) {
8972
+ const normalizedPath2 = normalizeRankingText(filePath).replace(/\\/g, "/");
8973
+ const segments = normalizedPath2.split("/").filter((segment) => segment.length > 0);
8974
+ if (segments.length === 0) {
8975
+ return [];
8976
+ }
8977
+ const basename9 = segments[segments.length - 1] ?? "";
8978
+ const basenameWithoutExt = basename9.replace(/\.[^/.]+$/u, "");
8979
+ const normalizedSegments = segments.map((segment) => segment.toLowerCase());
8980
+ return Array.from(/* @__PURE__ */ new Set([
8981
+ ...normalizedSegments,
8982
+ basenameWithoutExt.toLowerCase()
8983
+ ]));
8984
+ }
8985
+ function hasModuleAffinity(filePath, exactIdentifierVariants) {
8986
+ const haystack = pathSegmentsForAffinityMatch(filePath);
8987
+ return exactIdentifierVariants.some((variant) => {
8988
+ if (!variant || variant.length < 2) {
8989
+ return false;
8990
+ }
8991
+ return haystack.includes(variant);
8992
+ });
8993
+ }
9049
8994
  var FILE_PATH_HINT_EXTENSIONS = [
9050
8995
  "ts",
9051
8996
  "tsx",
@@ -9121,10 +9066,13 @@ function buildDeterministicIdentifierPass(query, candidates, limit, prioritizeSo
9121
9066
  ).map((candidate) => {
9122
9067
  const nameLower = (candidate.metadata.name ?? "").toLowerCase();
9123
9068
  const pathLower = candidate.metadata.filePath.toLowerCase();
9124
- let maxMatch = 0;
9125
- const nameMatchesPrimary = primaryVariants.some(
9069
+ const exactIdentifierVariants = primaryVariants.filter((value) => value.length >= 2);
9070
+ const exactMatch = exactIdentifierVariants.some(
9126
9071
  (variant) => nameLower === variant || nameLower.replace(/[^a-z0-9]/g, "") === variant.replace(/[^a-z0-9]/g, "")
9127
9072
  );
9073
+ let maxMatch = 0;
9074
+ const nameMatchesPrimary = exactMatch;
9075
+ const pathAffinity = exactMatch ? hasModuleAffinity(candidate.metadata.filePath, exactIdentifierVariants) : false;
9128
9076
  const pathMatchesFileHint = filePathHint ? pathMatchesHint(candidate.metadata.filePath, filePathHint) : false;
9129
9077
  for (const hint of hints) {
9130
9078
  const variants = normalizeIdentifierVariants(hint);
@@ -9145,12 +9093,17 @@ function buildDeterministicIdentifierPass(query, candidates, limit, prioritizeSo
9145
9093
  candidate,
9146
9094
  maxMatch,
9147
9095
  pathMatchesFileHint,
9148
- nameMatchesPrimary
9096
+ nameMatchesPrimary,
9097
+ pathAffinity
9149
9098
  };
9150
9099
  }).filter((entry) => entry.maxMatch >= 0.7).sort((a, b) => {
9151
9100
  const aAnchored = a.pathMatchesFileHint && a.nameMatchesPrimary ? 1 : 0;
9152
9101
  const bAnchored = b.pathMatchesFileHint && b.nameMatchesPrimary ? 1 : 0;
9153
9102
  if (aAnchored !== bAnchored) return bAnchored - aAnchored;
9103
+ if (a.nameMatchesPrimary !== b.nameMatchesPrimary) {
9104
+ return b.nameMatchesPrimary ? 1 : -1;
9105
+ }
9106
+ if (a.pathAffinity !== b.pathAffinity) return b.pathAffinity ? 1 : -1;
9154
9107
  if (b.maxMatch !== a.maxMatch) return b.maxMatch - a.maxMatch;
9155
9108
  if (b.candidate.score !== a.candidate.score) return b.candidate.score - a.candidate.score;
9156
9109
  return a.candidate.id.localeCompare(b.candidate.id);
@@ -10140,7 +10093,6 @@ var Indexer = class _Indexer {
10140
10093
  database = null;
10141
10094
  provider = null;
10142
10095
  configuredProviderInfo = null;
10143
- reranker = null;
10144
10096
  fileHashCache = /* @__PURE__ */ new Map();
10145
10097
  fileHashCachePath = "";
10146
10098
  failedBatchesPath = "";
@@ -10300,7 +10252,6 @@ var Indexer = class _Indexer {
10300
10252
  this.database = null;
10301
10253
  this.provider = null;
10302
10254
  this.configuredProviderInfo = null;
10303
- this.reranker = null;
10304
10255
  this.indexCompatibility = null;
10305
10256
  this.initializationMode = "none";
10306
10257
  this.readIssues = [];
@@ -11030,7 +10981,7 @@ var Indexer = class _Indexer {
11030
10981
  await options.queue.onSizeLessThan(Math.max(1, options.providerRateLimits.concurrency));
11031
10982
  const task = options.queue.add(async () => {
11032
10983
  if (options.rateLimitState.backoffMs > 0) {
11033
- await new Promise((resolve15) => setTimeout(resolve15, options.rateLimitState.backoffMs));
10984
+ await new Promise((resolve17) => setTimeout(resolve17, options.rateLimitState.backoffMs));
11034
10985
  }
11035
10986
  try {
11036
10987
  const embeddingResult = await pRetry(
@@ -11597,15 +11548,6 @@ var Indexer = class _Indexer {
11597
11548
  rerankerEnabled: this.config.reranker?.enabled ?? false
11598
11549
  });
11599
11550
  this.provider = createEmbeddingProvider(this.configuredProviderInfo);
11600
- if (this.config.reranker?.enabled) {
11601
- this.reranker = createReranker(this.config.reranker);
11602
- if (this.reranker.isAvailable()) {
11603
- this.logger.info("Reranker initialized", {
11604
- model: this.config.reranker.model,
11605
- baseUrl: this.config.reranker.baseUrl
11606
- });
11607
- }
11608
- }
11609
11551
  const dimensions = this.configuredProviderInfo.modelInfo.dimensions;
11610
11552
  const storePath = path19.join(this.indexPath, "vectors");
11611
11553
  const vectorMetadataPath = `${storePath}.meta.json`;
@@ -13010,6 +12952,7 @@ var Indexer = class _Indexer {
13010
12952
  const filterByBranch = options?.filterByBranch ?? true;
13011
12953
  const sourceIntent = options?.definitionIntent === true || classifyQueryIntentRaw(query) === "source";
13012
12954
  const identifierHints = extractIdentifierHints(query);
12955
+ const candidateLimit = maxResults * (sourceIntent ? 12 : 4);
13013
12956
  this.logger.search("debug", "Starting search", {
13014
12957
  query,
13015
12958
  maxResults,
@@ -13046,7 +12989,7 @@ var Indexer = class _Indexer {
13046
12989
  const semanticCandidates = embedding ? this.searchSemanticCandidates(
13047
12990
  store,
13048
12991
  embedding,
13049
- maxResults * 4,
12992
+ candidateLimit,
13050
12993
  branchChunkIds,
13051
12994
  shouldPrefilterByBranch
13052
12995
  ) : [];
@@ -13054,7 +12997,7 @@ var Indexer = class _Indexer {
13054
12997
  const keywordStartTime = import_perf_hooks.performance.now();
13055
12998
  const keywordCandidates = await this.keywordSearch(
13056
12999
  query,
13057
- maxResults * 4,
13000
+ candidateLimit,
13058
13001
  store,
13059
13002
  invertedIndex,
13060
13003
  branchChunkIds,
@@ -13812,9 +13755,9 @@ var Indexer = class _Indexer {
13812
13755
  this.requireReadableComponents(readIssues, "database");
13813
13756
  let shortest = [];
13814
13757
  for (const branchKey of this.getBranchCatalogKeys()) {
13815
- const path28 = database.findShortestPath(fromName, toName, branchKey, maxDepth);
13816
- if (path28.length > 0 && (shortest.length === 0 || path28.length < shortest.length)) {
13817
- shortest = path28;
13758
+ const path30 = database.findShortestPath(fromName, toName, branchKey, maxDepth);
13759
+ if (path30.length > 0 && (shortest.length === 0 || path30.length < shortest.length)) {
13760
+ shortest = path30;
13818
13761
  }
13819
13762
  }
13820
13763
  return shortest.map((hop) => this.resolveFilePathRecord(hop));
@@ -13862,13 +13805,13 @@ var Indexer = class _Indexer {
13862
13805
  }
13863
13806
  }
13864
13807
  if (!found) continue;
13865
- const path28 = [];
13808
+ const path30 = [];
13866
13809
  let currentSymbolId = toSymbolId;
13867
13810
  while (true) {
13868
13811
  const symbol = symbolsById.get(currentSymbolId);
13869
13812
  if (!symbol) break;
13870
13813
  const parent = parentBySymbolId.get(currentSymbolId);
13871
- path28.push({
13814
+ path30.push({
13872
13815
  symbolId: symbol.id,
13873
13816
  symbolName: symbol.name,
13874
13817
  filePath: symbol.filePath,
@@ -13878,9 +13821,9 @@ var Indexer = class _Indexer {
13878
13821
  if (!parent) break;
13879
13822
  currentSymbolId = parent.parentId;
13880
13823
  }
13881
- path28.reverse();
13882
- if (path28.length > 0 && (shortest.length === 0 || path28.length < shortest.length)) {
13883
- shortest = path28;
13824
+ path30.reverse();
13825
+ if (path30.length > 0 && (shortest.length === 0 || path30.length < shortest.length)) {
13826
+ shortest = path30;
13884
13827
  }
13885
13828
  }
13886
13829
  return shortest.map((hop) => this.resolveFilePathRecord(hop));
@@ -14216,7 +14159,6 @@ var Indexer = class _Indexer {
14216
14159
  this.store = null;
14217
14160
  this.invertedIndex = null;
14218
14161
  this.provider = null;
14219
- this.reranker = null;
14220
14162
  this.configuredProviderInfo = null;
14221
14163
  this.indexCompatibility = null;
14222
14164
  this.initializationMode = "none";
@@ -14541,12 +14483,12 @@ async function getCallGraphPath(projectRoot, host, from, to, maxDepth, fromFileP
14541
14483
  if (fromResolution.status !== "resolved" || toResolution.status !== "resolved") {
14542
14484
  return { from: fromResolution, to: toResolution, path: [] };
14543
14485
  }
14544
- const path28 = await indexer.findCallPathBySymbolIds(
14486
+ const path30 = await indexer.findCallPathBySymbolIds(
14545
14487
  fromResolution.symbolId,
14546
14488
  toResolution.symbolId,
14547
14489
  maxDepth
14548
14490
  );
14549
- return { from: fromResolution, to: toResolution, path: path28 };
14491
+ return { from: fromResolution, to: toResolution, path: path30 };
14550
14492
  }
14551
14493
  async function runIndexCodebase(projectRoot, host, args, onProgress) {
14552
14494
  const root = getProjectRoot(projectRoot, host);
@@ -14772,8 +14714,8 @@ function addKnowledgeBase(projectRoot, host, knowledgeBasePath) {
14772
14714
  }
14773
14715
  }
14774
14716
  try {
14775
- const stat4 = (0, import_fs13.statSync)(normalizedPath2);
14776
- if (!stat4.isDirectory()) {
14717
+ const stat5 = (0, import_fs13.statSync)(normalizedPath2);
14718
+ if (!stat5.isDirectory()) {
14777
14719
  return `Error: Path is not a directory: ${normalizedPath2}`;
14778
14720
  }
14779
14721
  } catch (error) {
@@ -14821,8 +14763,8 @@ function listKnowledgeBases(projectRoot, host) {
14821
14763
  `;
14822
14764
  if (exists) {
14823
14765
  try {
14824
- const stat4 = (0, import_fs13.statSync)(resolvedPath);
14825
- result += ` Type: ${stat4.isDirectory() ? "Directory" : "File"}
14766
+ const stat5 = (0, import_fs13.statSync)(resolvedPath);
14767
+ result += ` Type: ${stat5.isDirectory() ? "Directory" : "File"}
14826
14768
  `;
14827
14769
  } catch {
14828
14770
  }
@@ -14955,7 +14897,7 @@ var ReaddirpStream = class extends import_node_stream.Readable {
14955
14897
  this._directoryFilter = normalizeFilter(opts.directoryFilter);
14956
14898
  const statMethod = opts.lstat ? import_promises.lstat : import_promises.stat;
14957
14899
  if (wantBigintFsStats) {
14958
- this._stat = (path28) => statMethod(path28, { bigint: true });
14900
+ this._stat = (path30) => statMethod(path30, { bigint: true });
14959
14901
  } else {
14960
14902
  this._stat = statMethod;
14961
14903
  }
@@ -14980,8 +14922,8 @@ var ReaddirpStream = class extends import_node_stream.Readable {
14980
14922
  const par = this.parent;
14981
14923
  const fil = par && par.files;
14982
14924
  if (fil && fil.length > 0) {
14983
- const { path: path28, depth } = par;
14984
- const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path28));
14925
+ const { path: path30, depth } = par;
14926
+ const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path30));
14985
14927
  const awaited = await Promise.all(slice);
14986
14928
  for (const entry of awaited) {
14987
14929
  if (!entry)
@@ -15021,20 +14963,20 @@ var ReaddirpStream = class extends import_node_stream.Readable {
15021
14963
  this.reading = false;
15022
14964
  }
15023
14965
  }
15024
- async _exploreDir(path28, depth) {
14966
+ async _exploreDir(path30, depth) {
15025
14967
  let files;
15026
14968
  try {
15027
- files = await (0, import_promises.readdir)(path28, this._rdOptions);
14969
+ files = await (0, import_promises.readdir)(path30, this._rdOptions);
15028
14970
  } catch (error) {
15029
14971
  this._onError(error);
15030
14972
  }
15031
- return { files, depth, path: path28 };
14973
+ return { files, depth, path: path30 };
15032
14974
  }
15033
- async _formatEntry(dirent, path28) {
14975
+ async _formatEntry(dirent, path30) {
15034
14976
  let entry;
15035
14977
  const basename9 = this._isDirent ? dirent.name : dirent;
15036
14978
  try {
15037
- const fullPath = (0, import_node_path.resolve)((0, import_node_path.join)(path28, basename9));
14979
+ const fullPath = (0, import_node_path.resolve)((0, import_node_path.join)(path30, basename9));
15038
14980
  entry = { path: (0, import_node_path.relative)(this._root, fullPath), fullPath, basename: basename9 };
15039
14981
  entry[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath);
15040
14982
  } catch (err) {
@@ -15434,16 +15376,16 @@ var delFromSet = (main, prop, item) => {
15434
15376
  };
15435
15377
  var isEmptySet = (val) => val instanceof Set ? val.size === 0 : !val;
15436
15378
  var FsWatchInstances = /* @__PURE__ */ new Map();
15437
- function createFsWatchInstance(path28, options, listener, errHandler, emitRaw) {
15379
+ function createFsWatchInstance(path30, options, listener, errHandler, emitRaw) {
15438
15380
  const handleEvent = (rawEvent, evPath) => {
15439
- listener(path28);
15440
- emitRaw(rawEvent, evPath, { watchedPath: path28 });
15441
- if (evPath && path28 !== evPath) {
15442
- fsWatchBroadcast(sp.resolve(path28, evPath), KEY_LISTENERS, sp.join(path28, evPath));
15381
+ listener(path30);
15382
+ emitRaw(rawEvent, evPath, { watchedPath: path30 });
15383
+ if (evPath && path30 !== evPath) {
15384
+ fsWatchBroadcast(sp.resolve(path30, evPath), KEY_LISTENERS, sp.join(path30, evPath));
15443
15385
  }
15444
15386
  };
15445
15387
  try {
15446
- return (0, import_node_fs.watch)(path28, {
15388
+ return (0, import_node_fs.watch)(path30, {
15447
15389
  persistent: options.persistent
15448
15390
  }, handleEvent);
15449
15391
  } catch (error) {
@@ -15459,12 +15401,12 @@ var fsWatchBroadcast = (fullPath, listenerType, val1, val2, val3) => {
15459
15401
  listener(val1, val2, val3);
15460
15402
  });
15461
15403
  };
15462
- var setFsWatchListener = (path28, fullPath, options, handlers) => {
15404
+ var setFsWatchListener = (path30, fullPath, options, handlers) => {
15463
15405
  const { listener, errHandler, rawEmitter } = handlers;
15464
15406
  let cont = FsWatchInstances.get(fullPath);
15465
15407
  let watcher;
15466
15408
  if (!options.persistent) {
15467
- watcher = createFsWatchInstance(path28, options, listener, errHandler, rawEmitter);
15409
+ watcher = createFsWatchInstance(path30, options, listener, errHandler, rawEmitter);
15468
15410
  if (!watcher)
15469
15411
  return;
15470
15412
  return watcher.close.bind(watcher);
@@ -15475,7 +15417,7 @@ var setFsWatchListener = (path28, fullPath, options, handlers) => {
15475
15417
  addAndConvert(cont, KEY_RAW, rawEmitter);
15476
15418
  } else {
15477
15419
  watcher = createFsWatchInstance(
15478
- path28,
15420
+ path30,
15479
15421
  options,
15480
15422
  fsWatchBroadcast.bind(null, fullPath, KEY_LISTENERS),
15481
15423
  errHandler,
@@ -15490,7 +15432,7 @@ var setFsWatchListener = (path28, fullPath, options, handlers) => {
15490
15432
  cont.watcherUnusable = true;
15491
15433
  if (isWindows && error.code === "EPERM") {
15492
15434
  try {
15493
- const fd = await (0, import_promises2.open)(path28, "r");
15435
+ const fd = await (0, import_promises2.open)(path30, "r");
15494
15436
  await fd.close();
15495
15437
  broadcastErr(error);
15496
15438
  } catch (err) {
@@ -15521,7 +15463,7 @@ var setFsWatchListener = (path28, fullPath, options, handlers) => {
15521
15463
  };
15522
15464
  };
15523
15465
  var FsWatchFileInstances = /* @__PURE__ */ new Map();
15524
- var setFsWatchFileListener = (path28, fullPath, options, handlers) => {
15466
+ var setFsWatchFileListener = (path30, fullPath, options, handlers) => {
15525
15467
  const { listener, rawEmitter } = handlers;
15526
15468
  let cont = FsWatchFileInstances.get(fullPath);
15527
15469
  const copts = cont && cont.options;
@@ -15543,7 +15485,7 @@ var setFsWatchFileListener = (path28, fullPath, options, handlers) => {
15543
15485
  });
15544
15486
  const currmtime = curr.mtimeMs;
15545
15487
  if (curr.size !== prev.size || currmtime > prev.mtimeMs || currmtime === 0) {
15546
- foreach(cont.listeners, (listener2) => listener2(path28, curr));
15488
+ foreach(cont.listeners, (listener2) => listener2(path30, curr));
15547
15489
  }
15548
15490
  })
15549
15491
  };
@@ -15573,13 +15515,13 @@ var NodeFsHandler = class {
15573
15515
  * @param listener on fs change
15574
15516
  * @returns closer for the watcher instance
15575
15517
  */
15576
- _watchWithNodeFs(path28, listener) {
15518
+ _watchWithNodeFs(path30, listener) {
15577
15519
  const opts = this.fsw.options;
15578
- const directory = sp.dirname(path28);
15579
- const basename9 = sp.basename(path28);
15520
+ const directory = sp.dirname(path30);
15521
+ const basename9 = sp.basename(path30);
15580
15522
  const parent = this.fsw._getWatchedDir(directory);
15581
15523
  parent.add(basename9);
15582
- const absolutePath = sp.resolve(path28);
15524
+ const absolutePath = sp.resolve(path30);
15583
15525
  const options = {
15584
15526
  persistent: opts.persistent
15585
15527
  };
@@ -15589,12 +15531,12 @@ var NodeFsHandler = class {
15589
15531
  if (opts.usePolling) {
15590
15532
  const enableBin = opts.interval !== opts.binaryInterval;
15591
15533
  options.interval = enableBin && isBinaryPath(basename9) ? opts.binaryInterval : opts.interval;
15592
- closer = setFsWatchFileListener(path28, absolutePath, options, {
15534
+ closer = setFsWatchFileListener(path30, absolutePath, options, {
15593
15535
  listener,
15594
15536
  rawEmitter: this.fsw._emitRaw
15595
15537
  });
15596
15538
  } else {
15597
- closer = setFsWatchListener(path28, absolutePath, options, {
15539
+ closer = setFsWatchListener(path30, absolutePath, options, {
15598
15540
  listener,
15599
15541
  errHandler: this._boundHandleError,
15600
15542
  rawEmitter: this.fsw._emitRaw
@@ -15616,7 +15558,7 @@ var NodeFsHandler = class {
15616
15558
  let prevStats = stats;
15617
15559
  if (parent.has(basename9))
15618
15560
  return;
15619
- const listener = async (path28, newStats) => {
15561
+ const listener = async (path30, newStats) => {
15620
15562
  if (!this.fsw._throttle(THROTTLE_MODE_WATCH, file, 5))
15621
15563
  return;
15622
15564
  if (!newStats || newStats.mtimeMs === 0) {
@@ -15630,11 +15572,11 @@ var NodeFsHandler = class {
15630
15572
  this.fsw._emit(EV.CHANGE, file, newStats2);
15631
15573
  }
15632
15574
  if ((isMacos || isLinux || isFreeBSD) && prevStats.ino !== newStats2.ino) {
15633
- this.fsw._closeFile(path28);
15575
+ this.fsw._closeFile(path30);
15634
15576
  prevStats = newStats2;
15635
15577
  const closer2 = this._watchWithNodeFs(file, listener);
15636
15578
  if (closer2)
15637
- this.fsw._addPathCloser(path28, closer2);
15579
+ this.fsw._addPathCloser(path30, closer2);
15638
15580
  } else {
15639
15581
  prevStats = newStats2;
15640
15582
  }
@@ -15666,7 +15608,7 @@ var NodeFsHandler = class {
15666
15608
  * @param item basename of this item
15667
15609
  * @returns true if no more processing is needed for this entry.
15668
15610
  */
15669
- async _handleSymlink(entry, directory, path28, item) {
15611
+ async _handleSymlink(entry, directory, path30, item) {
15670
15612
  if (this.fsw.closed) {
15671
15613
  return;
15672
15614
  }
@@ -15676,7 +15618,7 @@ var NodeFsHandler = class {
15676
15618
  this.fsw._incrReadyCount();
15677
15619
  let linkPath;
15678
15620
  try {
15679
- linkPath = await (0, import_promises2.realpath)(path28);
15621
+ linkPath = await (0, import_promises2.realpath)(path30);
15680
15622
  } catch (e) {
15681
15623
  this.fsw._emitReady();
15682
15624
  return true;
@@ -15686,12 +15628,12 @@ var NodeFsHandler = class {
15686
15628
  if (dir.has(item)) {
15687
15629
  if (this.fsw._symlinkPaths.get(full) !== linkPath) {
15688
15630
  this.fsw._symlinkPaths.set(full, linkPath);
15689
- this.fsw._emit(EV.CHANGE, path28, entry.stats);
15631
+ this.fsw._emit(EV.CHANGE, path30, entry.stats);
15690
15632
  }
15691
15633
  } else {
15692
15634
  dir.add(item);
15693
15635
  this.fsw._symlinkPaths.set(full, linkPath);
15694
- this.fsw._emit(EV.ADD, path28, entry.stats);
15636
+ this.fsw._emit(EV.ADD, path30, entry.stats);
15695
15637
  }
15696
15638
  this.fsw._emitReady();
15697
15639
  return true;
@@ -15721,9 +15663,9 @@ var NodeFsHandler = class {
15721
15663
  return;
15722
15664
  }
15723
15665
  const item = entry.path;
15724
- let path28 = sp.join(directory, item);
15666
+ let path30 = sp.join(directory, item);
15725
15667
  current.add(item);
15726
- if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path28, item)) {
15668
+ if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path30, item)) {
15727
15669
  return;
15728
15670
  }
15729
15671
  if (this.fsw.closed) {
@@ -15732,11 +15674,11 @@ var NodeFsHandler = class {
15732
15674
  }
15733
15675
  if (item === target || !target && !previous.has(item)) {
15734
15676
  this.fsw._incrReadyCount();
15735
- path28 = sp.join(dir, sp.relative(dir, path28));
15736
- this._addToNodeFs(path28, initialAdd, wh, depth + 1);
15677
+ path30 = sp.join(dir, sp.relative(dir, path30));
15678
+ this._addToNodeFs(path30, initialAdd, wh, depth + 1);
15737
15679
  }
15738
15680
  }).on(EV.ERROR, this._boundHandleError);
15739
- return new Promise((resolve15, reject) => {
15681
+ return new Promise((resolve17, reject) => {
15740
15682
  if (!stream)
15741
15683
  return reject();
15742
15684
  stream.once(STR_END, () => {
@@ -15745,7 +15687,7 @@ var NodeFsHandler = class {
15745
15687
  return;
15746
15688
  }
15747
15689
  const wasThrottled = throttler ? throttler.clear() : false;
15748
- resolve15(void 0);
15690
+ resolve17(void 0);
15749
15691
  previous.getChildren().filter((item) => {
15750
15692
  return item !== directory && !current.has(item);
15751
15693
  }).forEach((item) => {
@@ -15802,13 +15744,13 @@ var NodeFsHandler = class {
15802
15744
  * @param depth Child path actually targeted for watch
15803
15745
  * @param target Child path actually targeted for watch
15804
15746
  */
15805
- async _addToNodeFs(path28, initialAdd, priorWh, depth, target) {
15747
+ async _addToNodeFs(path30, initialAdd, priorWh, depth, target) {
15806
15748
  const ready = this.fsw._emitReady;
15807
- if (this.fsw._isIgnored(path28) || this.fsw.closed) {
15749
+ if (this.fsw._isIgnored(path30) || this.fsw.closed) {
15808
15750
  ready();
15809
15751
  return false;
15810
15752
  }
15811
- const wh = this.fsw._getWatchHelpers(path28);
15753
+ const wh = this.fsw._getWatchHelpers(path30);
15812
15754
  if (priorWh) {
15813
15755
  wh.filterPath = (entry) => priorWh.filterPath(entry);
15814
15756
  wh.filterDir = (entry) => priorWh.filterDir(entry);
@@ -15824,8 +15766,8 @@ var NodeFsHandler = class {
15824
15766
  const follow = this.fsw.options.followSymlinks;
15825
15767
  let closer;
15826
15768
  if (stats.isDirectory()) {
15827
- const absPath = sp.resolve(path28);
15828
- const targetPath = follow ? await (0, import_promises2.realpath)(path28) : path28;
15769
+ const absPath = sp.resolve(path30);
15770
+ const targetPath = follow ? await (0, import_promises2.realpath)(path30) : path30;
15829
15771
  if (this.fsw.closed)
15830
15772
  return;
15831
15773
  closer = await this._handleDir(wh.watchPath, stats, initialAdd, depth, target, wh, targetPath);
@@ -15835,29 +15777,29 @@ var NodeFsHandler = class {
15835
15777
  this.fsw._symlinkPaths.set(absPath, targetPath);
15836
15778
  }
15837
15779
  } else if (stats.isSymbolicLink()) {
15838
- const targetPath = follow ? await (0, import_promises2.realpath)(path28) : path28;
15780
+ const targetPath = follow ? await (0, import_promises2.realpath)(path30) : path30;
15839
15781
  if (this.fsw.closed)
15840
15782
  return;
15841
15783
  const parent = sp.dirname(wh.watchPath);
15842
15784
  this.fsw._getWatchedDir(parent).add(wh.watchPath);
15843
15785
  this.fsw._emit(EV.ADD, wh.watchPath, stats);
15844
- closer = await this._handleDir(parent, stats, initialAdd, depth, path28, wh, targetPath);
15786
+ closer = await this._handleDir(parent, stats, initialAdd, depth, path30, wh, targetPath);
15845
15787
  if (this.fsw.closed)
15846
15788
  return;
15847
15789
  if (targetPath !== void 0) {
15848
- this.fsw._symlinkPaths.set(sp.resolve(path28), targetPath);
15790
+ this.fsw._symlinkPaths.set(sp.resolve(path30), targetPath);
15849
15791
  }
15850
15792
  } else {
15851
15793
  closer = this._handleFile(wh.watchPath, stats, initialAdd);
15852
15794
  }
15853
15795
  ready();
15854
15796
  if (closer)
15855
- this.fsw._addPathCloser(path28, closer);
15797
+ this.fsw._addPathCloser(path30, closer);
15856
15798
  return false;
15857
15799
  } catch (error) {
15858
15800
  if (this.fsw._handleError(error)) {
15859
15801
  ready();
15860
- return path28;
15802
+ return path30;
15861
15803
  }
15862
15804
  }
15863
15805
  }
@@ -15889,35 +15831,35 @@ function createPattern(matcher) {
15889
15831
  if (matcher.path === string)
15890
15832
  return true;
15891
15833
  if (matcher.recursive) {
15892
- const relative12 = sp2.relative(matcher.path, string);
15893
- if (!relative12) {
15834
+ const relative14 = sp2.relative(matcher.path, string);
15835
+ if (!relative14) {
15894
15836
  return false;
15895
15837
  }
15896
- return !relative12.startsWith("..") && !sp2.isAbsolute(relative12);
15838
+ return !relative14.startsWith("..") && !sp2.isAbsolute(relative14);
15897
15839
  }
15898
15840
  return false;
15899
15841
  };
15900
15842
  }
15901
15843
  return () => false;
15902
15844
  }
15903
- function normalizePath2(path28) {
15904
- if (typeof path28 !== "string")
15845
+ function normalizePath2(path30) {
15846
+ if (typeof path30 !== "string")
15905
15847
  throw new Error("string expected");
15906
- path28 = sp2.normalize(path28);
15907
- path28 = path28.replace(/\\/g, "/");
15848
+ path30 = sp2.normalize(path30);
15849
+ path30 = path30.replace(/\\/g, "/");
15908
15850
  let prepend = false;
15909
- if (path28.startsWith("//"))
15851
+ if (path30.startsWith("//"))
15910
15852
  prepend = true;
15911
- path28 = path28.replace(DOUBLE_SLASH_RE, "/");
15853
+ path30 = path30.replace(DOUBLE_SLASH_RE, "/");
15912
15854
  if (prepend)
15913
- path28 = "/" + path28;
15914
- return path28;
15855
+ path30 = "/" + path30;
15856
+ return path30;
15915
15857
  }
15916
15858
  function matchPatterns(patterns, testString, stats) {
15917
- const path28 = normalizePath2(testString);
15859
+ const path30 = normalizePath2(testString);
15918
15860
  for (let index = 0; index < patterns.length; index++) {
15919
15861
  const pattern = patterns[index];
15920
- if (pattern(path28, stats)) {
15862
+ if (pattern(path30, stats)) {
15921
15863
  return true;
15922
15864
  }
15923
15865
  }
@@ -15955,19 +15897,19 @@ var toUnix = (string) => {
15955
15897
  }
15956
15898
  return str;
15957
15899
  };
15958
- var normalizePathToUnix = (path28) => toUnix(sp2.normalize(toUnix(path28)));
15959
- var normalizeIgnored = (cwd = "") => (path28) => {
15960
- if (typeof path28 === "string") {
15961
- return normalizePathToUnix(sp2.isAbsolute(path28) ? path28 : sp2.join(cwd, path28));
15900
+ var normalizePathToUnix = (path30) => toUnix(sp2.normalize(toUnix(path30)));
15901
+ var normalizeIgnored = (cwd = "") => (path30) => {
15902
+ if (typeof path30 === "string") {
15903
+ return normalizePathToUnix(sp2.isAbsolute(path30) ? path30 : sp2.join(cwd, path30));
15962
15904
  } else {
15963
- return path28;
15905
+ return path30;
15964
15906
  }
15965
15907
  };
15966
- var getAbsolutePath = (path28, cwd) => {
15967
- if (sp2.isAbsolute(path28)) {
15968
- return path28;
15908
+ var getAbsolutePath = (path30, cwd) => {
15909
+ if (sp2.isAbsolute(path30)) {
15910
+ return path30;
15969
15911
  }
15970
- return sp2.join(cwd, path28);
15912
+ return sp2.join(cwd, path30);
15971
15913
  };
15972
15914
  var EMPTY_SET = Object.freeze(/* @__PURE__ */ new Set());
15973
15915
  var DirEntry = class {
@@ -16032,10 +15974,10 @@ var WatchHelper = class {
16032
15974
  dirParts;
16033
15975
  followSymlinks;
16034
15976
  statMethod;
16035
- constructor(path28, follow, fsw) {
15977
+ constructor(path30, follow, fsw) {
16036
15978
  this.fsw = fsw;
16037
- const watchPath = path28;
16038
- this.path = path28 = path28.replace(REPLACER_RE, "");
15979
+ const watchPath = path30;
15980
+ this.path = path30 = path30.replace(REPLACER_RE, "");
16039
15981
  this.watchPath = watchPath;
16040
15982
  this.fullWatchPath = sp2.resolve(watchPath);
16041
15983
  this.dirParts = [];
@@ -16175,20 +16117,20 @@ var FSWatcher = class extends import_node_events.EventEmitter {
16175
16117
  this._closePromise = void 0;
16176
16118
  let paths = unifyPaths(paths_);
16177
16119
  if (cwd) {
16178
- paths = paths.map((path28) => {
16179
- const absPath = getAbsolutePath(path28, cwd);
16120
+ paths = paths.map((path30) => {
16121
+ const absPath = getAbsolutePath(path30, cwd);
16180
16122
  return absPath;
16181
16123
  });
16182
16124
  }
16183
- paths.forEach((path28) => {
16184
- this._removeIgnoredPath(path28);
16125
+ paths.forEach((path30) => {
16126
+ this._removeIgnoredPath(path30);
16185
16127
  });
16186
16128
  this._userIgnored = void 0;
16187
16129
  if (!this._readyCount)
16188
16130
  this._readyCount = 0;
16189
16131
  this._readyCount += paths.length;
16190
- Promise.all(paths.map(async (path28) => {
16191
- const res = await this._nodeFsHandler._addToNodeFs(path28, !_internal, void 0, 0, _origAdd);
16132
+ Promise.all(paths.map(async (path30) => {
16133
+ const res = await this._nodeFsHandler._addToNodeFs(path30, !_internal, void 0, 0, _origAdd);
16192
16134
  if (res)
16193
16135
  this._emitReady();
16194
16136
  return res;
@@ -16210,17 +16152,17 @@ var FSWatcher = class extends import_node_events.EventEmitter {
16210
16152
  return this;
16211
16153
  const paths = unifyPaths(paths_);
16212
16154
  const { cwd } = this.options;
16213
- paths.forEach((path28) => {
16214
- if (!sp2.isAbsolute(path28) && !this._closers.has(path28)) {
16155
+ paths.forEach((path30) => {
16156
+ if (!sp2.isAbsolute(path30) && !this._closers.has(path30)) {
16215
16157
  if (cwd)
16216
- path28 = sp2.join(cwd, path28);
16217
- path28 = sp2.resolve(path28);
16158
+ path30 = sp2.join(cwd, path30);
16159
+ path30 = sp2.resolve(path30);
16218
16160
  }
16219
- this._closePath(path28);
16220
- this._addIgnoredPath(path28);
16221
- if (this._watched.has(path28)) {
16161
+ this._closePath(path30);
16162
+ this._addIgnoredPath(path30);
16163
+ if (this._watched.has(path30)) {
16222
16164
  this._addIgnoredPath({
16223
- path: path28,
16165
+ path: path30,
16224
16166
  recursive: true
16225
16167
  });
16226
16168
  }
@@ -16284,38 +16226,38 @@ var FSWatcher = class extends import_node_events.EventEmitter {
16284
16226
  * @param stats arguments to be passed with event
16285
16227
  * @returns the error if defined, otherwise the value of the FSWatcher instance's `closed` flag
16286
16228
  */
16287
- async _emit(event, path28, stats) {
16229
+ async _emit(event, path30, stats) {
16288
16230
  if (this.closed)
16289
16231
  return;
16290
16232
  const opts = this.options;
16291
16233
  if (isWindows)
16292
- path28 = sp2.normalize(path28);
16234
+ path30 = sp2.normalize(path30);
16293
16235
  if (opts.cwd)
16294
- path28 = sp2.relative(opts.cwd, path28);
16295
- const args = [path28];
16236
+ path30 = sp2.relative(opts.cwd, path30);
16237
+ const args = [path30];
16296
16238
  if (stats != null)
16297
16239
  args.push(stats);
16298
16240
  const awf = opts.awaitWriteFinish;
16299
16241
  let pw;
16300
- if (awf && (pw = this._pendingWrites.get(path28))) {
16242
+ if (awf && (pw = this._pendingWrites.get(path30))) {
16301
16243
  pw.lastChange = /* @__PURE__ */ new Date();
16302
16244
  return this;
16303
16245
  }
16304
16246
  if (opts.atomic) {
16305
16247
  if (event === EVENTS.UNLINK) {
16306
- this._pendingUnlinks.set(path28, [event, ...args]);
16248
+ this._pendingUnlinks.set(path30, [event, ...args]);
16307
16249
  setTimeout(() => {
16308
- this._pendingUnlinks.forEach((entry, path29) => {
16250
+ this._pendingUnlinks.forEach((entry, path31) => {
16309
16251
  this.emit(...entry);
16310
16252
  this.emit(EVENTS.ALL, ...entry);
16311
- this._pendingUnlinks.delete(path29);
16253
+ this._pendingUnlinks.delete(path31);
16312
16254
  });
16313
16255
  }, typeof opts.atomic === "number" ? opts.atomic : 100);
16314
16256
  return this;
16315
16257
  }
16316
- if (event === EVENTS.ADD && this._pendingUnlinks.has(path28)) {
16258
+ if (event === EVENTS.ADD && this._pendingUnlinks.has(path30)) {
16317
16259
  event = EVENTS.CHANGE;
16318
- this._pendingUnlinks.delete(path28);
16260
+ this._pendingUnlinks.delete(path30);
16319
16261
  }
16320
16262
  }
16321
16263
  if (awf && (event === EVENTS.ADD || event === EVENTS.CHANGE) && this._readyEmitted) {
@@ -16333,16 +16275,16 @@ var FSWatcher = class extends import_node_events.EventEmitter {
16333
16275
  this.emitWithAll(event, args);
16334
16276
  }
16335
16277
  };
16336
- this._awaitWriteFinish(path28, awf.stabilityThreshold, event, awfEmit);
16278
+ this._awaitWriteFinish(path30, awf.stabilityThreshold, event, awfEmit);
16337
16279
  return this;
16338
16280
  }
16339
16281
  if (event === EVENTS.CHANGE) {
16340
- const isThrottled = !this._throttle(EVENTS.CHANGE, path28, 50);
16282
+ const isThrottled = !this._throttle(EVENTS.CHANGE, path30, 50);
16341
16283
  if (isThrottled)
16342
16284
  return this;
16343
16285
  }
16344
16286
  if (opts.alwaysStat && stats === void 0 && (event === EVENTS.ADD || event === EVENTS.ADD_DIR || event === EVENTS.CHANGE)) {
16345
- const fullPath = opts.cwd ? sp2.join(opts.cwd, path28) : path28;
16287
+ const fullPath = opts.cwd ? sp2.join(opts.cwd, path30) : path30;
16346
16288
  let stats2;
16347
16289
  try {
16348
16290
  stats2 = await (0, import_promises3.stat)(fullPath);
@@ -16373,23 +16315,23 @@ var FSWatcher = class extends import_node_events.EventEmitter {
16373
16315
  * @param timeout duration of time to suppress duplicate actions
16374
16316
  * @returns tracking object or false if action should be suppressed
16375
16317
  */
16376
- _throttle(actionType, path28, timeout) {
16318
+ _throttle(actionType, path30, timeout) {
16377
16319
  if (!this._throttled.has(actionType)) {
16378
16320
  this._throttled.set(actionType, /* @__PURE__ */ new Map());
16379
16321
  }
16380
16322
  const action = this._throttled.get(actionType);
16381
16323
  if (!action)
16382
16324
  throw new Error("invalid throttle");
16383
- const actionPath = action.get(path28);
16325
+ const actionPath = action.get(path30);
16384
16326
  if (actionPath) {
16385
16327
  actionPath.count++;
16386
16328
  return false;
16387
16329
  }
16388
16330
  let timeoutObject;
16389
16331
  const clear = () => {
16390
- const item = action.get(path28);
16332
+ const item = action.get(path30);
16391
16333
  const count = item ? item.count : 0;
16392
- action.delete(path28);
16334
+ action.delete(path30);
16393
16335
  clearTimeout(timeoutObject);
16394
16336
  if (item)
16395
16337
  clearTimeout(item.timeoutObject);
@@ -16397,7 +16339,7 @@ var FSWatcher = class extends import_node_events.EventEmitter {
16397
16339
  };
16398
16340
  timeoutObject = setTimeout(clear, timeout);
16399
16341
  const thr = { timeoutObject, clear, count: 0 };
16400
- action.set(path28, thr);
16342
+ action.set(path30, thr);
16401
16343
  return thr;
16402
16344
  }
16403
16345
  _incrReadyCount() {
@@ -16411,44 +16353,44 @@ var FSWatcher = class extends import_node_events.EventEmitter {
16411
16353
  * @param event
16412
16354
  * @param awfEmit Callback to be called when ready for event to be emitted.
16413
16355
  */
16414
- _awaitWriteFinish(path28, threshold, event, awfEmit) {
16356
+ _awaitWriteFinish(path30, threshold, event, awfEmit) {
16415
16357
  const awf = this.options.awaitWriteFinish;
16416
16358
  if (typeof awf !== "object")
16417
16359
  return;
16418
16360
  const pollInterval = awf.pollInterval;
16419
16361
  let timeoutHandler;
16420
- let fullPath = path28;
16421
- if (this.options.cwd && !sp2.isAbsolute(path28)) {
16422
- fullPath = sp2.join(this.options.cwd, path28);
16362
+ let fullPath = path30;
16363
+ if (this.options.cwd && !sp2.isAbsolute(path30)) {
16364
+ fullPath = sp2.join(this.options.cwd, path30);
16423
16365
  }
16424
16366
  const now2 = /* @__PURE__ */ new Date();
16425
16367
  const writes = this._pendingWrites;
16426
16368
  function awaitWriteFinishFn(prevStat) {
16427
16369
  (0, import_node_fs2.stat)(fullPath, (err, curStat) => {
16428
- if (err || !writes.has(path28)) {
16370
+ if (err || !writes.has(path30)) {
16429
16371
  if (err && err.code !== "ENOENT")
16430
16372
  awfEmit(err);
16431
16373
  return;
16432
16374
  }
16433
16375
  const now3 = Number(/* @__PURE__ */ new Date());
16434
16376
  if (prevStat && curStat.size !== prevStat.size) {
16435
- writes.get(path28).lastChange = now3;
16377
+ writes.get(path30).lastChange = now3;
16436
16378
  }
16437
- const pw = writes.get(path28);
16379
+ const pw = writes.get(path30);
16438
16380
  const df = now3 - pw.lastChange;
16439
16381
  if (df >= threshold) {
16440
- writes.delete(path28);
16382
+ writes.delete(path30);
16441
16383
  awfEmit(void 0, curStat);
16442
16384
  } else {
16443
16385
  timeoutHandler = setTimeout(awaitWriteFinishFn, pollInterval, curStat);
16444
16386
  }
16445
16387
  });
16446
16388
  }
16447
- if (!writes.has(path28)) {
16448
- writes.set(path28, {
16389
+ if (!writes.has(path30)) {
16390
+ writes.set(path30, {
16449
16391
  lastChange: now2,
16450
16392
  cancelWait: () => {
16451
- writes.delete(path28);
16393
+ writes.delete(path30);
16452
16394
  clearTimeout(timeoutHandler);
16453
16395
  return event;
16454
16396
  }
@@ -16459,8 +16401,8 @@ var FSWatcher = class extends import_node_events.EventEmitter {
16459
16401
  /**
16460
16402
  * Determines whether user has asked to ignore this path.
16461
16403
  */
16462
- _isIgnored(path28, stats) {
16463
- if (this.options.atomic && DOT_RE.test(path28))
16404
+ _isIgnored(path30, stats) {
16405
+ if (this.options.atomic && DOT_RE.test(path30))
16464
16406
  return true;
16465
16407
  if (!this._userIgnored) {
16466
16408
  const { cwd } = this.options;
@@ -16470,17 +16412,17 @@ var FSWatcher = class extends import_node_events.EventEmitter {
16470
16412
  const list = [...ignoredPaths.map(normalizeIgnored(cwd)), ...ignored];
16471
16413
  this._userIgnored = anymatch(list, void 0);
16472
16414
  }
16473
- return this._userIgnored(path28, stats);
16415
+ return this._userIgnored(path30, stats);
16474
16416
  }
16475
- _isntIgnored(path28, stat4) {
16476
- return !this._isIgnored(path28, stat4);
16417
+ _isntIgnored(path30, stat5) {
16418
+ return !this._isIgnored(path30, stat5);
16477
16419
  }
16478
16420
  /**
16479
16421
  * Provides a set of common helpers and properties relating to symlink handling.
16480
16422
  * @param path file or directory pattern being watched
16481
16423
  */
16482
- _getWatchHelpers(path28) {
16483
- return new WatchHelper(path28, this.options.followSymlinks, this);
16424
+ _getWatchHelpers(path30) {
16425
+ return new WatchHelper(path30, this.options.followSymlinks, this);
16484
16426
  }
16485
16427
  // Directory helpers
16486
16428
  // -----------------
@@ -16512,63 +16454,63 @@ var FSWatcher = class extends import_node_events.EventEmitter {
16512
16454
  * @param item base path of item/directory
16513
16455
  */
16514
16456
  _remove(directory, item, isDirectory) {
16515
- const path28 = sp2.join(directory, item);
16516
- const fullPath = sp2.resolve(path28);
16517
- isDirectory = isDirectory != null ? isDirectory : this._watched.has(path28) || this._watched.has(fullPath);
16518
- if (!this._throttle("remove", path28, 100))
16457
+ const path30 = sp2.join(directory, item);
16458
+ const fullPath = sp2.resolve(path30);
16459
+ isDirectory = isDirectory != null ? isDirectory : this._watched.has(path30) || this._watched.has(fullPath);
16460
+ if (!this._throttle("remove", path30, 100))
16519
16461
  return;
16520
16462
  if (!isDirectory && this._watched.size === 1) {
16521
16463
  this.add(directory, item, true);
16522
16464
  }
16523
- const wp = this._getWatchedDir(path28);
16465
+ const wp = this._getWatchedDir(path30);
16524
16466
  const nestedDirectoryChildren = wp.getChildren();
16525
- nestedDirectoryChildren.forEach((nested) => this._remove(path28, nested));
16467
+ nestedDirectoryChildren.forEach((nested) => this._remove(path30, nested));
16526
16468
  const parent = this._getWatchedDir(directory);
16527
16469
  const wasTracked = parent.has(item);
16528
16470
  parent.remove(item);
16529
16471
  if (this._symlinkPaths.has(fullPath)) {
16530
16472
  this._symlinkPaths.delete(fullPath);
16531
16473
  }
16532
- let relPath = path28;
16474
+ let relPath = path30;
16533
16475
  if (this.options.cwd)
16534
- relPath = sp2.relative(this.options.cwd, path28);
16476
+ relPath = sp2.relative(this.options.cwd, path30);
16535
16477
  if (this.options.awaitWriteFinish && this._pendingWrites.has(relPath)) {
16536
16478
  const event = this._pendingWrites.get(relPath).cancelWait();
16537
16479
  if (event === EVENTS.ADD)
16538
16480
  return;
16539
16481
  }
16540
- this._watched.delete(path28);
16482
+ this._watched.delete(path30);
16541
16483
  this._watched.delete(fullPath);
16542
16484
  const eventName = isDirectory ? EVENTS.UNLINK_DIR : EVENTS.UNLINK;
16543
- if (wasTracked && !this._isIgnored(path28))
16544
- this._emit(eventName, path28);
16545
- this._closePath(path28);
16485
+ if (wasTracked && !this._isIgnored(path30))
16486
+ this._emit(eventName, path30);
16487
+ this._closePath(path30);
16546
16488
  }
16547
16489
  /**
16548
16490
  * Closes all watchers for a path
16549
16491
  */
16550
- _closePath(path28) {
16551
- this._closeFile(path28);
16552
- const dir = sp2.dirname(path28);
16553
- this._getWatchedDir(dir).remove(sp2.basename(path28));
16492
+ _closePath(path30) {
16493
+ this._closeFile(path30);
16494
+ const dir = sp2.dirname(path30);
16495
+ this._getWatchedDir(dir).remove(sp2.basename(path30));
16554
16496
  }
16555
16497
  /**
16556
16498
  * Closes only file-specific watchers
16557
16499
  */
16558
- _closeFile(path28) {
16559
- const closers = this._closers.get(path28);
16500
+ _closeFile(path30) {
16501
+ const closers = this._closers.get(path30);
16560
16502
  if (!closers)
16561
16503
  return;
16562
16504
  closers.forEach((closer) => closer());
16563
- this._closers.delete(path28);
16505
+ this._closers.delete(path30);
16564
16506
  }
16565
- _addPathCloser(path28, closer) {
16507
+ _addPathCloser(path30, closer) {
16566
16508
  if (!closer)
16567
16509
  return;
16568
- let list = this._closers.get(path28);
16510
+ let list = this._closers.get(path30);
16569
16511
  if (!list) {
16570
16512
  list = [];
16571
- this._closers.set(path28, list);
16513
+ this._closers.set(path30, list);
16572
16514
  }
16573
16515
  list.push(closer);
16574
16516
  }
@@ -16598,12 +16540,291 @@ function watch(paths, options = {}) {
16598
16540
  var chokidar_default = { watch, FSWatcher };
16599
16541
 
16600
16542
  // src/watcher/file-watcher.ts
16543
+ var path23 = __toESM(require("path"), 1);
16544
+
16545
+ // src/watcher/native-recursive-watcher.ts
16546
+ var import_node_fs3 = require("fs");
16601
16547
  var path21 = __toESM(require("path"), 1);
16548
+ var NativeRecursiveWatcher = class {
16549
+ constructor(root, onChange, options = {}) {
16550
+ this.root = root;
16551
+ this.onChange = onChange;
16552
+ this.watchFactory = options.watchFactory ?? this.defaultWatchFactory;
16553
+ this.onError = options.onError;
16554
+ }
16555
+ root;
16556
+ onChange;
16557
+ watcher = null;
16558
+ listenerToken = 0;
16559
+ watchFactory;
16560
+ onError;
16561
+ start() {
16562
+ if (this.watcher) return;
16563
+ const token = ++this.listenerToken;
16564
+ const listener = (_eventType, filename) => {
16565
+ if (this.watcher === null || this.listenerToken !== token) return;
16566
+ const absolutePath = this.toAbsolutePath(filename);
16567
+ const nextResult = this.onChange(absolutePath);
16568
+ if (nextResult instanceof Promise) {
16569
+ void nextResult.catch((error) => {
16570
+ console.error("[codebase-index] Error handling native watcher event:", error);
16571
+ });
16572
+ }
16573
+ };
16574
+ const watcher = this.watchFactory(this.root, listener, {
16575
+ persistent: true,
16576
+ recursive: true
16577
+ });
16578
+ watcher.on?.("error", (error) => {
16579
+ if (this.watcher === watcher && this.listenerToken === token) {
16580
+ this.onError?.(error);
16581
+ }
16582
+ });
16583
+ this.watcher = watcher;
16584
+ }
16585
+ async stop() {
16586
+ const watcher = this.watcher;
16587
+ this.watcher = null;
16588
+ this.listenerToken += 1;
16589
+ if (!watcher) return;
16590
+ await watcher.close();
16591
+ }
16592
+ toAbsolutePath(filename) {
16593
+ if (filename == null) return null;
16594
+ const normalizedFilename = typeof filename === "string" ? filename : filename.toString();
16595
+ const absolutePath = path21.resolve(this.root, normalizedFilename);
16596
+ const relativePath = path21.relative(this.root, absolutePath);
16597
+ const outsideRoot = relativePath === ".." || relativePath.startsWith(`..${path21.sep}`) || path21.isAbsolute(relativePath);
16598
+ return outsideRoot ? null : absolutePath;
16599
+ }
16600
+ defaultWatchFactory = (root, listener, options) => (0, import_node_fs3.watch)(root, options, listener);
16601
+ };
16602
+
16603
+ // src/watcher/snapshot.ts
16604
+ var fsPromises4 = __toESM(require("fs/promises"), 1);
16605
+ var path22 = __toESM(require("path"), 1);
16606
+ async function buildFileSnapshotScan(projectRoot, config, configPaths = []) {
16607
+ const normalizedProjectRoot = path22.resolve(projectRoot);
16608
+ const ignoreFilter = createIgnoreFilter(normalizedProjectRoot);
16609
+ const includePatterns = [...config.include, ...config.additionalInclude ?? []];
16610
+ const maxDepth = config.indexing?.maxDepth ?? -1;
16611
+ const snapshot = /* @__PURE__ */ new Map();
16612
+ const unreadablePrefixes = /* @__PURE__ */ new Set();
16613
+ const includeFile = async (filePath) => {
16614
+ const normalizedPath2 = path22.resolve(filePath);
16615
+ if (!shouldIncludeFile(normalizedPath2, normalizedProjectRoot, includePatterns, config.exclude, ignoreFilter)) return;
16616
+ const stat5 = await readStatIfFile(normalizedPath2, unreadablePrefixes);
16617
+ if (stat5) snapshot.set(normalizedPath2, { size: stat5.size, mtimeMs: stat5.mtimeMs });
16618
+ };
16619
+ const walk = async (directoryPath, depth) => {
16620
+ let entries;
16621
+ try {
16622
+ entries = await fsPromises4.readdir(directoryPath, { withFileTypes: true });
16623
+ } catch (error) {
16624
+ if (isMissingFsError(error)) return;
16625
+ if (isPermissionFsError(error)) {
16626
+ unreadablePrefixes.add(path22.resolve(directoryPath));
16627
+ return;
16628
+ }
16629
+ throw error;
16630
+ }
16631
+ for (const entry of entries) {
16632
+ const fullPath = path22.join(directoryPath, entry.name);
16633
+ const relativePath = path22.relative(normalizedProjectRoot, fullPath);
16634
+ if (entry.isDirectory()) {
16635
+ if (hasFilteredPathSegment(relativePath, path22.sep) || isRestrictedDirectory(relativePath, path22.sep)) continue;
16636
+ if (ignoreFilter.ignores(relativePath)) continue;
16637
+ if (maxDepth === -1 || depth < maxDepth) await walk(fullPath, depth + 1);
16638
+ } else if (entry.isFile()) {
16639
+ await includeFile(fullPath);
16640
+ }
16641
+ }
16642
+ };
16643
+ await walk(normalizedProjectRoot, 0);
16644
+ await includeExplicitConfigPaths(snapshot, unreadablePrefixes, configPaths);
16645
+ return { entries: snapshot, unreadablePrefixes };
16646
+ }
16647
+ async function buildFileSnapshotForPathScan(projectRoot, config, configPaths, targetPath) {
16648
+ const normalizedProjectRoot = path22.resolve(projectRoot);
16649
+ const normalizedTargetPath = path22.resolve(targetPath);
16650
+ if (!isWithinPath(normalizedProjectRoot, normalizedTargetPath)) {
16651
+ return { entries: /* @__PURE__ */ new Map(), unreadablePrefixes: /* @__PURE__ */ new Set() };
16652
+ }
16653
+ const ignoreFilter = createIgnoreFilter(normalizedProjectRoot);
16654
+ const includePatterns = [...config.include, ...config.additionalInclude ?? []];
16655
+ const maxDepth = config.indexing?.maxDepth ?? -1;
16656
+ const explicitConfigPaths = new Set(configPaths.map((configPath) => path22.resolve(configPath)));
16657
+ const snapshot = /* @__PURE__ */ new Map();
16658
+ const unreadablePrefixes = /* @__PURE__ */ new Set();
16659
+ const includeFile = async (filePath) => {
16660
+ const normalizedPath2 = path22.resolve(filePath);
16661
+ if (!explicitConfigPaths.has(normalizedPath2) && !shouldIncludeFile(
16662
+ normalizedPath2,
16663
+ normalizedProjectRoot,
16664
+ includePatterns,
16665
+ config.exclude,
16666
+ ignoreFilter
16667
+ )) return;
16668
+ const stat5 = await readStatIfFile(normalizedPath2, unreadablePrefixes);
16669
+ if (stat5) snapshot.set(normalizedPath2, { size: stat5.size, mtimeMs: stat5.mtimeMs });
16670
+ };
16671
+ const walk = async (directoryPath, depth) => {
16672
+ let entries;
16673
+ try {
16674
+ entries = await fsPromises4.readdir(directoryPath, { withFileTypes: true });
16675
+ } catch (error) {
16676
+ if (isMissingFsError(error)) return;
16677
+ if (isPermissionFsError(error)) {
16678
+ unreadablePrefixes.add(path22.resolve(directoryPath));
16679
+ return;
16680
+ }
16681
+ throw error;
16682
+ }
16683
+ for (const entry of entries) {
16684
+ const fullPath = path22.join(directoryPath, entry.name);
16685
+ const relativePath = path22.relative(normalizedProjectRoot, fullPath);
16686
+ if (entry.isDirectory()) {
16687
+ if (hasFilteredPathSegment(relativePath, path22.sep) || isRestrictedDirectory(relativePath, path22.sep)) continue;
16688
+ if (ignoreFilter.ignores(relativePath)) continue;
16689
+ if (maxDepth === -1 || depth < maxDepth) await walk(fullPath, depth + 1);
16690
+ } else if (entry.isFile()) {
16691
+ await includeFile(fullPath);
16692
+ }
16693
+ }
16694
+ };
16695
+ const targetStat = await readStatIfFile(normalizedTargetPath, unreadablePrefixes);
16696
+ if (targetStat) await includeFile(normalizedTargetPath);
16697
+ else await walk(normalizedTargetPath, 0);
16698
+ await includeExplicitConfigPathsInPath(snapshot, unreadablePrefixes, configPaths, normalizedTargetPath);
16699
+ return { entries: snapshot, unreadablePrefixes };
16700
+ }
16701
+ function completeFileSnapshot(previous, scan) {
16702
+ const completed = new Map(scan.entries);
16703
+ for (const unreadablePrefix of scan.unreadablePrefixes) {
16704
+ for (const [entryPath, entry] of previous) {
16705
+ if (isWithinPath(unreadablePrefix, entryPath) && !completed.has(entryPath)) completed.set(entryPath, entry);
16706
+ }
16707
+ }
16708
+ return completed;
16709
+ }
16710
+ async function includeExplicitConfigPaths(snapshot, unreadablePrefixes, configPaths) {
16711
+ for (const configPath of [...new Set(configPaths.map((value) => path22.resolve(value)))]) {
16712
+ if (snapshot.has(configPath)) continue;
16713
+ const stat5 = await readStatIfFile(configPath, unreadablePrefixes);
16714
+ if (stat5) snapshot.set(configPath, { size: stat5.size, mtimeMs: stat5.mtimeMs });
16715
+ }
16716
+ }
16717
+ async function includeExplicitConfigPathsInPath(snapshot, unreadablePrefixes, configPaths, targetPath) {
16718
+ await includeExplicitConfigPaths(
16719
+ snapshot,
16720
+ unreadablePrefixes,
16721
+ configPaths.filter((configPath) => isWithinPath(targetPath, path22.resolve(configPath)))
16722
+ );
16723
+ }
16724
+ function isWithinPath(parentPath, childPath) {
16725
+ const relativePath = path22.relative(parentPath, childPath);
16726
+ return relativePath === "" || !relativePath.startsWith(`..${path22.sep}`) && relativePath !== ".." && !path22.isAbsolute(relativePath);
16727
+ }
16728
+ async function readStatIfFile(filePath, unreadablePrefixes) {
16729
+ try {
16730
+ const stat5 = await fsPromises4.stat(filePath);
16731
+ return stat5.isFile() ? stat5 : null;
16732
+ } catch (error) {
16733
+ if (isMissingFsError(error)) return null;
16734
+ if (isPermissionFsError(error)) {
16735
+ unreadablePrefixes.add(path22.resolve(filePath));
16736
+ return null;
16737
+ }
16738
+ throw error;
16739
+ }
16740
+ }
16741
+ function isMissingFsError(error) {
16742
+ return error instanceof Error && ["ENOENT", "ENOTDIR"].includes(error.code ?? "");
16743
+ }
16744
+ function isPermissionFsError(error) {
16745
+ return error instanceof Error && ["EACCES", "EPERM"].includes(error.code ?? "");
16746
+ }
16747
+ var diffTypeOrder = { add: 0, change: 1, unlink: 2 };
16748
+ function diffFileSnapshots(previous, current, forcedChanges = /* @__PURE__ */ new Set()) {
16749
+ const changes = [];
16750
+ for (const [filePath, previousEntry] of previous) {
16751
+ const currentEntry = current.get(filePath);
16752
+ if (!currentEntry) changes.push({ type: "unlink", path: filePath });
16753
+ else if (forcedChanges.has(filePath) || currentEntry.size !== previousEntry.size || currentEntry.mtimeMs !== previousEntry.mtimeMs) {
16754
+ changes.push({ type: "change", path: filePath });
16755
+ }
16756
+ }
16757
+ for (const [filePath] of current) {
16758
+ if (!previous.has(filePath)) changes.push({ type: "add", path: filePath });
16759
+ }
16760
+ return changes.sort((left, right) => left.path.localeCompare(right.path) || diffTypeOrder[left.type] - diffTypeOrder[right.type]);
16761
+ }
16762
+
16763
+ // src/watcher/snapshot-reconciler.ts
16764
+ var FileSnapshotReconciler = class {
16765
+ constructor(projectRoot, config, configPaths) {
16766
+ this.projectRoot = projectRoot;
16767
+ this.config = config;
16768
+ this.configPaths = configPaths;
16769
+ }
16770
+ projectRoot;
16771
+ config;
16772
+ configPaths;
16773
+ snapshot = null;
16774
+ reconciliationTail = Promise.resolve();
16775
+ async initialize() {
16776
+ this.snapshot = (await buildFileSnapshotScan(this.projectRoot, this.config, this.configPaths)).entries;
16777
+ }
16778
+ async reconcile(invalidations = []) {
16779
+ if (this.snapshot === null) {
16780
+ throw new Error("FileSnapshotReconciler is not initialized. Call initialize() before reconcile().");
16781
+ }
16782
+ const reconciliation = this.reconciliationTail.then(async () => {
16783
+ const previousSnapshot = this.snapshot;
16784
+ if (previousSnapshot === null) {
16785
+ throw new Error("FileSnapshotReconciler is not initialized. Call initialize() before reconcile().");
16786
+ }
16787
+ const normalizedInvalidations = invalidations.map((invalidation) => typeof invalidation === "string" || invalidation === null ? { path: invalidation, forceChange: false } : { path: invalidation.path, forceChange: invalidation.forceChange === true });
16788
+ const scopedPaths = normalizedInvalidations.map((invalidation) => invalidation.path).filter((filePath) => filePath !== null);
16789
+ const scan = scopedPaths.length === 0 || scopedPaths.length !== normalizedInvalidations.length ? await buildFileSnapshotScan(this.projectRoot, this.config, this.configPaths) : await this.reconcilePaths(previousSnapshot, scopedPaths);
16790
+ const nextSnapshot = completeFileSnapshot(previousSnapshot, scan);
16791
+ const forcedChanges = new Set(normalizedInvalidations.filter((invalidation) => invalidation.path !== null && invalidation.forceChange).map((invalidation) => invalidation.path));
16792
+ const changes = diffFileSnapshots(previousSnapshot, nextSnapshot, forcedChanges);
16793
+ this.snapshot = nextSnapshot;
16794
+ return changes;
16795
+ });
16796
+ this.reconciliationTail = reconciliation.then(() => void 0, () => void 0);
16797
+ return reconciliation;
16798
+ }
16799
+ async reconcilePaths(previousSnapshot, invalidatedPaths) {
16800
+ const scopes = this.getScopes(invalidatedPaths);
16801
+ const entries = new Map(previousSnapshot);
16802
+ const unreadablePrefixes = /* @__PURE__ */ new Set();
16803
+ for (const scope of scopes) {
16804
+ for (const previousPath of entries.keys()) {
16805
+ if (isWithinPath(scope, previousPath)) entries.delete(previousPath);
16806
+ }
16807
+ const scopedScan = await buildFileSnapshotForPathScan(this.projectRoot, this.config, this.configPaths, scope);
16808
+ for (const [filePath, entry] of scopedScan.entries) entries.set(filePath, entry);
16809
+ for (const unreadablePrefix of scopedScan.unreadablePrefixes) unreadablePrefixes.add(unreadablePrefix);
16810
+ }
16811
+ return { entries, unreadablePrefixes };
16812
+ }
16813
+ getScopes(invalidatedPaths) {
16814
+ const uniquePaths = [...new Set(invalidatedPaths)].sort((left, right) => left.length - right.length);
16815
+ return uniquePaths.filter((candidate, index) => !uniquePaths.slice(0, index).some(
16816
+ (ancestor) => isWithinPath(ancestor, candidate)
16817
+ ));
16818
+ }
16819
+ };
16820
+
16821
+ // src/watcher/file-watcher.ts
16602
16822
  var FileWatcher = class {
16603
16823
  watcher = null;
16604
16824
  projectRoot;
16605
16825
  config;
16606
16826
  configPath;
16827
+ backend;
16607
16828
  projectConfigPaths;
16608
16829
  pendingChanges = /* @__PURE__ */ new Map();
16609
16830
  debounceTimer = null;
@@ -16613,44 +16834,74 @@ var FileWatcher = class {
16613
16834
  resolveReady = null;
16614
16835
  pollingFallbackAttempted = false;
16615
16836
  pendingClose = null;
16837
+ startupReadySignals = 1;
16838
+ nativeWatcher = null;
16839
+ nativeReconciler = null;
16840
+ nativeSetupGeneration = 0;
16841
+ nativeStarting = false;
16842
+ nativeInitializing = false;
16843
+ nativeReconcileTimer = null;
16844
+ nativeInvalidatedPaths = /* @__PURE__ */ new Map();
16845
+ configPathStates = /* @__PURE__ */ new Map();
16616
16846
  constructor(projectRoot, config, host, options = {}) {
16617
16847
  this.projectRoot = projectRoot;
16618
16848
  this.config = config;
16849
+ this.backend = options.backend ?? "auto";
16619
16850
  this.configPath = options.configPath;
16620
16851
  this.projectConfigPaths = options.configPath ? [options.configPath] : getProjectConfigCandidatePaths(projectRoot, host);
16621
16852
  }
16622
16853
  start(handler) {
16623
- if (this.watcher) {
16854
+ if (this.watcher || this.nativeWatcher || this.nativeStarting) {
16624
16855
  return;
16625
16856
  }
16626
16857
  this.onChanges = handler;
16627
16858
  this.pollingFallbackAttempted = false;
16628
16859
  this.resetReady();
16860
+ if (this.shouldUseNativeWatcher()) {
16861
+ if (this.hasExternalConfigWatchTarget()) {
16862
+ this.setStartupReadySignals(2);
16863
+ this.startExternalConfigWatcher();
16864
+ }
16865
+ this.nativeStarting = true;
16866
+ void this.createNativeWatcher();
16867
+ return;
16868
+ }
16629
16869
  this.createWatcher();
16630
16870
  }
16631
16871
  resetReady() {
16632
- this.readyPromise = new Promise((resolve15) => {
16633
- this.resolveReady = resolve15;
16872
+ this.readyPromise = new Promise((resolve17) => {
16873
+ this.resolveReady = resolve17;
16634
16874
  });
16875
+ this.startupReadySignals = 1;
16635
16876
  }
16636
- createWatcher(usePolling = false) {
16637
- const ignoreFilter = createIgnoreFilter(this.projectRoot);
16638
- let watchTargets = this.projectRoot;
16639
- if (this.configPath) {
16640
- watchTargets = [this.projectRoot, this.configPath];
16641
- } else {
16642
- const externalConfigTargets = this.projectConfigPaths.filter((projectConfigPath) => {
16643
- const relativeConfigPath = path21.relative(this.projectRoot, projectConfigPath);
16644
- return this.isOutsideProjectPath(relativeConfigPath);
16645
- }).map((projectConfigPath) => (0, import_fs14.existsSync)(projectConfigPath) ? projectConfigPath : this.getNearestExistingDirectory(path21.dirname(projectConfigPath)));
16646
- const uniqueExternalConfigTargets = [...new Set(externalConfigTargets)];
16647
- if (uniqueExternalConfigTargets.length > 0) {
16648
- watchTargets = [this.projectRoot, ...uniqueExternalConfigTargets];
16649
- }
16877
+ setStartupReadySignals(expectedSignals) {
16878
+ if (!this.readyPromise) {
16879
+ return;
16880
+ }
16881
+ this.startupReadySignals = Math.max(0, expectedSignals);
16882
+ }
16883
+ reportStartupReadySignal() {
16884
+ if (!this.readyPromise || !this.resolveReady) {
16885
+ return;
16650
16886
  }
16887
+ if (this.startupReadySignals <= 0) {
16888
+ return;
16889
+ }
16890
+ this.startupReadySignals -= 1;
16891
+ if (this.startupReadySignals !== 0) {
16892
+ return;
16893
+ }
16894
+ this.resolveReady();
16895
+ this.resolveReady = null;
16896
+ }
16897
+ createWatcher(watchTargets, usePolling = false, reportsStartupReady = true) {
16898
+ let reportedStartupReady = false;
16899
+ this.configPathStates = this.getConfigPathStates();
16900
+ const ignoreFilter = createIgnoreFilter(this.projectRoot);
16901
+ const resolvedWatchTargets = watchTargets ?? this.getFullChokidarWatchTargets();
16651
16902
  const watcherOptions = {
16652
16903
  ignored: (filePath) => {
16653
- const relativePath = path21.relative(this.projectRoot, filePath);
16904
+ const relativePath = path23.relative(this.projectRoot, filePath);
16654
16905
  if (!relativePath) return false;
16655
16906
  if (this.isProjectConfigPathOrAncestor(relativePath)) {
16656
16907
  return false;
@@ -16658,10 +16909,10 @@ var FileWatcher = class {
16658
16909
  if (this.isOutsideProjectPath(relativePath)) {
16659
16910
  return true;
16660
16911
  }
16661
- if (hasFilteredPathSegment(relativePath, path21.sep)) {
16912
+ if (hasFilteredPathSegment(relativePath, path23.sep)) {
16662
16913
  return true;
16663
16914
  }
16664
- if (isRestrictedDirectory(relativePath, path21.sep)) {
16915
+ if (isRestrictedDirectory(relativePath, path23.sep)) {
16665
16916
  return true;
16666
16917
  }
16667
16918
  if (ignoreFilter.ignores(relativePath)) {
@@ -16694,10 +16945,13 @@ var FileWatcher = class {
16694
16945
  watcher = new FSWatcher(watcherOptions);
16695
16946
  }
16696
16947
  this.watcher = watcher;
16697
- watcher.once("ready", () => {
16948
+ watcher.on("ready", () => {
16698
16949
  if (this.watcher !== watcher) return;
16699
- this.resolveReady?.();
16700
- this.resolveReady = null;
16950
+ this.reconcileConfigPathStates();
16951
+ if (reportsStartupReady) {
16952
+ this.reportStartupReadySignal();
16953
+ reportedStartupReady = true;
16954
+ }
16701
16955
  });
16702
16956
  watcher.on("error", (error) => {
16703
16957
  const err = error instanceof Error ? error : null;
@@ -16711,10 +16965,13 @@ var FileWatcher = class {
16711
16965
  console.error("[codebase-index] Failed to close exhausted file watcher:", closeError);
16712
16966
  });
16713
16967
  if (this.onChanges) {
16968
+ const replacementReportsStartupReady = reportsStartupReady || reportedStartupReady;
16714
16969
  if (!this.resolveReady) {
16715
16970
  this.resetReady();
16971
+ } else if (reportedStartupReady) {
16972
+ this.startupReadySignals += 1;
16716
16973
  }
16717
- this.createWatcher(true);
16974
+ this.createWatcher(resolvedWatchTargets, true, replacementReportsStartupReady);
16718
16975
  } else {
16719
16976
  this.watcher = null;
16720
16977
  }
@@ -16725,13 +16982,166 @@ var FileWatcher = class {
16725
16982
  watcher.on("add", (filePath) => this.handleChange(watcher, "add", filePath));
16726
16983
  watcher.on("change", (filePath) => this.handleChange(watcher, "change", filePath));
16727
16984
  watcher.on("unlink", (filePath) => this.handleChange(watcher, "unlink", filePath));
16728
- watcher.add(watchTargets);
16985
+ watcher.add(resolvedWatchTargets);
16986
+ }
16987
+ shouldUseNativeWatcher() {
16988
+ if (this.backend === "chokidar") {
16989
+ return false;
16990
+ }
16991
+ return true;
16992
+ }
16993
+ getFullChokidarWatchTargets() {
16994
+ if (this.configPath) {
16995
+ return [this.projectRoot, this.configPath];
16996
+ }
16997
+ const externalConfigTargets = this.getExternalConfigWatchTargets();
16998
+ if (externalConfigTargets.length === 0) {
16999
+ return this.projectRoot;
17000
+ }
17001
+ return [this.projectRoot, ...externalConfigTargets];
17002
+ }
17003
+ getExternalConfigWatchTargets() {
17004
+ return [...new Set(
17005
+ this.projectConfigPaths.filter((projectConfigPath) => {
17006
+ const relativeConfigPath = path23.relative(this.projectRoot, projectConfigPath);
17007
+ return this.isOutsideProjectPath(relativeConfigPath);
17008
+ }).map((projectConfigPath) => {
17009
+ if ((0, import_fs14.existsSync)(projectConfigPath)) {
17010
+ return projectConfigPath;
17011
+ }
17012
+ return this.getNearestExistingDirectory(path23.dirname(projectConfigPath));
17013
+ })
17014
+ )];
17015
+ }
17016
+ hasExternalConfigWatchTarget() {
17017
+ return this.getExternalConfigWatchTargets().length > 0;
17018
+ }
17019
+ startExternalConfigWatcher(usePolling = false) {
17020
+ const externalTargets = this.getExternalConfigWatchTargets();
17021
+ if (externalTargets.length === 0) {
17022
+ return;
17023
+ }
17024
+ this.createWatcher(externalTargets, usePolling);
17025
+ }
17026
+ async createNativeWatcher() {
17027
+ const generation = ++this.nativeSetupGeneration;
17028
+ const reconciler = new FileSnapshotReconciler(this.projectRoot, this.config, this.projectConfigPaths);
17029
+ const watcher = new NativeRecursiveWatcher(
17030
+ this.projectRoot,
17031
+ (filePath) => this.scheduleNativeReconciliation(generation, filePath),
17032
+ { onError: (error) => void this.fallbackFromNativeWatcher(generation, error) }
17033
+ );
17034
+ this.nativeReconciler = reconciler;
17035
+ this.nativeWatcher = watcher;
17036
+ this.nativeInitializing = true;
17037
+ try {
17038
+ watcher.start();
17039
+ if (!this.isCurrentNativeSetup(generation)) {
17040
+ await watcher.stop();
17041
+ return;
17042
+ }
17043
+ await reconciler.initialize();
17044
+ if (!this.isCurrentNativeSetup(generation) || this.nativeWatcher !== watcher) {
17045
+ await watcher.stop();
17046
+ return;
17047
+ }
17048
+ this.nativeStarting = false;
17049
+ this.nativeInitializing = false;
17050
+ await this.reconcileNativeWatcherWithPendingInvalidations(generation);
17051
+ this.reportStartupReadySignal();
17052
+ } catch (error) {
17053
+ if (!this.isCurrentNativeSetup(generation)) return;
17054
+ this.nativeInitializing = false;
17055
+ if (this.nativeWatcher) {
17056
+ await this.fallbackFromNativeWatcher(generation, error);
17057
+ return;
17058
+ }
17059
+ this.nativeStarting = false;
17060
+ const externalWatcher = this.watcher;
17061
+ this.watcher = null;
17062
+ this.nativeReconciler = null;
17063
+ await externalWatcher?.close();
17064
+ console.warn("[codebase-index] Native recursive watcher unavailable; using Chokidar fallback.", error);
17065
+ this.setStartupReadySignals(1);
17066
+ this.createWatcher();
17067
+ }
17068
+ }
17069
+ isCurrentNativeSetup(generation) {
17070
+ return this.nativeSetupGeneration === generation && this.onChanges !== null;
17071
+ }
17072
+ scheduleNativeReconciliation(generation, filePath) {
17073
+ if (!this.isCurrentNativeSetup(generation)) return;
17074
+ const requiresFullReconciliation = filePath === path23.join(this.projectRoot, ".gitignore");
17075
+ const invalidatedPath = requiresFullReconciliation ? null : filePath;
17076
+ this.nativeInvalidatedPaths.set(invalidatedPath, invalidatedPath !== null);
17077
+ if (this.nativeReconcileTimer) {
17078
+ clearTimeout(this.nativeReconcileTimer);
17079
+ }
17080
+ this.nativeReconcileTimer = setTimeout(() => {
17081
+ this.nativeReconcileTimer = null;
17082
+ void this.reconcileNativeWatcherFromQueue(generation);
17083
+ }, 100);
17084
+ }
17085
+ reconcileNativeWatcherFromQueue(generation) {
17086
+ if (!this.isCurrentNativeSetup(generation) || this.nativeInitializing) return;
17087
+ const invalidatedPaths = this.popNativeInvalidations();
17088
+ if (invalidatedPaths.length === 0) return;
17089
+ void this.reconcileNativeWatcher(generation, invalidatedPaths);
17090
+ }
17091
+ async reconcileNativeWatcher(generation, invalidatedPaths) {
17092
+ if (!this.isCurrentNativeSetup(generation) || !this.nativeReconciler) return;
17093
+ try {
17094
+ const reconciler = this.nativeReconciler;
17095
+ const changes = await reconciler.reconcile(invalidatedPaths);
17096
+ if (!this.isCurrentNativeSetup(generation) || this.nativeReconciler !== reconciler) return;
17097
+ this.recordChanges(changes);
17098
+ } catch (error) {
17099
+ await this.fallbackFromNativeWatcher(generation, error);
17100
+ }
17101
+ }
17102
+ async reconcileNativeWatcherWithPendingInvalidations(generation) {
17103
+ const invalidatedPaths = this.popNativeInvalidations();
17104
+ if (invalidatedPaths.length === 0) return;
17105
+ await this.reconcileNativeWatcher(generation, invalidatedPaths);
17106
+ }
17107
+ popNativeInvalidations() {
17108
+ if (this.nativeInvalidatedPaths.size === 0) return [];
17109
+ const invalidations = [...this.nativeInvalidatedPaths].map(([invalidatedPath, forceChange]) => ({
17110
+ path: invalidatedPath,
17111
+ forceChange
17112
+ }));
17113
+ this.nativeInvalidatedPaths.clear();
17114
+ return invalidations;
17115
+ }
17116
+ async fallbackFromNativeWatcher(generation, error) {
17117
+ if (!this.isCurrentNativeSetup(generation)) return;
17118
+ const watcher = this.nativeWatcher;
17119
+ const externalWatcher = this.watcher;
17120
+ this.nativeWatcher = null;
17121
+ this.watcher = null;
17122
+ this.nativeReconciler = null;
17123
+ this.nativeStarting = false;
17124
+ this.nativeInitializing = false;
17125
+ this.nativeSetupGeneration += 1;
17126
+ if (this.nativeReconcileTimer) {
17127
+ clearTimeout(this.nativeReconcileTimer);
17128
+ this.nativeReconcileTimer = null;
17129
+ }
17130
+ this.nativeInvalidatedPaths.clear();
17131
+ this.setStartupReadySignals(1);
17132
+ console.warn("[codebase-index] Native recursive watcher failed; using Chokidar fallback.", error);
17133
+ await watcher?.stop();
17134
+ await externalWatcher?.close();
17135
+ if (this.onChanges) {
17136
+ this.createWatcher();
17137
+ }
16729
17138
  }
16730
17139
  handleChange(watcher, type, filePath) {
16731
17140
  if (this.watcher !== watcher) {
16732
17141
  return;
16733
17142
  }
16734
17143
  if (this.isProjectConfigPath(filePath)) {
17144
+ this.updateConfigPathState(filePath);
16735
17145
  this.pendingChanges.set(filePath, type);
16736
17146
  this.scheduleFlush();
16737
17147
  return;
@@ -16746,27 +17156,33 @@ var FileWatcher = class {
16746
17156
  )) {
16747
17157
  return;
16748
17158
  }
16749
- this.pendingChanges.set(filePath, type);
17159
+ this.recordChanges([{ path: filePath, type }]);
17160
+ }
17161
+ recordChanges(changes) {
17162
+ if (changes.length === 0) return;
17163
+ for (const change of changes) {
17164
+ this.pendingChanges.set(change.path, change.type);
17165
+ }
16750
17166
  this.scheduleFlush();
16751
17167
  }
16752
17168
  isProjectConfigPath(filePath) {
16753
- const relativePath = path21.relative(this.projectRoot, filePath);
16754
- const normalizedRelativePath = path21.normalize(relativePath);
17169
+ const relativePath = path23.relative(this.projectRoot, filePath);
17170
+ const normalizedRelativePath = path23.normalize(relativePath);
16755
17171
  return this.getProjectConfigRelativePaths().some((configPath) => configPath === normalizedRelativePath);
16756
17172
  }
16757
17173
  isProjectConfigPathOrAncestor(relativePath) {
16758
- const normalizedRelativePath = path21.normalize(relativePath);
17174
+ const normalizedRelativePath = path23.normalize(relativePath);
16759
17175
  return this.getProjectConfigRelativePaths().some(
16760
- (configPath) => configPath === normalizedRelativePath || configPath.startsWith(`${normalizedRelativePath}${path21.sep}`)
17176
+ (configPath) => configPath === normalizedRelativePath || configPath.startsWith(`${normalizedRelativePath}${path23.sep}`)
16761
17177
  );
16762
17178
  }
16763
17179
  isOutsideProjectPath(relativePath) {
16764
- return relativePath === ".." || relativePath.startsWith(`..${path21.sep}`) || path21.isAbsolute(relativePath);
17180
+ return relativePath === ".." || relativePath.startsWith(`..${path23.sep}`) || path23.isAbsolute(relativePath);
16765
17181
  }
16766
17182
  getNearestExistingDirectory(directoryPath) {
16767
17183
  let candidate = directoryPath;
16768
17184
  while (!(0, import_fs14.existsSync)(candidate)) {
16769
- const parent = path21.dirname(candidate);
17185
+ const parent = path23.dirname(candidate);
16770
17186
  if (parent === candidate) break;
16771
17187
  candidate = parent;
16772
17188
  }
@@ -16774,9 +17190,51 @@ var FileWatcher = class {
16774
17190
  }
16775
17191
  getProjectConfigRelativePaths() {
16776
17192
  return this.projectConfigPaths.map(
16777
- (configPath) => path21.normalize(path21.relative(this.projectRoot, configPath))
17193
+ (configPath) => path23.normalize(path23.relative(this.projectRoot, configPath))
16778
17194
  );
16779
17195
  }
17196
+ getConfigPathStates() {
17197
+ const states = /* @__PURE__ */ new Map();
17198
+ for (const configPath of this.projectConfigPaths) {
17199
+ const state = this.getConfigPathState(configPath);
17200
+ if (state) states.set(configPath, state);
17201
+ }
17202
+ return states;
17203
+ }
17204
+ getConfigPathState(configPath) {
17205
+ try {
17206
+ const stats = (0, import_fs14.statSync)(configPath);
17207
+ return stats.isFile() ? { mtimeMs: stats.mtimeMs, size: stats.size } : void 0;
17208
+ } catch (error) {
17209
+ void error;
17210
+ return void 0;
17211
+ }
17212
+ }
17213
+ updateConfigPathState(configPath) {
17214
+ const state = this.getConfigPathState(configPath);
17215
+ if (state) {
17216
+ this.configPathStates.set(configPath, state);
17217
+ } else {
17218
+ this.configPathStates.delete(configPath);
17219
+ }
17220
+ }
17221
+ reconcileConfigPathStates() {
17222
+ const nextStates = this.getConfigPathStates();
17223
+ const changes = [];
17224
+ for (const configPath of this.projectConfigPaths) {
17225
+ const previous = this.configPathStates.get(configPath);
17226
+ const next = nextStates.get(configPath);
17227
+ if (!previous && next) {
17228
+ changes.push({ path: configPath, type: "add" });
17229
+ } else if (previous && !next) {
17230
+ changes.push({ path: configPath, type: "unlink" });
17231
+ } else if (previous && next && (previous.size !== next.size || previous.mtimeMs !== next.mtimeMs)) {
17232
+ changes.push({ path: configPath, type: "change" });
17233
+ }
17234
+ }
17235
+ this.configPathStates = nextStates;
17236
+ this.recordChanges(changes);
17237
+ }
16780
17238
  scheduleFlush() {
16781
17239
  if (this.debounceTimer) {
16782
17240
  clearTimeout(this.debounceTimer);
@@ -16790,7 +17248,7 @@ var FileWatcher = class {
16790
17248
  return;
16791
17249
  }
16792
17250
  const changes = Array.from(this.pendingChanges.entries()).map(
16793
- ([path28, type]) => ({ path: path28, type })
17251
+ ([path30, type]) => ({ path: path30, type })
16794
17252
  );
16795
17253
  this.pendingChanges.clear();
16796
17254
  try {
@@ -16804,20 +17262,31 @@ var FileWatcher = class {
16804
17262
  clearTimeout(this.debounceTimer);
16805
17263
  this.debounceTimer = null;
16806
17264
  }
17265
+ if (this.nativeReconcileTimer) {
17266
+ clearTimeout(this.nativeReconcileTimer);
17267
+ this.nativeReconcileTimer = null;
17268
+ }
17269
+ this.nativeInvalidatedPaths.clear();
16807
17270
  const watcher = this.watcher;
17271
+ const nativeWatcher = this.nativeWatcher;
16808
17272
  const pendingClose = this.pendingClose;
16809
17273
  const resolveReady = this.resolveReady;
16810
17274
  this.watcher = null;
17275
+ this.nativeWatcher = null;
17276
+ this.nativeReconciler = null;
17277
+ this.nativeStarting = false;
17278
+ this.nativeInitializing = false;
17279
+ this.nativeSetupGeneration += 1;
16811
17280
  this.pendingClose = null;
16812
17281
  this.resolveReady = null;
16813
17282
  this.readyPromise = null;
16814
17283
  this.pendingChanges.clear();
16815
17284
  this.onChanges = null;
16816
- await Promise.all([watcher?.close(), pendingClose]);
17285
+ await Promise.all([watcher?.close(), nativeWatcher?.stop(), pendingClose]);
16817
17286
  resolveReady?.();
16818
17287
  }
16819
17288
  isRunning() {
16820
- return this.watcher !== null;
17289
+ return this.watcher !== null || this.nativeWatcher !== null || this.nativeStarting;
16821
17290
  }
16822
17291
  async waitUntilReady() {
16823
17292
  await (this.readyPromise ?? Promise.resolve());
@@ -16825,7 +17294,7 @@ var FileWatcher = class {
16825
17294
  };
16826
17295
 
16827
17296
  // src/watcher/git-head-watcher.ts
16828
- var path22 = __toESM(require("path"), 1);
17297
+ var path24 = __toESM(require("path"), 1);
16829
17298
  var GitHeadWatcher = class {
16830
17299
  watcher = null;
16831
17300
  projectRoot;
@@ -16847,13 +17316,13 @@ var GitHeadWatcher = class {
16847
17316
  this.readyPromise = Promise.resolve();
16848
17317
  return;
16849
17318
  }
16850
- this.readyPromise = new Promise((resolve15) => {
16851
- this.resolveReady = resolve15;
17319
+ this.readyPromise = new Promise((resolve17) => {
17320
+ this.resolveReady = resolve17;
16852
17321
  });
16853
17322
  this.onBranchChange = handler;
16854
17323
  this.currentBranch = getCurrentBranch(this.projectRoot);
16855
17324
  const headPath = getHeadPath(this.projectRoot);
16856
- const refsPath = path22.join(this.projectRoot, ".git", "refs", "heads");
17325
+ const refsPath = path24.join(this.projectRoot, ".git", "refs", "heads");
16857
17326
  this.watcher = chokidar_default.watch([headPath, refsPath], {
16858
17327
  persistent: true,
16859
17328
  ignoreInitial: true,
@@ -17709,7 +18178,7 @@ async function resolveCodebaseContextUnmeasured(projectRoot, host, input) {
17709
18178
  const directory = input.directory ?? void 0;
17710
18179
  const tokenBudget = input.tokenBudget ?? void 0;
17711
18180
  if (from && to) {
17712
- const path28 = await getCallGraphPath(
18181
+ const path30 = await getCallGraphPath(
17713
18182
  projectRoot,
17714
18183
  host,
17715
18184
  from,
@@ -17718,25 +18187,25 @@ async function resolveCodebaseContextUnmeasured(projectRoot, host, input) {
17718
18187
  fromFilePath,
17719
18188
  toFilePath
17720
18189
  );
17721
- const pathText = formatCallGraphPathResult(path28);
17722
- if (path28.path.length > 0) {
18190
+ const pathText = formatCallGraphPathResult(path30);
18191
+ if (path30.path.length > 0) {
17723
18192
  const fitted2 = fitTextToContextBudget(
17724
18193
  pathText,
17725
18194
  tokenBudget
17726
18195
  );
17727
18196
  return {
17728
18197
  text: fitted2.text,
17729
- details: fittedDetails("path", fitted2, path28.path.length)
18198
+ details: fittedDetails("path", fitted2, path30.path.length)
17730
18199
  };
17731
18200
  }
17732
- if (path28.from.status !== "resolved" || path28.to.status !== "resolved") {
18201
+ if (path30.from.status !== "resolved" || path30.to.status !== "resolved") {
17733
18202
  const fitted2 = fitTextToContextBudget(pathText, tokenBudget);
17734
18203
  return {
17735
18204
  text: fitted2.text,
17736
18205
  details: fittedDetails("path", fitted2, 0)
17737
18206
  };
17738
18207
  }
17739
- const resolvedFrom = path28.from;
18208
+ const resolvedFrom = path30.from;
17740
18209
  const { callers } = await getCallGraphData(projectRoot, host, {
17741
18210
  name: to,
17742
18211
  direction: "callers",
@@ -17895,7 +18364,7 @@ async function executeCallGraph(projectRoot, host, args) {
17895
18364
  return { text: formatCallGraphResult(await getCallGraphData(projectRoot, host, args)) };
17896
18365
  }
17897
18366
  async function executeCallGraphPath(projectRoot, host, args) {
17898
- const path28 = await getCallGraphPath(
18367
+ const path30 = await getCallGraphPath(
17899
18368
  projectRoot,
17900
18369
  host,
17901
18370
  args.from,
@@ -17904,7 +18373,7 @@ async function executeCallGraphPath(projectRoot, host, args) {
17904
18373
  args.fromFilePath,
17905
18374
  args.toFilePath
17906
18375
  );
17907
- return { text: formatCallGraphPathResult(path28) };
18376
+ return { text: formatCallGraphPathResult(path30) };
17908
18377
  }
17909
18378
  async function executeCodeCommunities(projectRoot, host, args) {
17910
18379
  const result = await getCodeCommunities(projectRoot, host, args);
@@ -17914,11 +18383,11 @@ async function executeCodeCommunities(projectRoot, host, args) {
17914
18383
  // src/adapters/opencode/tools.ts
17915
18384
  var import_fs15 = require("fs");
17916
18385
  var os7 = __toESM(require("os"), 1);
17917
- var path25 = __toESM(require("path"), 1);
18386
+ var path27 = __toESM(require("path"), 1);
17918
18387
 
17919
18388
  // src/tools/visualize/activity.ts
17920
18389
  var import_child_process5 = require("child_process");
17921
- var path23 = __toESM(require("path"), 1);
18390
+ var path25 = __toESM(require("path"), 1);
17922
18391
  function attachRecentActivity(data, projectRoot) {
17923
18392
  const activity = readGitActivity(projectRoot);
17924
18393
  const changes = activity.size > 0 ? buildGitChanges(data, activity, projectRoot) : buildGraphChanges(data);
@@ -18080,7 +18549,7 @@ function normalizePath3(filePath) {
18080
18549
  return filePath.replace(/\\/g, "/");
18081
18550
  }
18082
18551
  function toGitRelativePath(projectRoot, filePath) {
18083
- const relativePath = path23.isAbsolute(filePath) ? path23.relative(projectRoot, filePath) : filePath;
18552
+ const relativePath = path25.isAbsolute(filePath) ? path25.relative(projectRoot, filePath) : filePath;
18084
18553
  return normalizePath3(relativePath);
18085
18554
  }
18086
18555
 
@@ -18338,7 +18807,7 @@ render();
18338
18807
  }
18339
18808
 
18340
18809
  // src/tools/visualize/transform.ts
18341
- var path24 = __toESM(require("path"), 1);
18810
+ var path26 = __toESM(require("path"), 1);
18342
18811
 
18343
18812
  // src/tools/visualize/modules.ts
18344
18813
  var MAX_MODULES = 18;
@@ -18471,8 +18940,8 @@ function compactModules(prefixToNodes) {
18471
18940
  function deriveModules(nodes) {
18472
18941
  const initial = /* @__PURE__ */ new Map();
18473
18942
  for (const node of nodes) {
18474
- const relative12 = stripToProjectRelative(node.filePath);
18475
- const prefix = modulePrefixFromRelativePath(relative12);
18943
+ const relative14 = stripToProjectRelative(node.filePath);
18944
+ const prefix = modulePrefixFromRelativePath(relative14);
18476
18945
  if (!initial.has(prefix)) initial.set(prefix, []);
18477
18946
  initial.get(prefix)?.push(node);
18478
18947
  }
@@ -18598,7 +19067,7 @@ function transformForVisualization(symbols, edges, options = {}) {
18598
19067
  filePath: s.filePath,
18599
19068
  kind: s.kind,
18600
19069
  line: s.startLine,
18601
- directory: path24.dirname(s.filePath),
19070
+ directory: path26.dirname(s.filePath),
18602
19071
  moduleId: "",
18603
19072
  moduleLabel: ""
18604
19073
  }));
@@ -18918,7 +19387,7 @@ var index_visualize = tool({
18918
19387
  return "No connected symbols found for visualization. Try including orphans with includeOrphans=true, or check that the call graph has resolved edges.";
18919
19388
  }
18920
19389
  const html = generateVisualizationHtml(vizData);
18921
- const outputPath = path25.join(os7.tmpdir(), `call-graph-${Date.now()}.html`);
19390
+ const outputPath = path27.join(os7.tmpdir(), `call-graph-${Date.now()}.html`);
18922
19391
  (0, import_fs15.writeFileSync)(outputPath, html, "utf-8");
18923
19392
  let result = `Temporal call graph visualization generated: ${outputPath}
18924
19393
 
@@ -19025,7 +19494,7 @@ var PI_TOOL_NAMES = [
19025
19494
 
19026
19495
  // src/commands/loader.ts
19027
19496
  var import_fs16 = require("fs");
19028
- var path26 = __toESM(require("path"), 1);
19497
+ var path28 = __toESM(require("path"), 1);
19029
19498
  function parseFrontmatter(content) {
19030
19499
  const frontmatterRegex = /^---\s*\n([\s\S]*?)\n---\s*\n([\s\S]*)$/;
19031
19500
  const match = content.match(frontmatterRegex);
@@ -19051,7 +19520,7 @@ function loadCommandsFromDirectory(commandsDir) {
19051
19520
  }
19052
19521
  const files = (0, import_fs16.readdirSync)(commandsDir).filter((f) => f.endsWith(".md"));
19053
19522
  for (const file of files) {
19054
- const filePath = path26.join(commandsDir, file);
19523
+ const filePath = path28.join(commandsDir, file);
19055
19524
  let content;
19056
19525
  try {
19057
19526
  content = (0, import_fs16.readFileSync)(filePath, "utf-8");
@@ -19060,7 +19529,7 @@ function loadCommandsFromDirectory(commandsDir) {
19060
19529
  throw new Error(`Failed to load command file ${filePath}: ${message}`);
19061
19530
  }
19062
19531
  const { frontmatter, body } = parseFrontmatter(content);
19063
- const name = path26.basename(file, ".md");
19532
+ const name = path28.basename(file, ".md");
19064
19533
  const description = frontmatter.description || `Run the ${name} command`;
19065
19534
  commands.set(name, {
19066
19535
  description,
@@ -19395,23 +19864,41 @@ var RoutingHintController = class {
19395
19864
  // src/adapters/opencode.ts
19396
19865
  var import_meta2 = {};
19397
19866
  var activeWatchers = /* @__PURE__ */ new Map();
19398
- function replaceActiveWatcher(projectRoot, nextWatcher) {
19399
- const existing = activeWatchers.get(projectRoot);
19400
- if (existing) {
19401
- existing.stop();
19402
- activeWatchers.delete(projectRoot);
19403
- }
19404
- if (nextWatcher) {
19405
- activeWatchers.set(projectRoot, nextWatcher);
19867
+ var watcherReplacementChains = /* @__PURE__ */ new Map();
19868
+ async function replaceActiveWatcher(projectRoot, createNextWatcher) {
19869
+ const chain = (watcherReplacementChains.get(projectRoot) ?? Promise.resolve()).catch(() => void 0).then(async () => {
19870
+ const existing = activeWatchers.get(projectRoot);
19871
+ if (existing) {
19872
+ try {
19873
+ await existing.stop();
19874
+ } catch (error) {
19875
+ console.error("[codebase-index] Failed to stop replaced watcher:", error);
19876
+ throw error;
19877
+ }
19878
+ if (activeWatchers.get(projectRoot) === existing) {
19879
+ activeWatchers.delete(projectRoot);
19880
+ }
19881
+ }
19882
+ if (createNextWatcher) {
19883
+ activeWatchers.set(projectRoot, createNextWatcher());
19884
+ }
19885
+ });
19886
+ watcherReplacementChains.set(projectRoot, chain);
19887
+ try {
19888
+ await chain;
19889
+ } finally {
19890
+ if (watcherReplacementChains.get(projectRoot) === chain) {
19891
+ watcherReplacementChains.delete(projectRoot);
19892
+ }
19406
19893
  }
19407
19894
  }
19408
19895
  function getCommandsDir() {
19409
19896
  let currentDir = process.cwd();
19410
19897
  if (typeof import_meta2 !== "undefined" && import_meta2.url) {
19411
- currentDir = path27.dirname((0, import_url.fileURLToPath)(import_meta2.url));
19898
+ currentDir = path29.dirname((0, import_url.fileURLToPath)(import_meta2.url));
19412
19899
  }
19413
- const packageRoot = path27.basename(currentDir) === "adapters" ? path27.join(currentDir, "..", "..") : path27.join(currentDir, "..");
19414
- return path27.join(packageRoot, "commands");
19900
+ const packageRoot = path29.basename(currentDir) === "adapters" ? path29.join(currentDir, "..", "..") : path29.join(currentDir, "..");
19901
+ return path29.join(packageRoot, "commands");
19415
19902
  }
19416
19903
  function appendRoutingHints(output, hints, preferredRole) {
19417
19904
  const preferredBucket = preferredRole === "developer" ? output.developer : output.system;
@@ -19452,9 +19939,12 @@ var plugin = async ({ directory, worktree }) => {
19452
19939
  startAutoIndex(projectRoot, "opencode", "startup");
19453
19940
  }
19454
19941
  if (config.indexing.watchFiles && isValidProject) {
19455
- replaceActiveWatcher(projectRoot, createWatcherWithIndexer(getProjectIndexer, projectRoot, config, "opencode"));
19942
+ await replaceActiveWatcher(
19943
+ projectRoot,
19944
+ () => createWatcherWithIndexer(getProjectIndexer, projectRoot, config, "opencode")
19945
+ );
19456
19946
  } else {
19457
- replaceActiveWatcher(projectRoot, null);
19947
+ await replaceActiveWatcher(projectRoot, null);
19458
19948
  }
19459
19949
  return {
19460
19950
  tool: {