opencode-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.js CHANGED
@@ -328,7 +328,7 @@ var require_ignore = __commonJS({
328
328
  // path matching.
329
329
  // - check `string` either `MODE_IGNORE` or `MODE_CHECK_IGNORE`
330
330
  // @returns {TestResult} true if a file is ignored
331
- test(path28, checkUnignored, mode) {
331
+ test(path30, checkUnignored, mode) {
332
332
  let ignored = false;
333
333
  let unignored = false;
334
334
  let matchedRule;
@@ -337,7 +337,7 @@ var require_ignore = __commonJS({
337
337
  if (unignored === negative && ignored !== unignored || negative && !ignored && !unignored && !checkUnignored) {
338
338
  return;
339
339
  }
340
- const matched = rule[mode].test(path28);
340
+ const matched = rule[mode].test(path30);
341
341
  if (!matched) {
342
342
  return;
343
343
  }
@@ -358,17 +358,17 @@ var require_ignore = __commonJS({
358
358
  var throwError = (message, Ctor) => {
359
359
  throw new Ctor(message);
360
360
  };
361
- var checkPath = (path28, originalPath, doThrow) => {
362
- if (!isString(path28)) {
361
+ var checkPath = (path30, originalPath, doThrow) => {
362
+ if (!isString(path30)) {
363
363
  return doThrow(
364
364
  `path must be a string, but got \`${originalPath}\``,
365
365
  TypeError
366
366
  );
367
367
  }
368
- if (!path28) {
368
+ if (!path30) {
369
369
  return doThrow(`path must not be empty`, TypeError);
370
370
  }
371
- if (checkPath.isNotRelative(path28)) {
371
+ if (checkPath.isNotRelative(path30)) {
372
372
  const r = "`path.relative()`d";
373
373
  return doThrow(
374
374
  `path should be a ${r} string, but got "${originalPath}"`,
@@ -377,7 +377,7 @@ var require_ignore = __commonJS({
377
377
  }
378
378
  return true;
379
379
  };
380
- var isNotRelative = (path28) => REGEX_TEST_INVALID_PATH.test(path28);
380
+ var isNotRelative = (path30) => REGEX_TEST_INVALID_PATH.test(path30);
381
381
  checkPath.isNotRelative = isNotRelative;
382
382
  checkPath.convert = (p) => p;
383
383
  var Ignore2 = class {
@@ -407,19 +407,19 @@ var require_ignore = __commonJS({
407
407
  }
408
408
  // @returns {TestResult}
409
409
  _test(originalPath, cache, checkUnignored, slices) {
410
- const path28 = originalPath && checkPath.convert(originalPath);
410
+ const path30 = originalPath && checkPath.convert(originalPath);
411
411
  checkPath(
412
- path28,
412
+ path30,
413
413
  originalPath,
414
414
  this._strictPathCheck ? throwError : RETURN_FALSE
415
415
  );
416
- return this._t(path28, cache, checkUnignored, slices);
416
+ return this._t(path30, cache, checkUnignored, slices);
417
417
  }
418
- checkIgnore(path28) {
419
- if (!REGEX_TEST_TRAILING_SLASH.test(path28)) {
420
- return this.test(path28);
418
+ checkIgnore(path30) {
419
+ if (!REGEX_TEST_TRAILING_SLASH.test(path30)) {
420
+ return this.test(path30);
421
421
  }
422
- const slices = path28.split(SLASH2).filter(Boolean);
422
+ const slices = path30.split(SLASH2).filter(Boolean);
423
423
  slices.pop();
424
424
  if (slices.length) {
425
425
  const parent = this._t(
@@ -432,18 +432,18 @@ var require_ignore = __commonJS({
432
432
  return parent;
433
433
  }
434
434
  }
435
- return this._rules.test(path28, false, MODE_CHECK_IGNORE);
435
+ return this._rules.test(path30, false, MODE_CHECK_IGNORE);
436
436
  }
437
- _t(path28, cache, checkUnignored, slices) {
438
- if (path28 in cache) {
439
- return cache[path28];
437
+ _t(path30, cache, checkUnignored, slices) {
438
+ if (path30 in cache) {
439
+ return cache[path30];
440
440
  }
441
441
  if (!slices) {
442
- slices = path28.split(SLASH2).filter(Boolean);
442
+ slices = path30.split(SLASH2).filter(Boolean);
443
443
  }
444
444
  slices.pop();
445
445
  if (!slices.length) {
446
- return cache[path28] = this._rules.test(path28, checkUnignored, MODE_IGNORE);
446
+ return cache[path30] = this._rules.test(path30, checkUnignored, MODE_IGNORE);
447
447
  }
448
448
  const parent = this._t(
449
449
  slices.join(SLASH2) + SLASH2,
@@ -451,29 +451,29 @@ var require_ignore = __commonJS({
451
451
  checkUnignored,
452
452
  slices
453
453
  );
454
- return cache[path28] = parent.ignored ? parent : this._rules.test(path28, checkUnignored, MODE_IGNORE);
454
+ return cache[path30] = parent.ignored ? parent : this._rules.test(path30, checkUnignored, MODE_IGNORE);
455
455
  }
456
- ignores(path28) {
457
- return this._test(path28, this._ignoreCache, false).ignored;
456
+ ignores(path30) {
457
+ return this._test(path30, this._ignoreCache, false).ignored;
458
458
  }
459
459
  createFilter() {
460
- return (path28) => !this.ignores(path28);
460
+ return (path30) => !this.ignores(path30);
461
461
  }
462
462
  filter(paths) {
463
463
  return makeArray(paths).filter(this.createFilter());
464
464
  }
465
465
  // @returns {TestResult}
466
- test(path28) {
467
- return this._test(path28, this._testCache, true);
466
+ test(path30) {
467
+ return this._test(path30, this._testCache, true);
468
468
  }
469
469
  };
470
470
  var factory = (options) => new Ignore2(options);
471
- var isPathValid = (path28) => checkPath(path28 && checkPath.convert(path28), path28, RETURN_FALSE);
471
+ var isPathValid = (path30) => checkPath(path30 && checkPath.convert(path30), path30, RETURN_FALSE);
472
472
  var setupWindows = () => {
473
473
  const makePosix = (str) => /^\\\\\?\\/.test(str) || /["<>|\u0000-\u001F]+/u.test(str) ? str : str.replace(/\\/g, "/");
474
474
  checkPath.convert = makePosix;
475
475
  const REGEX_TEST_WINDOWS_PATH_ABSOLUTE = /^[a-z]:\//i;
476
- checkPath.isNotRelative = (path28) => REGEX_TEST_WINDOWS_PATH_ABSOLUTE.test(path28) || isNotRelative(path28);
476
+ checkPath.isNotRelative = (path30) => REGEX_TEST_WINDOWS_PATH_ABSOLUTE.test(path30) || isNotRelative(path30);
477
477
  };
478
478
  if (
479
479
  // Detect `process` so that it can run in browsers.
@@ -651,7 +651,7 @@ var require_eventemitter3 = __commonJS({
651
651
  });
652
652
 
653
653
  // src/adapters/opencode.ts
654
- import * as path27 from "path";
654
+ import * as path29 from "path";
655
655
  import { fileURLToPath as fileURLToPath2 } from "url";
656
656
 
657
657
  // src/config/constants.ts
@@ -1129,11 +1129,11 @@ function resolveGitDir(repoRoot) {
1129
1129
  return null;
1130
1130
  }
1131
1131
  try {
1132
- const stat4 = statSync2(gitPath);
1133
- if (stat4.isDirectory()) {
1132
+ const stat5 = statSync2(gitPath);
1133
+ if (stat5.isDirectory()) {
1134
1134
  return gitPath;
1135
1135
  }
1136
- if (stat4.isFile()) {
1136
+ if (stat5.isFile()) {
1137
1137
  const content = readFileSync2(gitPath, "utf-8").trim();
1138
1138
  const match = content.match(/^gitdir:\s*(.+)$/);
1139
1139
  if (match) {
@@ -2202,7 +2202,7 @@ function analyzeQueryIntent(query) {
2202
2202
  }
2203
2203
  function isTestPath(filePath) {
2204
2204
  const normalized = normalizePath(filePath);
2205
- return /(?:^|\/)(?:test|tests|__tests__|spec|specs)(?:\/|$)/u.test(normalized) || /\.(?:test|spec)\.[^/]+$/u.test(normalized);
2205
+ return /(?:^|\/)(?:test|tests|__tests__|spec|specs)(?:\/|$)/u.test(normalized) || /(?:\.(?:test|spec)|_(?:test|spec))\.[^/]+$/u.test(normalized) || /(?:^|\/)(?:test|spec)_[^/]+\.[^/]+$/u.test(normalized);
2206
2206
  }
2207
2207
  function isFixturePath(filePath) {
2208
2208
  const normalized = normalizePath(filePath);
@@ -2577,8 +2577,8 @@ function formatExactSearchHandoff(results) {
2577
2577
  }
2578
2578
  function formatContextEvidence(result, index) {
2579
2579
  const symbol = result.name ? ` ${JSON.stringify(compactEvidenceValue(result.name, 80))}` : "";
2580
- const path28 = compactEvidenceValue(result.filePath, 120);
2581
- return `[${index}] ${result.chunkType}${symbol} in ${path28}:${result.startLine}-${result.endLine} (score ${result.score.toFixed(2)})`;
2580
+ const path30 = compactEvidenceValue(result.filePath, 120);
2581
+ return `[${index}] ${result.chunkType}${symbol} in ${path30}:${result.startLine}-${result.endLine} (score ${result.score.toFixed(2)})`;
2582
2582
  }
2583
2583
  function formatContextPack(heading, selected, candidateCount, duplicateCount, limitOmittedCount, budgetOmittedCount, includeExactSearchHandoff) {
2584
2584
  const lines = selected.map((result, index) => formatContextEvidence(result, index + 1));
@@ -3741,8 +3741,8 @@ async function* walkDirectory(dir, projectRoot, includePatterns, excludePatterns
3741
3741
  if (entry.isDirectory()) {
3742
3742
  subdirs.push({ fullPath, relativePath });
3743
3743
  } else if (entry.isFile()) {
3744
- const stat4 = await fsPromises.stat(fullPath);
3745
- if (stat4.size > maxFileSize) {
3744
+ const stat5 = await fsPromises.stat(fullPath);
3745
+ if (stat5.size > maxFileSize) {
3746
3746
  skipped.push({ path: relativePath, reason: "too_large" });
3747
3747
  continue;
3748
3748
  }
@@ -3760,7 +3760,7 @@ async function* walkDirectory(dir, projectRoot, includePatterns, excludePatterns
3760
3760
  }
3761
3761
  }
3762
3762
  if (matched) {
3763
- filesInDir.push({ path: fullPath, size: stat4.size });
3763
+ filesInDir.push({ path: fullPath, size: stat5.size });
3764
3764
  }
3765
3765
  }
3766
3766
  }
@@ -3817,8 +3817,8 @@ async function collectFiles(projectRoot, includePatterns, excludePatterns, maxFi
3817
3817
  }
3818
3818
  for (const resolvedKbRoot of normalizedRoots) {
3819
3819
  try {
3820
- const stat4 = await fsPromises.stat(resolvedKbRoot);
3821
- if (!stat4.isDirectory()) {
3820
+ const stat5 = await fsPromises.stat(resolvedKbRoot);
3821
+ if (!stat5.isDirectory()) {
3822
3822
  skipped.push({ path: resolvedKbRoot, reason: "excluded" });
3823
3823
  continue;
3824
3824
  }
@@ -3852,7 +3852,7 @@ function getErrorMessage(error) {
3852
3852
  return error instanceof Error ? error.message : String(error);
3853
3853
  }
3854
3854
  function runCommand(file, args, options) {
3855
- return new Promise((resolve15, reject) => {
3855
+ return new Promise((resolve17, reject) => {
3856
3856
  childProcess.execFile(
3857
3857
  file,
3858
3858
  args,
@@ -3862,7 +3862,7 @@ function runCommand(file, args, options) {
3862
3862
  reject(error);
3863
3863
  return;
3864
3864
  }
3865
- resolve15(stdout);
3865
+ resolve17(stdout);
3866
3866
  }
3867
3867
  );
3868
3868
  });
@@ -4007,10 +4007,10 @@ function safeFailureMessage(error) {
4007
4007
  }
4008
4008
  function cancellableDelay(delayMs, signal) {
4009
4009
  if (signal.aborted) return Promise.reject(new AutoIndexCancelledError());
4010
- return new Promise((resolve15, reject) => {
4010
+ return new Promise((resolve17, reject) => {
4011
4011
  const timer = setTimeout(() => {
4012
4012
  signal.removeEventListener("abort", onAbort);
4013
- resolve15();
4013
+ resolve17();
4014
4014
  }, delayMs);
4015
4015
  timer.unref?.();
4016
4016
  const onAbort = () => {
@@ -4022,15 +4022,15 @@ function cancellableDelay(delayMs, signal) {
4022
4022
  }
4023
4023
  function withTimeout(promise, timeoutMs) {
4024
4024
  if (timeoutMs <= 0) return Promise.resolve(void 0);
4025
- return new Promise((resolve15) => {
4026
- const timer = setTimeout(() => resolve15(void 0), timeoutMs);
4025
+ return new Promise((resolve17) => {
4026
+ const timer = setTimeout(() => resolve17(void 0), timeoutMs);
4027
4027
  timer.unref?.();
4028
4028
  void promise.then((value) => {
4029
4029
  clearTimeout(timer);
4030
- resolve15(value);
4030
+ resolve17(value);
4031
4031
  }, () => {
4032
4032
  clearTimeout(timer);
4033
- resolve15(void 0);
4033
+ resolve17(void 0);
4034
4034
  });
4035
4035
  });
4036
4036
  }
@@ -4412,17 +4412,17 @@ var AutoIndexCoordinator = class {
4412
4412
  }
4413
4413
  }
4414
4414
  waitForBatteryRetry(delayMs) {
4415
- return new Promise((resolve15) => {
4415
+ return new Promise((resolve17) => {
4416
4416
  const timer = setTimeout(() => {
4417
4417
  if (this.batteryRetryTimer === timer) {
4418
4418
  this.batteryRetryTimer = null;
4419
4419
  this.resolveBatteryRetry = null;
4420
4420
  }
4421
- resolve15();
4421
+ resolve17();
4422
4422
  }, delayMs);
4423
4423
  timer.unref?.();
4424
4424
  this.batteryRetryTimer = timer;
4425
- this.resolveBatteryRetry = resolve15;
4425
+ this.resolveBatteryRetry = resolve17;
4426
4426
  });
4427
4427
  }
4428
4428
  cancelBatteryRetry() {
@@ -4430,9 +4430,9 @@ var AutoIndexCoordinator = class {
4430
4430
  clearTimeout(this.batteryRetryTimer);
4431
4431
  this.batteryRetryTimer = null;
4432
4432
  }
4433
- const resolve15 = this.resolveBatteryRetry;
4433
+ const resolve17 = this.resolveBatteryRetry;
4434
4434
  this.resolveBatteryRetry = null;
4435
- resolve15?.();
4435
+ resolve17?.();
4436
4436
  }
4437
4437
  finishBatteryCheck(batteryCheck) {
4438
4438
  if (this.batteryCheck !== batteryCheck) return;
@@ -4637,7 +4637,7 @@ function pTimeout(promise, options) {
4637
4637
  } = options;
4638
4638
  let timer;
4639
4639
  let abortHandler;
4640
- const wrappedPromise = new Promise((resolve15, reject) => {
4640
+ const wrappedPromise = new Promise((resolve17, reject) => {
4641
4641
  if (typeof milliseconds !== "number" || Math.sign(milliseconds) !== 1) {
4642
4642
  throw new TypeError(`Expected \`milliseconds\` to be a positive number, got \`${milliseconds}\``);
4643
4643
  }
@@ -4651,7 +4651,7 @@ function pTimeout(promise, options) {
4651
4651
  };
4652
4652
  signal.addEventListener("abort", abortHandler, { once: true });
4653
4653
  }
4654
- promise.then(resolve15, reject);
4654
+ promise.then(resolve17, reject);
4655
4655
  if (milliseconds === Number.POSITIVE_INFINITY) {
4656
4656
  return;
4657
4657
  }
@@ -4659,7 +4659,7 @@ function pTimeout(promise, options) {
4659
4659
  timer = customTimers.setTimeout.call(void 0, () => {
4660
4660
  if (fallback) {
4661
4661
  try {
4662
- resolve15(fallback());
4662
+ resolve17(fallback());
4663
4663
  } catch (error) {
4664
4664
  reject(error);
4665
4665
  }
@@ -4669,7 +4669,7 @@ function pTimeout(promise, options) {
4669
4669
  promise.cancel();
4670
4670
  }
4671
4671
  if (message === false) {
4672
- resolve15();
4672
+ resolve17();
4673
4673
  } else if (message instanceof Error) {
4674
4674
  reject(message);
4675
4675
  } else {
@@ -5071,7 +5071,7 @@ var PQueue = class extends import_index.default {
5071
5071
  // Assign unique ID if not provided
5072
5072
  id: options.id ?? (this.#idAssigner++).toString()
5073
5073
  };
5074
- return new Promise((resolve15, reject) => {
5074
+ return new Promise((resolve17, reject) => {
5075
5075
  const taskSymbol = /* @__PURE__ */ Symbol(`task-${options.id}`);
5076
5076
  let cleanupQueueAbortHandler = () => void 0;
5077
5077
  const run = async () => {
@@ -5111,7 +5111,7 @@ var PQueue = class extends import_index.default {
5111
5111
  })]);
5112
5112
  }
5113
5113
  const result = await operation;
5114
- resolve15(result);
5114
+ resolve17(result);
5115
5115
  this.emit("completed", result);
5116
5116
  } catch (error) {
5117
5117
  reject(error);
@@ -5299,13 +5299,13 @@ var PQueue = class extends import_index.default {
5299
5299
  });
5300
5300
  }
5301
5301
  async #onEvent(event, filter) {
5302
- return new Promise((resolve15) => {
5302
+ return new Promise((resolve17) => {
5303
5303
  const listener = () => {
5304
5304
  if (filter && !filter()) {
5305
5305
  return;
5306
5306
  }
5307
5307
  this.off(event, listener);
5308
- resolve15();
5308
+ resolve17();
5309
5309
  };
5310
5310
  this.on(event, listener);
5311
5311
  });
@@ -5591,7 +5591,7 @@ async function onAttemptFailure({ error, attemptNumber, retriesConsumed, startTi
5591
5591
  const finalDelay = Math.min(delayTime, remainingTime);
5592
5592
  options.signal?.throwIfAborted();
5593
5593
  if (finalDelay > 0) {
5594
- await new Promise((resolve15, reject) => {
5594
+ await new Promise((resolve17, reject) => {
5595
5595
  const onAbort = () => {
5596
5596
  clearTimeout(timeoutToken);
5597
5597
  options.signal?.removeEventListener("abort", onAbort);
@@ -5599,7 +5599,7 @@ async function onAttemptFailure({ error, attemptNumber, retriesConsumed, startTi
5599
5599
  };
5600
5600
  const timeoutToken = setTimeout(() => {
5601
5601
  options.signal?.removeEventListener("abort", onAbort);
5602
- resolve15();
5602
+ resolve17();
5603
5603
  }, finalDelay);
5604
5604
  if (options.unref) {
5605
5605
  timeoutToken.unref?.();
@@ -6405,85 +6405,6 @@ function createEmbeddingProvider(configuredProviderInfo) {
6405
6405
  }
6406
6406
  }
6407
6407
 
6408
- // src/rerank/index.ts
6409
- function createReranker(config) {
6410
- if (!config.enabled) {
6411
- return new NoOpReranker();
6412
- }
6413
- return new SiliconFlowReranker(config);
6414
- }
6415
- var NoOpReranker = class {
6416
- isAvailable() {
6417
- return false;
6418
- }
6419
- async rerank(_query, documents, _topN) {
6420
- return {
6421
- results: documents.map((_, index) => ({ index, relevanceScore: 0 }))
6422
- };
6423
- }
6424
- };
6425
- var SiliconFlowReranker = class {
6426
- config;
6427
- constructor(config) {
6428
- this.config = config;
6429
- }
6430
- isAvailable() {
6431
- return this.config.enabled && !!this.config.baseUrl && !!this.config.model;
6432
- }
6433
- async rerank(query, documents, topN) {
6434
- if (documents.length === 0) {
6435
- return { results: [] };
6436
- }
6437
- const headers = {
6438
- "Content-Type": "application/json"
6439
- };
6440
- if (this.config.apiKey) {
6441
- headers["Authorization"] = `Bearer ${this.config.apiKey}`;
6442
- }
6443
- const baseUrl = this.config.baseUrl;
6444
- if (!baseUrl) {
6445
- throw new Error("Reranker baseUrl is required. Configure reranker.baseUrl in your codebase-index.json.");
6446
- }
6447
- const timeoutMs = this.config.timeoutMs ?? 3e4;
6448
- const controller = new AbortController();
6449
- const timeout = setTimeout(() => controller.abort(), timeoutMs);
6450
- try {
6451
- const response = await fetch(`${baseUrl}/rerank`, {
6452
- method: "POST",
6453
- headers,
6454
- body: JSON.stringify({
6455
- model: this.config.model,
6456
- query,
6457
- documents,
6458
- top_n: topN ?? this.config.topN ?? 20,
6459
- return_documents: false
6460
- }),
6461
- signal: controller.signal
6462
- });
6463
- clearTimeout(timeout);
6464
- if (!response.ok) {
6465
- const errorText = await response.text();
6466
- throw new Error(`Rerank API error: ${response.status} - ${errorText}`);
6467
- }
6468
- const data = await response.json();
6469
- return {
6470
- results: data.results.map((r) => ({
6471
- index: r.index,
6472
- relevanceScore: r.relevance_score,
6473
- document: r.document?.text
6474
- })),
6475
- tokensUsed: data.meta?.tokens?.input_tokens
6476
- };
6477
- } catch (error) {
6478
- clearTimeout(timeout);
6479
- if (error instanceof Error && error.name === "AbortError") {
6480
- throw new Error(`Rerank API request timed out after ${timeoutMs}ms`);
6481
- }
6482
- throw error;
6483
- }
6484
- }
6485
- };
6486
-
6487
6408
  // src/utils/cost.ts
6488
6409
  function estimateChunksFromFiles(files) {
6489
6410
  let totalChunks = 0;
@@ -8043,8 +7964,8 @@ async function isWorktreeRegistered(projectRoot, worktreePath) {
8043
7964
  return false;
8044
7965
  }
8045
7966
  function isPathWithinRoot(filePath, rootPath) {
8046
- const relative12 = path15.relative(path15.resolve(rootPath), path15.resolve(filePath));
8047
- return relative12 === "" || !relative12.startsWith(`..${path15.sep}`) && relative12 !== ".." && !path15.isAbsolute(relative12);
7967
+ const relative14 = path15.relative(path15.resolve(rootPath), path15.resolve(filePath));
7968
+ return relative14 === "" || !relative14.startsWith(`..${path15.sep}`) && relative14 !== ".." && !path15.isAbsolute(relative14);
8048
7969
  }
8049
7970
  async function pruneExactMissingWorktreeRegistration(projectRoot, worktreePath) {
8050
7971
  if (await pathExists(worktreePath)) return false;
@@ -8421,11 +8342,11 @@ function normalizeFiles(rawFiles, projectRoot) {
8421
8342
  for (const raw of rawFiles) {
8422
8343
  if (raw.length === 0) continue;
8423
8344
  const absolute = path16.resolve(root, raw);
8424
- const relative12 = path16.relative(root, absolute);
8425
- if (path16.isAbsolute(raw) || relative12 === ".." || relative12.startsWith(`..${path16.sep}`) || path16.isAbsolute(relative12)) {
8345
+ const relative14 = path16.relative(root, absolute);
8346
+ if (path16.isAbsolute(raw) || relative14 === ".." || relative14.startsWith(`..${path16.sep}`) || path16.isAbsolute(relative14)) {
8426
8347
  throw new Error(`Changed file escapes the project root: ${JSON.stringify(raw)}`);
8427
8348
  }
8428
- const cleaned = relative12.startsWith(`.${path16.sep}`) ? relative12.slice(2) : relative12;
8349
+ const cleaned = relative14.startsWith(`.${path16.sep}`) ? relative14.slice(2) : relative14;
8429
8350
  if (!seen.has(cleaned)) {
8430
8351
  seen.add(cleaned);
8431
8352
  result.push(cleaned);
@@ -8657,7 +8578,8 @@ function rankHybridResults(query, semanticResults, keywordResults, options) {
8657
8578
  return cached;
8658
8579
  }
8659
8580
  }
8660
- const overfetchLimit = Math.max(options.limit * 4, options.limit);
8581
+ const overfetchFactor = prioritizeSourcePaths ? 12 : 4;
8582
+ const overfetchLimit = Math.max(options.limit * overfetchFactor, options.limit);
8661
8583
  const fused = options.fusionStrategy === "rrf" ? fuseResultsRrf(semanticResults, keywordResults, options.rrfK, overfetchLimit) : fuseResultsWeighted(semanticResults, keywordResults, options.hybridWeight, overfetchLimit);
8662
8584
  const rerankPoolLimit = Math.max(overfetchLimit, options.rerankTopN * 3, options.limit * 6);
8663
8585
  const rerankPool = fused.slice(0, rerankPoolLimit);
@@ -9043,6 +8965,29 @@ function extractPrimaryIdentifierQueryHint(query) {
9043
8965
  const best = codeTerms.find((term) => term.length >= 6);
9044
8966
  return best ?? null;
9045
8967
  }
8968
+ function pathSegmentsForAffinityMatch(filePath) {
8969
+ const normalizedPath2 = normalizeRankingText(filePath).replace(/\\/g, "/");
8970
+ const segments = normalizedPath2.split("/").filter((segment) => segment.length > 0);
8971
+ if (segments.length === 0) {
8972
+ return [];
8973
+ }
8974
+ const basename9 = segments[segments.length - 1] ?? "";
8975
+ const basenameWithoutExt = basename9.replace(/\.[^/.]+$/u, "");
8976
+ const normalizedSegments = segments.map((segment) => segment.toLowerCase());
8977
+ return Array.from(/* @__PURE__ */ new Set([
8978
+ ...normalizedSegments,
8979
+ basenameWithoutExt.toLowerCase()
8980
+ ]));
8981
+ }
8982
+ function hasModuleAffinity(filePath, exactIdentifierVariants) {
8983
+ const haystack = pathSegmentsForAffinityMatch(filePath);
8984
+ return exactIdentifierVariants.some((variant) => {
8985
+ if (!variant || variant.length < 2) {
8986
+ return false;
8987
+ }
8988
+ return haystack.includes(variant);
8989
+ });
8990
+ }
9046
8991
  var FILE_PATH_HINT_EXTENSIONS = [
9047
8992
  "ts",
9048
8993
  "tsx",
@@ -9118,10 +9063,13 @@ function buildDeterministicIdentifierPass(query, candidates, limit, prioritizeSo
9118
9063
  ).map((candidate) => {
9119
9064
  const nameLower = (candidate.metadata.name ?? "").toLowerCase();
9120
9065
  const pathLower = candidate.metadata.filePath.toLowerCase();
9121
- let maxMatch = 0;
9122
- const nameMatchesPrimary = primaryVariants.some(
9066
+ const exactIdentifierVariants = primaryVariants.filter((value) => value.length >= 2);
9067
+ const exactMatch = exactIdentifierVariants.some(
9123
9068
  (variant) => nameLower === variant || nameLower.replace(/[^a-z0-9]/g, "") === variant.replace(/[^a-z0-9]/g, "")
9124
9069
  );
9070
+ let maxMatch = 0;
9071
+ const nameMatchesPrimary = exactMatch;
9072
+ const pathAffinity = exactMatch ? hasModuleAffinity(candidate.metadata.filePath, exactIdentifierVariants) : false;
9125
9073
  const pathMatchesFileHint = filePathHint ? pathMatchesHint(candidate.metadata.filePath, filePathHint) : false;
9126
9074
  for (const hint of hints) {
9127
9075
  const variants = normalizeIdentifierVariants(hint);
@@ -9142,12 +9090,17 @@ function buildDeterministicIdentifierPass(query, candidates, limit, prioritizeSo
9142
9090
  candidate,
9143
9091
  maxMatch,
9144
9092
  pathMatchesFileHint,
9145
- nameMatchesPrimary
9093
+ nameMatchesPrimary,
9094
+ pathAffinity
9146
9095
  };
9147
9096
  }).filter((entry) => entry.maxMatch >= 0.7).sort((a, b) => {
9148
9097
  const aAnchored = a.pathMatchesFileHint && a.nameMatchesPrimary ? 1 : 0;
9149
9098
  const bAnchored = b.pathMatchesFileHint && b.nameMatchesPrimary ? 1 : 0;
9150
9099
  if (aAnchored !== bAnchored) return bAnchored - aAnchored;
9100
+ if (a.nameMatchesPrimary !== b.nameMatchesPrimary) {
9101
+ return b.nameMatchesPrimary ? 1 : -1;
9102
+ }
9103
+ if (a.pathAffinity !== b.pathAffinity) return b.pathAffinity ? 1 : -1;
9151
9104
  if (b.maxMatch !== a.maxMatch) return b.maxMatch - a.maxMatch;
9152
9105
  if (b.candidate.score !== a.candidate.score) return b.candidate.score - a.candidate.score;
9153
9106
  return a.candidate.id.localeCompare(b.candidate.id);
@@ -10137,7 +10090,6 @@ var Indexer = class _Indexer {
10137
10090
  database = null;
10138
10091
  provider = null;
10139
10092
  configuredProviderInfo = null;
10140
- reranker = null;
10141
10093
  fileHashCache = /* @__PURE__ */ new Map();
10142
10094
  fileHashCachePath = "";
10143
10095
  failedBatchesPath = "";
@@ -10297,7 +10249,6 @@ var Indexer = class _Indexer {
10297
10249
  this.database = null;
10298
10250
  this.provider = null;
10299
10251
  this.configuredProviderInfo = null;
10300
- this.reranker = null;
10301
10252
  this.indexCompatibility = null;
10302
10253
  this.initializationMode = "none";
10303
10254
  this.readIssues = [];
@@ -11027,7 +10978,7 @@ var Indexer = class _Indexer {
11027
10978
  await options.queue.onSizeLessThan(Math.max(1, options.providerRateLimits.concurrency));
11028
10979
  const task = options.queue.add(async () => {
11029
10980
  if (options.rateLimitState.backoffMs > 0) {
11030
- await new Promise((resolve15) => setTimeout(resolve15, options.rateLimitState.backoffMs));
10981
+ await new Promise((resolve17) => setTimeout(resolve17, options.rateLimitState.backoffMs));
11031
10982
  }
11032
10983
  try {
11033
10984
  const embeddingResult = await pRetry(
@@ -11594,15 +11545,6 @@ var Indexer = class _Indexer {
11594
11545
  rerankerEnabled: this.config.reranker?.enabled ?? false
11595
11546
  });
11596
11547
  this.provider = createEmbeddingProvider(this.configuredProviderInfo);
11597
- if (this.config.reranker?.enabled) {
11598
- this.reranker = createReranker(this.config.reranker);
11599
- if (this.reranker.isAvailable()) {
11600
- this.logger.info("Reranker initialized", {
11601
- model: this.config.reranker.model,
11602
- baseUrl: this.config.reranker.baseUrl
11603
- });
11604
- }
11605
- }
11606
11548
  const dimensions = this.configuredProviderInfo.modelInfo.dimensions;
11607
11549
  const storePath = path19.join(this.indexPath, "vectors");
11608
11550
  const vectorMetadataPath = `${storePath}.meta.json`;
@@ -13007,6 +12949,7 @@ var Indexer = class _Indexer {
13007
12949
  const filterByBranch = options?.filterByBranch ?? true;
13008
12950
  const sourceIntent = options?.definitionIntent === true || classifyQueryIntentRaw(query) === "source";
13009
12951
  const identifierHints = extractIdentifierHints(query);
12952
+ const candidateLimit = maxResults * (sourceIntent ? 12 : 4);
13010
12953
  this.logger.search("debug", "Starting search", {
13011
12954
  query,
13012
12955
  maxResults,
@@ -13043,7 +12986,7 @@ var Indexer = class _Indexer {
13043
12986
  const semanticCandidates = embedding ? this.searchSemanticCandidates(
13044
12987
  store,
13045
12988
  embedding,
13046
- maxResults * 4,
12989
+ candidateLimit,
13047
12990
  branchChunkIds,
13048
12991
  shouldPrefilterByBranch
13049
12992
  ) : [];
@@ -13051,7 +12994,7 @@ var Indexer = class _Indexer {
13051
12994
  const keywordStartTime = performance2.now();
13052
12995
  const keywordCandidates = await this.keywordSearch(
13053
12996
  query,
13054
- maxResults * 4,
12997
+ candidateLimit,
13055
12998
  store,
13056
12999
  invertedIndex,
13057
13000
  branchChunkIds,
@@ -13809,9 +13752,9 @@ var Indexer = class _Indexer {
13809
13752
  this.requireReadableComponents(readIssues, "database");
13810
13753
  let shortest = [];
13811
13754
  for (const branchKey of this.getBranchCatalogKeys()) {
13812
- const path28 = database.findShortestPath(fromName, toName, branchKey, maxDepth);
13813
- if (path28.length > 0 && (shortest.length === 0 || path28.length < shortest.length)) {
13814
- shortest = path28;
13755
+ const path30 = database.findShortestPath(fromName, toName, branchKey, maxDepth);
13756
+ if (path30.length > 0 && (shortest.length === 0 || path30.length < shortest.length)) {
13757
+ shortest = path30;
13815
13758
  }
13816
13759
  }
13817
13760
  return shortest.map((hop) => this.resolveFilePathRecord(hop));
@@ -13859,13 +13802,13 @@ var Indexer = class _Indexer {
13859
13802
  }
13860
13803
  }
13861
13804
  if (!found) continue;
13862
- const path28 = [];
13805
+ const path30 = [];
13863
13806
  let currentSymbolId = toSymbolId;
13864
13807
  while (true) {
13865
13808
  const symbol = symbolsById.get(currentSymbolId);
13866
13809
  if (!symbol) break;
13867
13810
  const parent = parentBySymbolId.get(currentSymbolId);
13868
- path28.push({
13811
+ path30.push({
13869
13812
  symbolId: symbol.id,
13870
13813
  symbolName: symbol.name,
13871
13814
  filePath: symbol.filePath,
@@ -13875,9 +13818,9 @@ var Indexer = class _Indexer {
13875
13818
  if (!parent) break;
13876
13819
  currentSymbolId = parent.parentId;
13877
13820
  }
13878
- path28.reverse();
13879
- if (path28.length > 0 && (shortest.length === 0 || path28.length < shortest.length)) {
13880
- shortest = path28;
13821
+ path30.reverse();
13822
+ if (path30.length > 0 && (shortest.length === 0 || path30.length < shortest.length)) {
13823
+ shortest = path30;
13881
13824
  }
13882
13825
  }
13883
13826
  return shortest.map((hop) => this.resolveFilePathRecord(hop));
@@ -14213,7 +14156,6 @@ var Indexer = class _Indexer {
14213
14156
  this.store = null;
14214
14157
  this.invertedIndex = null;
14215
14158
  this.provider = null;
14216
- this.reranker = null;
14217
14159
  this.configuredProviderInfo = null;
14218
14160
  this.indexCompatibility = null;
14219
14161
  this.initializationMode = "none";
@@ -14538,12 +14480,12 @@ async function getCallGraphPath(projectRoot, host, from, to, maxDepth, fromFileP
14538
14480
  if (fromResolution.status !== "resolved" || toResolution.status !== "resolved") {
14539
14481
  return { from: fromResolution, to: toResolution, path: [] };
14540
14482
  }
14541
- const path28 = await indexer.findCallPathBySymbolIds(
14483
+ const path30 = await indexer.findCallPathBySymbolIds(
14542
14484
  fromResolution.symbolId,
14543
14485
  toResolution.symbolId,
14544
14486
  maxDepth
14545
14487
  );
14546
- return { from: fromResolution, to: toResolution, path: path28 };
14488
+ return { from: fromResolution, to: toResolution, path: path30 };
14547
14489
  }
14548
14490
  async function runIndexCodebase(projectRoot, host, args, onProgress) {
14549
14491
  const root = getProjectRoot(projectRoot, host);
@@ -14769,8 +14711,8 @@ function addKnowledgeBase(projectRoot, host, knowledgeBasePath) {
14769
14711
  }
14770
14712
  }
14771
14713
  try {
14772
- const stat4 = statSync5(normalizedPath2);
14773
- if (!stat4.isDirectory()) {
14714
+ const stat5 = statSync5(normalizedPath2);
14715
+ if (!stat5.isDirectory()) {
14774
14716
  return `Error: Path is not a directory: ${normalizedPath2}`;
14775
14717
  }
14776
14718
  } catch (error) {
@@ -14818,8 +14760,8 @@ function listKnowledgeBases(projectRoot, host) {
14818
14760
  `;
14819
14761
  if (exists) {
14820
14762
  try {
14821
- const stat4 = statSync5(resolvedPath);
14822
- result += ` Type: ${stat4.isDirectory() ? "Directory" : "File"}
14763
+ const stat5 = statSync5(resolvedPath);
14764
+ result += ` Type: ${stat5.isDirectory() ? "Directory" : "File"}
14823
14765
  `;
14824
14766
  } catch {
14825
14767
  }
@@ -14860,7 +14802,7 @@ Run /index to rebuild the index without the removed knowledge base.`;
14860
14802
  }
14861
14803
 
14862
14804
  // src/watcher/file-watcher.ts
14863
- import { existsSync as existsSync13 } from "fs";
14805
+ import { existsSync as existsSync13, statSync as statSync6 } from "fs";
14864
14806
 
14865
14807
  // node_modules/chokidar/index.js
14866
14808
  import { EventEmitter as EventEmitter2 } from "events";
@@ -14952,7 +14894,7 @@ var ReaddirpStream = class extends Readable {
14952
14894
  this._directoryFilter = normalizeFilter(opts.directoryFilter);
14953
14895
  const statMethod = opts.lstat ? lstat : stat;
14954
14896
  if (wantBigintFsStats) {
14955
- this._stat = (path28) => statMethod(path28, { bigint: true });
14897
+ this._stat = (path30) => statMethod(path30, { bigint: true });
14956
14898
  } else {
14957
14899
  this._stat = statMethod;
14958
14900
  }
@@ -14977,8 +14919,8 @@ var ReaddirpStream = class extends Readable {
14977
14919
  const par = this.parent;
14978
14920
  const fil = par && par.files;
14979
14921
  if (fil && fil.length > 0) {
14980
- const { path: path28, depth } = par;
14981
- const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path28));
14922
+ const { path: path30, depth } = par;
14923
+ const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path30));
14982
14924
  const awaited = await Promise.all(slice);
14983
14925
  for (const entry of awaited) {
14984
14926
  if (!entry)
@@ -15018,20 +14960,20 @@ var ReaddirpStream = class extends Readable {
15018
14960
  this.reading = false;
15019
14961
  }
15020
14962
  }
15021
- async _exploreDir(path28, depth) {
14963
+ async _exploreDir(path30, depth) {
15022
14964
  let files;
15023
14965
  try {
15024
- files = await readdir(path28, this._rdOptions);
14966
+ files = await readdir(path30, this._rdOptions);
15025
14967
  } catch (error) {
15026
14968
  this._onError(error);
15027
14969
  }
15028
- return { files, depth, path: path28 };
14970
+ return { files, depth, path: path30 };
15029
14971
  }
15030
- async _formatEntry(dirent, path28) {
14972
+ async _formatEntry(dirent, path30) {
15031
14973
  let entry;
15032
14974
  const basename9 = this._isDirent ? dirent.name : dirent;
15033
14975
  try {
15034
- const fullPath = presolve(pjoin(path28, basename9));
14976
+ const fullPath = presolve(pjoin(path30, basename9));
15035
14977
  entry = { path: prelative(this._root, fullPath), fullPath, basename: basename9 };
15036
14978
  entry[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath);
15037
14979
  } catch (err) {
@@ -15431,16 +15373,16 @@ var delFromSet = (main, prop, item) => {
15431
15373
  };
15432
15374
  var isEmptySet = (val) => val instanceof Set ? val.size === 0 : !val;
15433
15375
  var FsWatchInstances = /* @__PURE__ */ new Map();
15434
- function createFsWatchInstance(path28, options, listener, errHandler, emitRaw) {
15376
+ function createFsWatchInstance(path30, options, listener, errHandler, emitRaw) {
15435
15377
  const handleEvent = (rawEvent, evPath) => {
15436
- listener(path28);
15437
- emitRaw(rawEvent, evPath, { watchedPath: path28 });
15438
- if (evPath && path28 !== evPath) {
15439
- fsWatchBroadcast(sp.resolve(path28, evPath), KEY_LISTENERS, sp.join(path28, evPath));
15378
+ listener(path30);
15379
+ emitRaw(rawEvent, evPath, { watchedPath: path30 });
15380
+ if (evPath && path30 !== evPath) {
15381
+ fsWatchBroadcast(sp.resolve(path30, evPath), KEY_LISTENERS, sp.join(path30, evPath));
15440
15382
  }
15441
15383
  };
15442
15384
  try {
15443
- return fs_watch(path28, {
15385
+ return fs_watch(path30, {
15444
15386
  persistent: options.persistent
15445
15387
  }, handleEvent);
15446
15388
  } catch (error) {
@@ -15456,12 +15398,12 @@ var fsWatchBroadcast = (fullPath, listenerType, val1, val2, val3) => {
15456
15398
  listener(val1, val2, val3);
15457
15399
  });
15458
15400
  };
15459
- var setFsWatchListener = (path28, fullPath, options, handlers) => {
15401
+ var setFsWatchListener = (path30, fullPath, options, handlers) => {
15460
15402
  const { listener, errHandler, rawEmitter } = handlers;
15461
15403
  let cont = FsWatchInstances.get(fullPath);
15462
15404
  let watcher;
15463
15405
  if (!options.persistent) {
15464
- watcher = createFsWatchInstance(path28, options, listener, errHandler, rawEmitter);
15406
+ watcher = createFsWatchInstance(path30, options, listener, errHandler, rawEmitter);
15465
15407
  if (!watcher)
15466
15408
  return;
15467
15409
  return watcher.close.bind(watcher);
@@ -15472,7 +15414,7 @@ var setFsWatchListener = (path28, fullPath, options, handlers) => {
15472
15414
  addAndConvert(cont, KEY_RAW, rawEmitter);
15473
15415
  } else {
15474
15416
  watcher = createFsWatchInstance(
15475
- path28,
15417
+ path30,
15476
15418
  options,
15477
15419
  fsWatchBroadcast.bind(null, fullPath, KEY_LISTENERS),
15478
15420
  errHandler,
@@ -15487,7 +15429,7 @@ var setFsWatchListener = (path28, fullPath, options, handlers) => {
15487
15429
  cont.watcherUnusable = true;
15488
15430
  if (isWindows && error.code === "EPERM") {
15489
15431
  try {
15490
- const fd = await open(path28, "r");
15432
+ const fd = await open(path30, "r");
15491
15433
  await fd.close();
15492
15434
  broadcastErr(error);
15493
15435
  } catch (err) {
@@ -15518,7 +15460,7 @@ var setFsWatchListener = (path28, fullPath, options, handlers) => {
15518
15460
  };
15519
15461
  };
15520
15462
  var FsWatchFileInstances = /* @__PURE__ */ new Map();
15521
- var setFsWatchFileListener = (path28, fullPath, options, handlers) => {
15463
+ var setFsWatchFileListener = (path30, fullPath, options, handlers) => {
15522
15464
  const { listener, rawEmitter } = handlers;
15523
15465
  let cont = FsWatchFileInstances.get(fullPath);
15524
15466
  const copts = cont && cont.options;
@@ -15540,7 +15482,7 @@ var setFsWatchFileListener = (path28, fullPath, options, handlers) => {
15540
15482
  });
15541
15483
  const currmtime = curr.mtimeMs;
15542
15484
  if (curr.size !== prev.size || currmtime > prev.mtimeMs || currmtime === 0) {
15543
- foreach(cont.listeners, (listener2) => listener2(path28, curr));
15485
+ foreach(cont.listeners, (listener2) => listener2(path30, curr));
15544
15486
  }
15545
15487
  })
15546
15488
  };
@@ -15570,13 +15512,13 @@ var NodeFsHandler = class {
15570
15512
  * @param listener on fs change
15571
15513
  * @returns closer for the watcher instance
15572
15514
  */
15573
- _watchWithNodeFs(path28, listener) {
15515
+ _watchWithNodeFs(path30, listener) {
15574
15516
  const opts = this.fsw.options;
15575
- const directory = sp.dirname(path28);
15576
- const basename9 = sp.basename(path28);
15517
+ const directory = sp.dirname(path30);
15518
+ const basename9 = sp.basename(path30);
15577
15519
  const parent = this.fsw._getWatchedDir(directory);
15578
15520
  parent.add(basename9);
15579
- const absolutePath = sp.resolve(path28);
15521
+ const absolutePath = sp.resolve(path30);
15580
15522
  const options = {
15581
15523
  persistent: opts.persistent
15582
15524
  };
@@ -15586,12 +15528,12 @@ var NodeFsHandler = class {
15586
15528
  if (opts.usePolling) {
15587
15529
  const enableBin = opts.interval !== opts.binaryInterval;
15588
15530
  options.interval = enableBin && isBinaryPath(basename9) ? opts.binaryInterval : opts.interval;
15589
- closer = setFsWatchFileListener(path28, absolutePath, options, {
15531
+ closer = setFsWatchFileListener(path30, absolutePath, options, {
15590
15532
  listener,
15591
15533
  rawEmitter: this.fsw._emitRaw
15592
15534
  });
15593
15535
  } else {
15594
- closer = setFsWatchListener(path28, absolutePath, options, {
15536
+ closer = setFsWatchListener(path30, absolutePath, options, {
15595
15537
  listener,
15596
15538
  errHandler: this._boundHandleError,
15597
15539
  rawEmitter: this.fsw._emitRaw
@@ -15613,7 +15555,7 @@ var NodeFsHandler = class {
15613
15555
  let prevStats = stats;
15614
15556
  if (parent.has(basename9))
15615
15557
  return;
15616
- const listener = async (path28, newStats) => {
15558
+ const listener = async (path30, newStats) => {
15617
15559
  if (!this.fsw._throttle(THROTTLE_MODE_WATCH, file, 5))
15618
15560
  return;
15619
15561
  if (!newStats || newStats.mtimeMs === 0) {
@@ -15627,11 +15569,11 @@ var NodeFsHandler = class {
15627
15569
  this.fsw._emit(EV.CHANGE, file, newStats2);
15628
15570
  }
15629
15571
  if ((isMacos || isLinux || isFreeBSD) && prevStats.ino !== newStats2.ino) {
15630
- this.fsw._closeFile(path28);
15572
+ this.fsw._closeFile(path30);
15631
15573
  prevStats = newStats2;
15632
15574
  const closer2 = this._watchWithNodeFs(file, listener);
15633
15575
  if (closer2)
15634
- this.fsw._addPathCloser(path28, closer2);
15576
+ this.fsw._addPathCloser(path30, closer2);
15635
15577
  } else {
15636
15578
  prevStats = newStats2;
15637
15579
  }
@@ -15663,7 +15605,7 @@ var NodeFsHandler = class {
15663
15605
  * @param item basename of this item
15664
15606
  * @returns true if no more processing is needed for this entry.
15665
15607
  */
15666
- async _handleSymlink(entry, directory, path28, item) {
15608
+ async _handleSymlink(entry, directory, path30, item) {
15667
15609
  if (this.fsw.closed) {
15668
15610
  return;
15669
15611
  }
@@ -15673,7 +15615,7 @@ var NodeFsHandler = class {
15673
15615
  this.fsw._incrReadyCount();
15674
15616
  let linkPath;
15675
15617
  try {
15676
- linkPath = await fsrealpath(path28);
15618
+ linkPath = await fsrealpath(path30);
15677
15619
  } catch (e) {
15678
15620
  this.fsw._emitReady();
15679
15621
  return true;
@@ -15683,12 +15625,12 @@ var NodeFsHandler = class {
15683
15625
  if (dir.has(item)) {
15684
15626
  if (this.fsw._symlinkPaths.get(full) !== linkPath) {
15685
15627
  this.fsw._symlinkPaths.set(full, linkPath);
15686
- this.fsw._emit(EV.CHANGE, path28, entry.stats);
15628
+ this.fsw._emit(EV.CHANGE, path30, entry.stats);
15687
15629
  }
15688
15630
  } else {
15689
15631
  dir.add(item);
15690
15632
  this.fsw._symlinkPaths.set(full, linkPath);
15691
- this.fsw._emit(EV.ADD, path28, entry.stats);
15633
+ this.fsw._emit(EV.ADD, path30, entry.stats);
15692
15634
  }
15693
15635
  this.fsw._emitReady();
15694
15636
  return true;
@@ -15718,9 +15660,9 @@ var NodeFsHandler = class {
15718
15660
  return;
15719
15661
  }
15720
15662
  const item = entry.path;
15721
- let path28 = sp.join(directory, item);
15663
+ let path30 = sp.join(directory, item);
15722
15664
  current.add(item);
15723
- if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path28, item)) {
15665
+ if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path30, item)) {
15724
15666
  return;
15725
15667
  }
15726
15668
  if (this.fsw.closed) {
@@ -15729,11 +15671,11 @@ var NodeFsHandler = class {
15729
15671
  }
15730
15672
  if (item === target || !target && !previous.has(item)) {
15731
15673
  this.fsw._incrReadyCount();
15732
- path28 = sp.join(dir, sp.relative(dir, path28));
15733
- this._addToNodeFs(path28, initialAdd, wh, depth + 1);
15674
+ path30 = sp.join(dir, sp.relative(dir, path30));
15675
+ this._addToNodeFs(path30, initialAdd, wh, depth + 1);
15734
15676
  }
15735
15677
  }).on(EV.ERROR, this._boundHandleError);
15736
- return new Promise((resolve15, reject) => {
15678
+ return new Promise((resolve17, reject) => {
15737
15679
  if (!stream)
15738
15680
  return reject();
15739
15681
  stream.once(STR_END, () => {
@@ -15742,7 +15684,7 @@ var NodeFsHandler = class {
15742
15684
  return;
15743
15685
  }
15744
15686
  const wasThrottled = throttler ? throttler.clear() : false;
15745
- resolve15(void 0);
15687
+ resolve17(void 0);
15746
15688
  previous.getChildren().filter((item) => {
15747
15689
  return item !== directory && !current.has(item);
15748
15690
  }).forEach((item) => {
@@ -15799,13 +15741,13 @@ var NodeFsHandler = class {
15799
15741
  * @param depth Child path actually targeted for watch
15800
15742
  * @param target Child path actually targeted for watch
15801
15743
  */
15802
- async _addToNodeFs(path28, initialAdd, priorWh, depth, target) {
15744
+ async _addToNodeFs(path30, initialAdd, priorWh, depth, target) {
15803
15745
  const ready = this.fsw._emitReady;
15804
- if (this.fsw._isIgnored(path28) || this.fsw.closed) {
15746
+ if (this.fsw._isIgnored(path30) || this.fsw.closed) {
15805
15747
  ready();
15806
15748
  return false;
15807
15749
  }
15808
- const wh = this.fsw._getWatchHelpers(path28);
15750
+ const wh = this.fsw._getWatchHelpers(path30);
15809
15751
  if (priorWh) {
15810
15752
  wh.filterPath = (entry) => priorWh.filterPath(entry);
15811
15753
  wh.filterDir = (entry) => priorWh.filterDir(entry);
@@ -15821,8 +15763,8 @@ var NodeFsHandler = class {
15821
15763
  const follow = this.fsw.options.followSymlinks;
15822
15764
  let closer;
15823
15765
  if (stats.isDirectory()) {
15824
- const absPath = sp.resolve(path28);
15825
- const targetPath = follow ? await fsrealpath(path28) : path28;
15766
+ const absPath = sp.resolve(path30);
15767
+ const targetPath = follow ? await fsrealpath(path30) : path30;
15826
15768
  if (this.fsw.closed)
15827
15769
  return;
15828
15770
  closer = await this._handleDir(wh.watchPath, stats, initialAdd, depth, target, wh, targetPath);
@@ -15832,29 +15774,29 @@ var NodeFsHandler = class {
15832
15774
  this.fsw._symlinkPaths.set(absPath, targetPath);
15833
15775
  }
15834
15776
  } else if (stats.isSymbolicLink()) {
15835
- const targetPath = follow ? await fsrealpath(path28) : path28;
15777
+ const targetPath = follow ? await fsrealpath(path30) : path30;
15836
15778
  if (this.fsw.closed)
15837
15779
  return;
15838
15780
  const parent = sp.dirname(wh.watchPath);
15839
15781
  this.fsw._getWatchedDir(parent).add(wh.watchPath);
15840
15782
  this.fsw._emit(EV.ADD, wh.watchPath, stats);
15841
- closer = await this._handleDir(parent, stats, initialAdd, depth, path28, wh, targetPath);
15783
+ closer = await this._handleDir(parent, stats, initialAdd, depth, path30, wh, targetPath);
15842
15784
  if (this.fsw.closed)
15843
15785
  return;
15844
15786
  if (targetPath !== void 0) {
15845
- this.fsw._symlinkPaths.set(sp.resolve(path28), targetPath);
15787
+ this.fsw._symlinkPaths.set(sp.resolve(path30), targetPath);
15846
15788
  }
15847
15789
  } else {
15848
15790
  closer = this._handleFile(wh.watchPath, stats, initialAdd);
15849
15791
  }
15850
15792
  ready();
15851
15793
  if (closer)
15852
- this.fsw._addPathCloser(path28, closer);
15794
+ this.fsw._addPathCloser(path30, closer);
15853
15795
  return false;
15854
15796
  } catch (error) {
15855
15797
  if (this.fsw._handleError(error)) {
15856
15798
  ready();
15857
- return path28;
15799
+ return path30;
15858
15800
  }
15859
15801
  }
15860
15802
  }
@@ -15886,35 +15828,35 @@ function createPattern(matcher) {
15886
15828
  if (matcher.path === string)
15887
15829
  return true;
15888
15830
  if (matcher.recursive) {
15889
- const relative12 = sp2.relative(matcher.path, string);
15890
- if (!relative12) {
15831
+ const relative14 = sp2.relative(matcher.path, string);
15832
+ if (!relative14) {
15891
15833
  return false;
15892
15834
  }
15893
- return !relative12.startsWith("..") && !sp2.isAbsolute(relative12);
15835
+ return !relative14.startsWith("..") && !sp2.isAbsolute(relative14);
15894
15836
  }
15895
15837
  return false;
15896
15838
  };
15897
15839
  }
15898
15840
  return () => false;
15899
15841
  }
15900
- function normalizePath2(path28) {
15901
- if (typeof path28 !== "string")
15842
+ function normalizePath2(path30) {
15843
+ if (typeof path30 !== "string")
15902
15844
  throw new Error("string expected");
15903
- path28 = sp2.normalize(path28);
15904
- path28 = path28.replace(/\\/g, "/");
15845
+ path30 = sp2.normalize(path30);
15846
+ path30 = path30.replace(/\\/g, "/");
15905
15847
  let prepend = false;
15906
- if (path28.startsWith("//"))
15848
+ if (path30.startsWith("//"))
15907
15849
  prepend = true;
15908
- path28 = path28.replace(DOUBLE_SLASH_RE, "/");
15850
+ path30 = path30.replace(DOUBLE_SLASH_RE, "/");
15909
15851
  if (prepend)
15910
- path28 = "/" + path28;
15911
- return path28;
15852
+ path30 = "/" + path30;
15853
+ return path30;
15912
15854
  }
15913
15855
  function matchPatterns(patterns, testString, stats) {
15914
- const path28 = normalizePath2(testString);
15856
+ const path30 = normalizePath2(testString);
15915
15857
  for (let index = 0; index < patterns.length; index++) {
15916
15858
  const pattern = patterns[index];
15917
- if (pattern(path28, stats)) {
15859
+ if (pattern(path30, stats)) {
15918
15860
  return true;
15919
15861
  }
15920
15862
  }
@@ -15952,19 +15894,19 @@ var toUnix = (string) => {
15952
15894
  }
15953
15895
  return str;
15954
15896
  };
15955
- var normalizePathToUnix = (path28) => toUnix(sp2.normalize(toUnix(path28)));
15956
- var normalizeIgnored = (cwd = "") => (path28) => {
15957
- if (typeof path28 === "string") {
15958
- return normalizePathToUnix(sp2.isAbsolute(path28) ? path28 : sp2.join(cwd, path28));
15897
+ var normalizePathToUnix = (path30) => toUnix(sp2.normalize(toUnix(path30)));
15898
+ var normalizeIgnored = (cwd = "") => (path30) => {
15899
+ if (typeof path30 === "string") {
15900
+ return normalizePathToUnix(sp2.isAbsolute(path30) ? path30 : sp2.join(cwd, path30));
15959
15901
  } else {
15960
- return path28;
15902
+ return path30;
15961
15903
  }
15962
15904
  };
15963
- var getAbsolutePath = (path28, cwd) => {
15964
- if (sp2.isAbsolute(path28)) {
15965
- return path28;
15905
+ var getAbsolutePath = (path30, cwd) => {
15906
+ if (sp2.isAbsolute(path30)) {
15907
+ return path30;
15966
15908
  }
15967
- return sp2.join(cwd, path28);
15909
+ return sp2.join(cwd, path30);
15968
15910
  };
15969
15911
  var EMPTY_SET = Object.freeze(/* @__PURE__ */ new Set());
15970
15912
  var DirEntry = class {
@@ -16029,10 +15971,10 @@ var WatchHelper = class {
16029
15971
  dirParts;
16030
15972
  followSymlinks;
16031
15973
  statMethod;
16032
- constructor(path28, follow, fsw) {
15974
+ constructor(path30, follow, fsw) {
16033
15975
  this.fsw = fsw;
16034
- const watchPath = path28;
16035
- this.path = path28 = path28.replace(REPLACER_RE, "");
15976
+ const watchPath = path30;
15977
+ this.path = path30 = path30.replace(REPLACER_RE, "");
16036
15978
  this.watchPath = watchPath;
16037
15979
  this.fullWatchPath = sp2.resolve(watchPath);
16038
15980
  this.dirParts = [];
@@ -16172,20 +16114,20 @@ var FSWatcher = class extends EventEmitter2 {
16172
16114
  this._closePromise = void 0;
16173
16115
  let paths = unifyPaths(paths_);
16174
16116
  if (cwd) {
16175
- paths = paths.map((path28) => {
16176
- const absPath = getAbsolutePath(path28, cwd);
16117
+ paths = paths.map((path30) => {
16118
+ const absPath = getAbsolutePath(path30, cwd);
16177
16119
  return absPath;
16178
16120
  });
16179
16121
  }
16180
- paths.forEach((path28) => {
16181
- this._removeIgnoredPath(path28);
16122
+ paths.forEach((path30) => {
16123
+ this._removeIgnoredPath(path30);
16182
16124
  });
16183
16125
  this._userIgnored = void 0;
16184
16126
  if (!this._readyCount)
16185
16127
  this._readyCount = 0;
16186
16128
  this._readyCount += paths.length;
16187
- Promise.all(paths.map(async (path28) => {
16188
- const res = await this._nodeFsHandler._addToNodeFs(path28, !_internal, void 0, 0, _origAdd);
16129
+ Promise.all(paths.map(async (path30) => {
16130
+ const res = await this._nodeFsHandler._addToNodeFs(path30, !_internal, void 0, 0, _origAdd);
16189
16131
  if (res)
16190
16132
  this._emitReady();
16191
16133
  return res;
@@ -16207,17 +16149,17 @@ var FSWatcher = class extends EventEmitter2 {
16207
16149
  return this;
16208
16150
  const paths = unifyPaths(paths_);
16209
16151
  const { cwd } = this.options;
16210
- paths.forEach((path28) => {
16211
- if (!sp2.isAbsolute(path28) && !this._closers.has(path28)) {
16152
+ paths.forEach((path30) => {
16153
+ if (!sp2.isAbsolute(path30) && !this._closers.has(path30)) {
16212
16154
  if (cwd)
16213
- path28 = sp2.join(cwd, path28);
16214
- path28 = sp2.resolve(path28);
16155
+ path30 = sp2.join(cwd, path30);
16156
+ path30 = sp2.resolve(path30);
16215
16157
  }
16216
- this._closePath(path28);
16217
- this._addIgnoredPath(path28);
16218
- if (this._watched.has(path28)) {
16158
+ this._closePath(path30);
16159
+ this._addIgnoredPath(path30);
16160
+ if (this._watched.has(path30)) {
16219
16161
  this._addIgnoredPath({
16220
- path: path28,
16162
+ path: path30,
16221
16163
  recursive: true
16222
16164
  });
16223
16165
  }
@@ -16281,38 +16223,38 @@ var FSWatcher = class extends EventEmitter2 {
16281
16223
  * @param stats arguments to be passed with event
16282
16224
  * @returns the error if defined, otherwise the value of the FSWatcher instance's `closed` flag
16283
16225
  */
16284
- async _emit(event, path28, stats) {
16226
+ async _emit(event, path30, stats) {
16285
16227
  if (this.closed)
16286
16228
  return;
16287
16229
  const opts = this.options;
16288
16230
  if (isWindows)
16289
- path28 = sp2.normalize(path28);
16231
+ path30 = sp2.normalize(path30);
16290
16232
  if (opts.cwd)
16291
- path28 = sp2.relative(opts.cwd, path28);
16292
- const args = [path28];
16233
+ path30 = sp2.relative(opts.cwd, path30);
16234
+ const args = [path30];
16293
16235
  if (stats != null)
16294
16236
  args.push(stats);
16295
16237
  const awf = opts.awaitWriteFinish;
16296
16238
  let pw;
16297
- if (awf && (pw = this._pendingWrites.get(path28))) {
16239
+ if (awf && (pw = this._pendingWrites.get(path30))) {
16298
16240
  pw.lastChange = /* @__PURE__ */ new Date();
16299
16241
  return this;
16300
16242
  }
16301
16243
  if (opts.atomic) {
16302
16244
  if (event === EVENTS.UNLINK) {
16303
- this._pendingUnlinks.set(path28, [event, ...args]);
16245
+ this._pendingUnlinks.set(path30, [event, ...args]);
16304
16246
  setTimeout(() => {
16305
- this._pendingUnlinks.forEach((entry, path29) => {
16247
+ this._pendingUnlinks.forEach((entry, path31) => {
16306
16248
  this.emit(...entry);
16307
16249
  this.emit(EVENTS.ALL, ...entry);
16308
- this._pendingUnlinks.delete(path29);
16250
+ this._pendingUnlinks.delete(path31);
16309
16251
  });
16310
16252
  }, typeof opts.atomic === "number" ? opts.atomic : 100);
16311
16253
  return this;
16312
16254
  }
16313
- if (event === EVENTS.ADD && this._pendingUnlinks.has(path28)) {
16255
+ if (event === EVENTS.ADD && this._pendingUnlinks.has(path30)) {
16314
16256
  event = EVENTS.CHANGE;
16315
- this._pendingUnlinks.delete(path28);
16257
+ this._pendingUnlinks.delete(path30);
16316
16258
  }
16317
16259
  }
16318
16260
  if (awf && (event === EVENTS.ADD || event === EVENTS.CHANGE) && this._readyEmitted) {
@@ -16330,16 +16272,16 @@ var FSWatcher = class extends EventEmitter2 {
16330
16272
  this.emitWithAll(event, args);
16331
16273
  }
16332
16274
  };
16333
- this._awaitWriteFinish(path28, awf.stabilityThreshold, event, awfEmit);
16275
+ this._awaitWriteFinish(path30, awf.stabilityThreshold, event, awfEmit);
16334
16276
  return this;
16335
16277
  }
16336
16278
  if (event === EVENTS.CHANGE) {
16337
- const isThrottled = !this._throttle(EVENTS.CHANGE, path28, 50);
16279
+ const isThrottled = !this._throttle(EVENTS.CHANGE, path30, 50);
16338
16280
  if (isThrottled)
16339
16281
  return this;
16340
16282
  }
16341
16283
  if (opts.alwaysStat && stats === void 0 && (event === EVENTS.ADD || event === EVENTS.ADD_DIR || event === EVENTS.CHANGE)) {
16342
- const fullPath = opts.cwd ? sp2.join(opts.cwd, path28) : path28;
16284
+ const fullPath = opts.cwd ? sp2.join(opts.cwd, path30) : path30;
16343
16285
  let stats2;
16344
16286
  try {
16345
16287
  stats2 = await stat3(fullPath);
@@ -16370,23 +16312,23 @@ var FSWatcher = class extends EventEmitter2 {
16370
16312
  * @param timeout duration of time to suppress duplicate actions
16371
16313
  * @returns tracking object or false if action should be suppressed
16372
16314
  */
16373
- _throttle(actionType, path28, timeout) {
16315
+ _throttle(actionType, path30, timeout) {
16374
16316
  if (!this._throttled.has(actionType)) {
16375
16317
  this._throttled.set(actionType, /* @__PURE__ */ new Map());
16376
16318
  }
16377
16319
  const action = this._throttled.get(actionType);
16378
16320
  if (!action)
16379
16321
  throw new Error("invalid throttle");
16380
- const actionPath = action.get(path28);
16322
+ const actionPath = action.get(path30);
16381
16323
  if (actionPath) {
16382
16324
  actionPath.count++;
16383
16325
  return false;
16384
16326
  }
16385
16327
  let timeoutObject;
16386
16328
  const clear = () => {
16387
- const item = action.get(path28);
16329
+ const item = action.get(path30);
16388
16330
  const count = item ? item.count : 0;
16389
- action.delete(path28);
16331
+ action.delete(path30);
16390
16332
  clearTimeout(timeoutObject);
16391
16333
  if (item)
16392
16334
  clearTimeout(item.timeoutObject);
@@ -16394,7 +16336,7 @@ var FSWatcher = class extends EventEmitter2 {
16394
16336
  };
16395
16337
  timeoutObject = setTimeout(clear, timeout);
16396
16338
  const thr = { timeoutObject, clear, count: 0 };
16397
- action.set(path28, thr);
16339
+ action.set(path30, thr);
16398
16340
  return thr;
16399
16341
  }
16400
16342
  _incrReadyCount() {
@@ -16408,44 +16350,44 @@ var FSWatcher = class extends EventEmitter2 {
16408
16350
  * @param event
16409
16351
  * @param awfEmit Callback to be called when ready for event to be emitted.
16410
16352
  */
16411
- _awaitWriteFinish(path28, threshold, event, awfEmit) {
16353
+ _awaitWriteFinish(path30, threshold, event, awfEmit) {
16412
16354
  const awf = this.options.awaitWriteFinish;
16413
16355
  if (typeof awf !== "object")
16414
16356
  return;
16415
16357
  const pollInterval = awf.pollInterval;
16416
16358
  let timeoutHandler;
16417
- let fullPath = path28;
16418
- if (this.options.cwd && !sp2.isAbsolute(path28)) {
16419
- fullPath = sp2.join(this.options.cwd, path28);
16359
+ let fullPath = path30;
16360
+ if (this.options.cwd && !sp2.isAbsolute(path30)) {
16361
+ fullPath = sp2.join(this.options.cwd, path30);
16420
16362
  }
16421
16363
  const now2 = /* @__PURE__ */ new Date();
16422
16364
  const writes = this._pendingWrites;
16423
16365
  function awaitWriteFinishFn(prevStat) {
16424
16366
  statcb(fullPath, (err, curStat) => {
16425
- if (err || !writes.has(path28)) {
16367
+ if (err || !writes.has(path30)) {
16426
16368
  if (err && err.code !== "ENOENT")
16427
16369
  awfEmit(err);
16428
16370
  return;
16429
16371
  }
16430
16372
  const now3 = Number(/* @__PURE__ */ new Date());
16431
16373
  if (prevStat && curStat.size !== prevStat.size) {
16432
- writes.get(path28).lastChange = now3;
16374
+ writes.get(path30).lastChange = now3;
16433
16375
  }
16434
- const pw = writes.get(path28);
16376
+ const pw = writes.get(path30);
16435
16377
  const df = now3 - pw.lastChange;
16436
16378
  if (df >= threshold) {
16437
- writes.delete(path28);
16379
+ writes.delete(path30);
16438
16380
  awfEmit(void 0, curStat);
16439
16381
  } else {
16440
16382
  timeoutHandler = setTimeout(awaitWriteFinishFn, pollInterval, curStat);
16441
16383
  }
16442
16384
  });
16443
16385
  }
16444
- if (!writes.has(path28)) {
16445
- writes.set(path28, {
16386
+ if (!writes.has(path30)) {
16387
+ writes.set(path30, {
16446
16388
  lastChange: now2,
16447
16389
  cancelWait: () => {
16448
- writes.delete(path28);
16390
+ writes.delete(path30);
16449
16391
  clearTimeout(timeoutHandler);
16450
16392
  return event;
16451
16393
  }
@@ -16456,8 +16398,8 @@ var FSWatcher = class extends EventEmitter2 {
16456
16398
  /**
16457
16399
  * Determines whether user has asked to ignore this path.
16458
16400
  */
16459
- _isIgnored(path28, stats) {
16460
- if (this.options.atomic && DOT_RE.test(path28))
16401
+ _isIgnored(path30, stats) {
16402
+ if (this.options.atomic && DOT_RE.test(path30))
16461
16403
  return true;
16462
16404
  if (!this._userIgnored) {
16463
16405
  const { cwd } = this.options;
@@ -16467,17 +16409,17 @@ var FSWatcher = class extends EventEmitter2 {
16467
16409
  const list = [...ignoredPaths.map(normalizeIgnored(cwd)), ...ignored];
16468
16410
  this._userIgnored = anymatch(list, void 0);
16469
16411
  }
16470
- return this._userIgnored(path28, stats);
16412
+ return this._userIgnored(path30, stats);
16471
16413
  }
16472
- _isntIgnored(path28, stat4) {
16473
- return !this._isIgnored(path28, stat4);
16414
+ _isntIgnored(path30, stat5) {
16415
+ return !this._isIgnored(path30, stat5);
16474
16416
  }
16475
16417
  /**
16476
16418
  * Provides a set of common helpers and properties relating to symlink handling.
16477
16419
  * @param path file or directory pattern being watched
16478
16420
  */
16479
- _getWatchHelpers(path28) {
16480
- return new WatchHelper(path28, this.options.followSymlinks, this);
16421
+ _getWatchHelpers(path30) {
16422
+ return new WatchHelper(path30, this.options.followSymlinks, this);
16481
16423
  }
16482
16424
  // Directory helpers
16483
16425
  // -----------------
@@ -16509,63 +16451,63 @@ var FSWatcher = class extends EventEmitter2 {
16509
16451
  * @param item base path of item/directory
16510
16452
  */
16511
16453
  _remove(directory, item, isDirectory) {
16512
- const path28 = sp2.join(directory, item);
16513
- const fullPath = sp2.resolve(path28);
16514
- isDirectory = isDirectory != null ? isDirectory : this._watched.has(path28) || this._watched.has(fullPath);
16515
- if (!this._throttle("remove", path28, 100))
16454
+ const path30 = sp2.join(directory, item);
16455
+ const fullPath = sp2.resolve(path30);
16456
+ isDirectory = isDirectory != null ? isDirectory : this._watched.has(path30) || this._watched.has(fullPath);
16457
+ if (!this._throttle("remove", path30, 100))
16516
16458
  return;
16517
16459
  if (!isDirectory && this._watched.size === 1) {
16518
16460
  this.add(directory, item, true);
16519
16461
  }
16520
- const wp = this._getWatchedDir(path28);
16462
+ const wp = this._getWatchedDir(path30);
16521
16463
  const nestedDirectoryChildren = wp.getChildren();
16522
- nestedDirectoryChildren.forEach((nested) => this._remove(path28, nested));
16464
+ nestedDirectoryChildren.forEach((nested) => this._remove(path30, nested));
16523
16465
  const parent = this._getWatchedDir(directory);
16524
16466
  const wasTracked = parent.has(item);
16525
16467
  parent.remove(item);
16526
16468
  if (this._symlinkPaths.has(fullPath)) {
16527
16469
  this._symlinkPaths.delete(fullPath);
16528
16470
  }
16529
- let relPath = path28;
16471
+ let relPath = path30;
16530
16472
  if (this.options.cwd)
16531
- relPath = sp2.relative(this.options.cwd, path28);
16473
+ relPath = sp2.relative(this.options.cwd, path30);
16532
16474
  if (this.options.awaitWriteFinish && this._pendingWrites.has(relPath)) {
16533
16475
  const event = this._pendingWrites.get(relPath).cancelWait();
16534
16476
  if (event === EVENTS.ADD)
16535
16477
  return;
16536
16478
  }
16537
- this._watched.delete(path28);
16479
+ this._watched.delete(path30);
16538
16480
  this._watched.delete(fullPath);
16539
16481
  const eventName = isDirectory ? EVENTS.UNLINK_DIR : EVENTS.UNLINK;
16540
- if (wasTracked && !this._isIgnored(path28))
16541
- this._emit(eventName, path28);
16542
- this._closePath(path28);
16482
+ if (wasTracked && !this._isIgnored(path30))
16483
+ this._emit(eventName, path30);
16484
+ this._closePath(path30);
16543
16485
  }
16544
16486
  /**
16545
16487
  * Closes all watchers for a path
16546
16488
  */
16547
- _closePath(path28) {
16548
- this._closeFile(path28);
16549
- const dir = sp2.dirname(path28);
16550
- this._getWatchedDir(dir).remove(sp2.basename(path28));
16489
+ _closePath(path30) {
16490
+ this._closeFile(path30);
16491
+ const dir = sp2.dirname(path30);
16492
+ this._getWatchedDir(dir).remove(sp2.basename(path30));
16551
16493
  }
16552
16494
  /**
16553
16495
  * Closes only file-specific watchers
16554
16496
  */
16555
- _closeFile(path28) {
16556
- const closers = this._closers.get(path28);
16497
+ _closeFile(path30) {
16498
+ const closers = this._closers.get(path30);
16557
16499
  if (!closers)
16558
16500
  return;
16559
16501
  closers.forEach((closer) => closer());
16560
- this._closers.delete(path28);
16502
+ this._closers.delete(path30);
16561
16503
  }
16562
- _addPathCloser(path28, closer) {
16504
+ _addPathCloser(path30, closer) {
16563
16505
  if (!closer)
16564
16506
  return;
16565
- let list = this._closers.get(path28);
16507
+ let list = this._closers.get(path30);
16566
16508
  if (!list) {
16567
16509
  list = [];
16568
- this._closers.set(path28, list);
16510
+ this._closers.set(path30, list);
16569
16511
  }
16570
16512
  list.push(closer);
16571
16513
  }
@@ -16595,12 +16537,291 @@ function watch(paths, options = {}) {
16595
16537
  var chokidar_default = { watch, FSWatcher };
16596
16538
 
16597
16539
  // src/watcher/file-watcher.ts
16540
+ import * as path23 from "path";
16541
+
16542
+ // src/watcher/native-recursive-watcher.ts
16543
+ import { watch as watch2 } from "fs";
16598
16544
  import * as path21 from "path";
16545
+ var NativeRecursiveWatcher = class {
16546
+ constructor(root, onChange, options = {}) {
16547
+ this.root = root;
16548
+ this.onChange = onChange;
16549
+ this.watchFactory = options.watchFactory ?? this.defaultWatchFactory;
16550
+ this.onError = options.onError;
16551
+ }
16552
+ root;
16553
+ onChange;
16554
+ watcher = null;
16555
+ listenerToken = 0;
16556
+ watchFactory;
16557
+ onError;
16558
+ start() {
16559
+ if (this.watcher) return;
16560
+ const token = ++this.listenerToken;
16561
+ const listener = (_eventType, filename) => {
16562
+ if (this.watcher === null || this.listenerToken !== token) return;
16563
+ const absolutePath = this.toAbsolutePath(filename);
16564
+ const nextResult = this.onChange(absolutePath);
16565
+ if (nextResult instanceof Promise) {
16566
+ void nextResult.catch((error) => {
16567
+ console.error("[codebase-index] Error handling native watcher event:", error);
16568
+ });
16569
+ }
16570
+ };
16571
+ const watcher = this.watchFactory(this.root, listener, {
16572
+ persistent: true,
16573
+ recursive: true
16574
+ });
16575
+ watcher.on?.("error", (error) => {
16576
+ if (this.watcher === watcher && this.listenerToken === token) {
16577
+ this.onError?.(error);
16578
+ }
16579
+ });
16580
+ this.watcher = watcher;
16581
+ }
16582
+ async stop() {
16583
+ const watcher = this.watcher;
16584
+ this.watcher = null;
16585
+ this.listenerToken += 1;
16586
+ if (!watcher) return;
16587
+ await watcher.close();
16588
+ }
16589
+ toAbsolutePath(filename) {
16590
+ if (filename == null) return null;
16591
+ const normalizedFilename = typeof filename === "string" ? filename : filename.toString();
16592
+ const absolutePath = path21.resolve(this.root, normalizedFilename);
16593
+ const relativePath = path21.relative(this.root, absolutePath);
16594
+ const outsideRoot = relativePath === ".." || relativePath.startsWith(`..${path21.sep}`) || path21.isAbsolute(relativePath);
16595
+ return outsideRoot ? null : absolutePath;
16596
+ }
16597
+ defaultWatchFactory = (root, listener, options) => watch2(root, options, listener);
16598
+ };
16599
+
16600
+ // src/watcher/snapshot.ts
16601
+ import * as fsPromises4 from "fs/promises";
16602
+ import * as path22 from "path";
16603
+ async function buildFileSnapshotScan(projectRoot, config, configPaths = []) {
16604
+ const normalizedProjectRoot = path22.resolve(projectRoot);
16605
+ const ignoreFilter = createIgnoreFilter(normalizedProjectRoot);
16606
+ const includePatterns = [...config.include, ...config.additionalInclude ?? []];
16607
+ const maxDepth = config.indexing?.maxDepth ?? -1;
16608
+ const snapshot = /* @__PURE__ */ new Map();
16609
+ const unreadablePrefixes = /* @__PURE__ */ new Set();
16610
+ const includeFile = async (filePath) => {
16611
+ const normalizedPath2 = path22.resolve(filePath);
16612
+ if (!shouldIncludeFile(normalizedPath2, normalizedProjectRoot, includePatterns, config.exclude, ignoreFilter)) return;
16613
+ const stat5 = await readStatIfFile(normalizedPath2, unreadablePrefixes);
16614
+ if (stat5) snapshot.set(normalizedPath2, { size: stat5.size, mtimeMs: stat5.mtimeMs });
16615
+ };
16616
+ const walk = async (directoryPath, depth) => {
16617
+ let entries;
16618
+ try {
16619
+ entries = await fsPromises4.readdir(directoryPath, { withFileTypes: true });
16620
+ } catch (error) {
16621
+ if (isMissingFsError(error)) return;
16622
+ if (isPermissionFsError(error)) {
16623
+ unreadablePrefixes.add(path22.resolve(directoryPath));
16624
+ return;
16625
+ }
16626
+ throw error;
16627
+ }
16628
+ for (const entry of entries) {
16629
+ const fullPath = path22.join(directoryPath, entry.name);
16630
+ const relativePath = path22.relative(normalizedProjectRoot, fullPath);
16631
+ if (entry.isDirectory()) {
16632
+ if (hasFilteredPathSegment(relativePath, path22.sep) || isRestrictedDirectory(relativePath, path22.sep)) continue;
16633
+ if (ignoreFilter.ignores(relativePath)) continue;
16634
+ if (maxDepth === -1 || depth < maxDepth) await walk(fullPath, depth + 1);
16635
+ } else if (entry.isFile()) {
16636
+ await includeFile(fullPath);
16637
+ }
16638
+ }
16639
+ };
16640
+ await walk(normalizedProjectRoot, 0);
16641
+ await includeExplicitConfigPaths(snapshot, unreadablePrefixes, configPaths);
16642
+ return { entries: snapshot, unreadablePrefixes };
16643
+ }
16644
+ async function buildFileSnapshotForPathScan(projectRoot, config, configPaths, targetPath) {
16645
+ const normalizedProjectRoot = path22.resolve(projectRoot);
16646
+ const normalizedTargetPath = path22.resolve(targetPath);
16647
+ if (!isWithinPath(normalizedProjectRoot, normalizedTargetPath)) {
16648
+ return { entries: /* @__PURE__ */ new Map(), unreadablePrefixes: /* @__PURE__ */ new Set() };
16649
+ }
16650
+ const ignoreFilter = createIgnoreFilter(normalizedProjectRoot);
16651
+ const includePatterns = [...config.include, ...config.additionalInclude ?? []];
16652
+ const maxDepth = config.indexing?.maxDepth ?? -1;
16653
+ const explicitConfigPaths = new Set(configPaths.map((configPath) => path22.resolve(configPath)));
16654
+ const snapshot = /* @__PURE__ */ new Map();
16655
+ const unreadablePrefixes = /* @__PURE__ */ new Set();
16656
+ const includeFile = async (filePath) => {
16657
+ const normalizedPath2 = path22.resolve(filePath);
16658
+ if (!explicitConfigPaths.has(normalizedPath2) && !shouldIncludeFile(
16659
+ normalizedPath2,
16660
+ normalizedProjectRoot,
16661
+ includePatterns,
16662
+ config.exclude,
16663
+ ignoreFilter
16664
+ )) return;
16665
+ const stat5 = await readStatIfFile(normalizedPath2, unreadablePrefixes);
16666
+ if (stat5) snapshot.set(normalizedPath2, { size: stat5.size, mtimeMs: stat5.mtimeMs });
16667
+ };
16668
+ const walk = async (directoryPath, depth) => {
16669
+ let entries;
16670
+ try {
16671
+ entries = await fsPromises4.readdir(directoryPath, { withFileTypes: true });
16672
+ } catch (error) {
16673
+ if (isMissingFsError(error)) return;
16674
+ if (isPermissionFsError(error)) {
16675
+ unreadablePrefixes.add(path22.resolve(directoryPath));
16676
+ return;
16677
+ }
16678
+ throw error;
16679
+ }
16680
+ for (const entry of entries) {
16681
+ const fullPath = path22.join(directoryPath, entry.name);
16682
+ const relativePath = path22.relative(normalizedProjectRoot, fullPath);
16683
+ if (entry.isDirectory()) {
16684
+ if (hasFilteredPathSegment(relativePath, path22.sep) || isRestrictedDirectory(relativePath, path22.sep)) continue;
16685
+ if (ignoreFilter.ignores(relativePath)) continue;
16686
+ if (maxDepth === -1 || depth < maxDepth) await walk(fullPath, depth + 1);
16687
+ } else if (entry.isFile()) {
16688
+ await includeFile(fullPath);
16689
+ }
16690
+ }
16691
+ };
16692
+ const targetStat = await readStatIfFile(normalizedTargetPath, unreadablePrefixes);
16693
+ if (targetStat) await includeFile(normalizedTargetPath);
16694
+ else await walk(normalizedTargetPath, 0);
16695
+ await includeExplicitConfigPathsInPath(snapshot, unreadablePrefixes, configPaths, normalizedTargetPath);
16696
+ return { entries: snapshot, unreadablePrefixes };
16697
+ }
16698
+ function completeFileSnapshot(previous, scan) {
16699
+ const completed = new Map(scan.entries);
16700
+ for (const unreadablePrefix of scan.unreadablePrefixes) {
16701
+ for (const [entryPath, entry] of previous) {
16702
+ if (isWithinPath(unreadablePrefix, entryPath) && !completed.has(entryPath)) completed.set(entryPath, entry);
16703
+ }
16704
+ }
16705
+ return completed;
16706
+ }
16707
+ async function includeExplicitConfigPaths(snapshot, unreadablePrefixes, configPaths) {
16708
+ for (const configPath of [...new Set(configPaths.map((value) => path22.resolve(value)))]) {
16709
+ if (snapshot.has(configPath)) continue;
16710
+ const stat5 = await readStatIfFile(configPath, unreadablePrefixes);
16711
+ if (stat5) snapshot.set(configPath, { size: stat5.size, mtimeMs: stat5.mtimeMs });
16712
+ }
16713
+ }
16714
+ async function includeExplicitConfigPathsInPath(snapshot, unreadablePrefixes, configPaths, targetPath) {
16715
+ await includeExplicitConfigPaths(
16716
+ snapshot,
16717
+ unreadablePrefixes,
16718
+ configPaths.filter((configPath) => isWithinPath(targetPath, path22.resolve(configPath)))
16719
+ );
16720
+ }
16721
+ function isWithinPath(parentPath, childPath) {
16722
+ const relativePath = path22.relative(parentPath, childPath);
16723
+ return relativePath === "" || !relativePath.startsWith(`..${path22.sep}`) && relativePath !== ".." && !path22.isAbsolute(relativePath);
16724
+ }
16725
+ async function readStatIfFile(filePath, unreadablePrefixes) {
16726
+ try {
16727
+ const stat5 = await fsPromises4.stat(filePath);
16728
+ return stat5.isFile() ? stat5 : null;
16729
+ } catch (error) {
16730
+ if (isMissingFsError(error)) return null;
16731
+ if (isPermissionFsError(error)) {
16732
+ unreadablePrefixes.add(path22.resolve(filePath));
16733
+ return null;
16734
+ }
16735
+ throw error;
16736
+ }
16737
+ }
16738
+ function isMissingFsError(error) {
16739
+ return error instanceof Error && ["ENOENT", "ENOTDIR"].includes(error.code ?? "");
16740
+ }
16741
+ function isPermissionFsError(error) {
16742
+ return error instanceof Error && ["EACCES", "EPERM"].includes(error.code ?? "");
16743
+ }
16744
+ var diffTypeOrder = { add: 0, change: 1, unlink: 2 };
16745
+ function diffFileSnapshots(previous, current, forcedChanges = /* @__PURE__ */ new Set()) {
16746
+ const changes = [];
16747
+ for (const [filePath, previousEntry] of previous) {
16748
+ const currentEntry = current.get(filePath);
16749
+ if (!currentEntry) changes.push({ type: "unlink", path: filePath });
16750
+ else if (forcedChanges.has(filePath) || currentEntry.size !== previousEntry.size || currentEntry.mtimeMs !== previousEntry.mtimeMs) {
16751
+ changes.push({ type: "change", path: filePath });
16752
+ }
16753
+ }
16754
+ for (const [filePath] of current) {
16755
+ if (!previous.has(filePath)) changes.push({ type: "add", path: filePath });
16756
+ }
16757
+ return changes.sort((left, right) => left.path.localeCompare(right.path) || diffTypeOrder[left.type] - diffTypeOrder[right.type]);
16758
+ }
16759
+
16760
+ // src/watcher/snapshot-reconciler.ts
16761
+ var FileSnapshotReconciler = class {
16762
+ constructor(projectRoot, config, configPaths) {
16763
+ this.projectRoot = projectRoot;
16764
+ this.config = config;
16765
+ this.configPaths = configPaths;
16766
+ }
16767
+ projectRoot;
16768
+ config;
16769
+ configPaths;
16770
+ snapshot = null;
16771
+ reconciliationTail = Promise.resolve();
16772
+ async initialize() {
16773
+ this.snapshot = (await buildFileSnapshotScan(this.projectRoot, this.config, this.configPaths)).entries;
16774
+ }
16775
+ async reconcile(invalidations = []) {
16776
+ if (this.snapshot === null) {
16777
+ throw new Error("FileSnapshotReconciler is not initialized. Call initialize() before reconcile().");
16778
+ }
16779
+ const reconciliation = this.reconciliationTail.then(async () => {
16780
+ const previousSnapshot = this.snapshot;
16781
+ if (previousSnapshot === null) {
16782
+ throw new Error("FileSnapshotReconciler is not initialized. Call initialize() before reconcile().");
16783
+ }
16784
+ const normalizedInvalidations = invalidations.map((invalidation) => typeof invalidation === "string" || invalidation === null ? { path: invalidation, forceChange: false } : { path: invalidation.path, forceChange: invalidation.forceChange === true });
16785
+ const scopedPaths = normalizedInvalidations.map((invalidation) => invalidation.path).filter((filePath) => filePath !== null);
16786
+ const scan = scopedPaths.length === 0 || scopedPaths.length !== normalizedInvalidations.length ? await buildFileSnapshotScan(this.projectRoot, this.config, this.configPaths) : await this.reconcilePaths(previousSnapshot, scopedPaths);
16787
+ const nextSnapshot = completeFileSnapshot(previousSnapshot, scan);
16788
+ const forcedChanges = new Set(normalizedInvalidations.filter((invalidation) => invalidation.path !== null && invalidation.forceChange).map((invalidation) => invalidation.path));
16789
+ const changes = diffFileSnapshots(previousSnapshot, nextSnapshot, forcedChanges);
16790
+ this.snapshot = nextSnapshot;
16791
+ return changes;
16792
+ });
16793
+ this.reconciliationTail = reconciliation.then(() => void 0, () => void 0);
16794
+ return reconciliation;
16795
+ }
16796
+ async reconcilePaths(previousSnapshot, invalidatedPaths) {
16797
+ const scopes = this.getScopes(invalidatedPaths);
16798
+ const entries = new Map(previousSnapshot);
16799
+ const unreadablePrefixes = /* @__PURE__ */ new Set();
16800
+ for (const scope of scopes) {
16801
+ for (const previousPath of entries.keys()) {
16802
+ if (isWithinPath(scope, previousPath)) entries.delete(previousPath);
16803
+ }
16804
+ const scopedScan = await buildFileSnapshotForPathScan(this.projectRoot, this.config, this.configPaths, scope);
16805
+ for (const [filePath, entry] of scopedScan.entries) entries.set(filePath, entry);
16806
+ for (const unreadablePrefix of scopedScan.unreadablePrefixes) unreadablePrefixes.add(unreadablePrefix);
16807
+ }
16808
+ return { entries, unreadablePrefixes };
16809
+ }
16810
+ getScopes(invalidatedPaths) {
16811
+ const uniquePaths = [...new Set(invalidatedPaths)].sort((left, right) => left.length - right.length);
16812
+ return uniquePaths.filter((candidate, index) => !uniquePaths.slice(0, index).some(
16813
+ (ancestor) => isWithinPath(ancestor, candidate)
16814
+ ));
16815
+ }
16816
+ };
16817
+
16818
+ // src/watcher/file-watcher.ts
16599
16819
  var FileWatcher = class {
16600
16820
  watcher = null;
16601
16821
  projectRoot;
16602
16822
  config;
16603
16823
  configPath;
16824
+ backend;
16604
16825
  projectConfigPaths;
16605
16826
  pendingChanges = /* @__PURE__ */ new Map();
16606
16827
  debounceTimer = null;
@@ -16610,44 +16831,74 @@ var FileWatcher = class {
16610
16831
  resolveReady = null;
16611
16832
  pollingFallbackAttempted = false;
16612
16833
  pendingClose = null;
16834
+ startupReadySignals = 1;
16835
+ nativeWatcher = null;
16836
+ nativeReconciler = null;
16837
+ nativeSetupGeneration = 0;
16838
+ nativeStarting = false;
16839
+ nativeInitializing = false;
16840
+ nativeReconcileTimer = null;
16841
+ nativeInvalidatedPaths = /* @__PURE__ */ new Map();
16842
+ configPathStates = /* @__PURE__ */ new Map();
16613
16843
  constructor(projectRoot, config, host, options = {}) {
16614
16844
  this.projectRoot = projectRoot;
16615
16845
  this.config = config;
16846
+ this.backend = options.backend ?? "auto";
16616
16847
  this.configPath = options.configPath;
16617
16848
  this.projectConfigPaths = options.configPath ? [options.configPath] : getProjectConfigCandidatePaths(projectRoot, host);
16618
16849
  }
16619
16850
  start(handler) {
16620
- if (this.watcher) {
16851
+ if (this.watcher || this.nativeWatcher || this.nativeStarting) {
16621
16852
  return;
16622
16853
  }
16623
16854
  this.onChanges = handler;
16624
16855
  this.pollingFallbackAttempted = false;
16625
16856
  this.resetReady();
16857
+ if (this.shouldUseNativeWatcher()) {
16858
+ if (this.hasExternalConfigWatchTarget()) {
16859
+ this.setStartupReadySignals(2);
16860
+ this.startExternalConfigWatcher();
16861
+ }
16862
+ this.nativeStarting = true;
16863
+ void this.createNativeWatcher();
16864
+ return;
16865
+ }
16626
16866
  this.createWatcher();
16627
16867
  }
16628
16868
  resetReady() {
16629
- this.readyPromise = new Promise((resolve15) => {
16630
- this.resolveReady = resolve15;
16869
+ this.readyPromise = new Promise((resolve17) => {
16870
+ this.resolveReady = resolve17;
16631
16871
  });
16872
+ this.startupReadySignals = 1;
16632
16873
  }
16633
- createWatcher(usePolling = false) {
16634
- const ignoreFilter = createIgnoreFilter(this.projectRoot);
16635
- let watchTargets = this.projectRoot;
16636
- if (this.configPath) {
16637
- watchTargets = [this.projectRoot, this.configPath];
16638
- } else {
16639
- const externalConfigTargets = this.projectConfigPaths.filter((projectConfigPath) => {
16640
- const relativeConfigPath = path21.relative(this.projectRoot, projectConfigPath);
16641
- return this.isOutsideProjectPath(relativeConfigPath);
16642
- }).map((projectConfigPath) => existsSync13(projectConfigPath) ? projectConfigPath : this.getNearestExistingDirectory(path21.dirname(projectConfigPath)));
16643
- const uniqueExternalConfigTargets = [...new Set(externalConfigTargets)];
16644
- if (uniqueExternalConfigTargets.length > 0) {
16645
- watchTargets = [this.projectRoot, ...uniqueExternalConfigTargets];
16646
- }
16874
+ setStartupReadySignals(expectedSignals) {
16875
+ if (!this.readyPromise) {
16876
+ return;
16877
+ }
16878
+ this.startupReadySignals = Math.max(0, expectedSignals);
16879
+ }
16880
+ reportStartupReadySignal() {
16881
+ if (!this.readyPromise || !this.resolveReady) {
16882
+ return;
16647
16883
  }
16884
+ if (this.startupReadySignals <= 0) {
16885
+ return;
16886
+ }
16887
+ this.startupReadySignals -= 1;
16888
+ if (this.startupReadySignals !== 0) {
16889
+ return;
16890
+ }
16891
+ this.resolveReady();
16892
+ this.resolveReady = null;
16893
+ }
16894
+ createWatcher(watchTargets, usePolling = false, reportsStartupReady = true) {
16895
+ let reportedStartupReady = false;
16896
+ this.configPathStates = this.getConfigPathStates();
16897
+ const ignoreFilter = createIgnoreFilter(this.projectRoot);
16898
+ const resolvedWatchTargets = watchTargets ?? this.getFullChokidarWatchTargets();
16648
16899
  const watcherOptions = {
16649
16900
  ignored: (filePath) => {
16650
- const relativePath = path21.relative(this.projectRoot, filePath);
16901
+ const relativePath = path23.relative(this.projectRoot, filePath);
16651
16902
  if (!relativePath) return false;
16652
16903
  if (this.isProjectConfigPathOrAncestor(relativePath)) {
16653
16904
  return false;
@@ -16655,10 +16906,10 @@ var FileWatcher = class {
16655
16906
  if (this.isOutsideProjectPath(relativePath)) {
16656
16907
  return true;
16657
16908
  }
16658
- if (hasFilteredPathSegment(relativePath, path21.sep)) {
16909
+ if (hasFilteredPathSegment(relativePath, path23.sep)) {
16659
16910
  return true;
16660
16911
  }
16661
- if (isRestrictedDirectory(relativePath, path21.sep)) {
16912
+ if (isRestrictedDirectory(relativePath, path23.sep)) {
16662
16913
  return true;
16663
16914
  }
16664
16915
  if (ignoreFilter.ignores(relativePath)) {
@@ -16691,10 +16942,13 @@ var FileWatcher = class {
16691
16942
  watcher = new FSWatcher(watcherOptions);
16692
16943
  }
16693
16944
  this.watcher = watcher;
16694
- watcher.once("ready", () => {
16945
+ watcher.on("ready", () => {
16695
16946
  if (this.watcher !== watcher) return;
16696
- this.resolveReady?.();
16697
- this.resolveReady = null;
16947
+ this.reconcileConfigPathStates();
16948
+ if (reportsStartupReady) {
16949
+ this.reportStartupReadySignal();
16950
+ reportedStartupReady = true;
16951
+ }
16698
16952
  });
16699
16953
  watcher.on("error", (error) => {
16700
16954
  const err = error instanceof Error ? error : null;
@@ -16708,10 +16962,13 @@ var FileWatcher = class {
16708
16962
  console.error("[codebase-index] Failed to close exhausted file watcher:", closeError);
16709
16963
  });
16710
16964
  if (this.onChanges) {
16965
+ const replacementReportsStartupReady = reportsStartupReady || reportedStartupReady;
16711
16966
  if (!this.resolveReady) {
16712
16967
  this.resetReady();
16968
+ } else if (reportedStartupReady) {
16969
+ this.startupReadySignals += 1;
16713
16970
  }
16714
- this.createWatcher(true);
16971
+ this.createWatcher(resolvedWatchTargets, true, replacementReportsStartupReady);
16715
16972
  } else {
16716
16973
  this.watcher = null;
16717
16974
  }
@@ -16722,13 +16979,166 @@ var FileWatcher = class {
16722
16979
  watcher.on("add", (filePath) => this.handleChange(watcher, "add", filePath));
16723
16980
  watcher.on("change", (filePath) => this.handleChange(watcher, "change", filePath));
16724
16981
  watcher.on("unlink", (filePath) => this.handleChange(watcher, "unlink", filePath));
16725
- watcher.add(watchTargets);
16982
+ watcher.add(resolvedWatchTargets);
16983
+ }
16984
+ shouldUseNativeWatcher() {
16985
+ if (this.backend === "chokidar") {
16986
+ return false;
16987
+ }
16988
+ return true;
16989
+ }
16990
+ getFullChokidarWatchTargets() {
16991
+ if (this.configPath) {
16992
+ return [this.projectRoot, this.configPath];
16993
+ }
16994
+ const externalConfigTargets = this.getExternalConfigWatchTargets();
16995
+ if (externalConfigTargets.length === 0) {
16996
+ return this.projectRoot;
16997
+ }
16998
+ return [this.projectRoot, ...externalConfigTargets];
16999
+ }
17000
+ getExternalConfigWatchTargets() {
17001
+ return [...new Set(
17002
+ this.projectConfigPaths.filter((projectConfigPath) => {
17003
+ const relativeConfigPath = path23.relative(this.projectRoot, projectConfigPath);
17004
+ return this.isOutsideProjectPath(relativeConfigPath);
17005
+ }).map((projectConfigPath) => {
17006
+ if (existsSync13(projectConfigPath)) {
17007
+ return projectConfigPath;
17008
+ }
17009
+ return this.getNearestExistingDirectory(path23.dirname(projectConfigPath));
17010
+ })
17011
+ )];
17012
+ }
17013
+ hasExternalConfigWatchTarget() {
17014
+ return this.getExternalConfigWatchTargets().length > 0;
17015
+ }
17016
+ startExternalConfigWatcher(usePolling = false) {
17017
+ const externalTargets = this.getExternalConfigWatchTargets();
17018
+ if (externalTargets.length === 0) {
17019
+ return;
17020
+ }
17021
+ this.createWatcher(externalTargets, usePolling);
17022
+ }
17023
+ async createNativeWatcher() {
17024
+ const generation = ++this.nativeSetupGeneration;
17025
+ const reconciler = new FileSnapshotReconciler(this.projectRoot, this.config, this.projectConfigPaths);
17026
+ const watcher = new NativeRecursiveWatcher(
17027
+ this.projectRoot,
17028
+ (filePath) => this.scheduleNativeReconciliation(generation, filePath),
17029
+ { onError: (error) => void this.fallbackFromNativeWatcher(generation, error) }
17030
+ );
17031
+ this.nativeReconciler = reconciler;
17032
+ this.nativeWatcher = watcher;
17033
+ this.nativeInitializing = true;
17034
+ try {
17035
+ watcher.start();
17036
+ if (!this.isCurrentNativeSetup(generation)) {
17037
+ await watcher.stop();
17038
+ return;
17039
+ }
17040
+ await reconciler.initialize();
17041
+ if (!this.isCurrentNativeSetup(generation) || this.nativeWatcher !== watcher) {
17042
+ await watcher.stop();
17043
+ return;
17044
+ }
17045
+ this.nativeStarting = false;
17046
+ this.nativeInitializing = false;
17047
+ await this.reconcileNativeWatcherWithPendingInvalidations(generation);
17048
+ this.reportStartupReadySignal();
17049
+ } catch (error) {
17050
+ if (!this.isCurrentNativeSetup(generation)) return;
17051
+ this.nativeInitializing = false;
17052
+ if (this.nativeWatcher) {
17053
+ await this.fallbackFromNativeWatcher(generation, error);
17054
+ return;
17055
+ }
17056
+ this.nativeStarting = false;
17057
+ const externalWatcher = this.watcher;
17058
+ this.watcher = null;
17059
+ this.nativeReconciler = null;
17060
+ await externalWatcher?.close();
17061
+ console.warn("[codebase-index] Native recursive watcher unavailable; using Chokidar fallback.", error);
17062
+ this.setStartupReadySignals(1);
17063
+ this.createWatcher();
17064
+ }
17065
+ }
17066
+ isCurrentNativeSetup(generation) {
17067
+ return this.nativeSetupGeneration === generation && this.onChanges !== null;
17068
+ }
17069
+ scheduleNativeReconciliation(generation, filePath) {
17070
+ if (!this.isCurrentNativeSetup(generation)) return;
17071
+ const requiresFullReconciliation = filePath === path23.join(this.projectRoot, ".gitignore");
17072
+ const invalidatedPath = requiresFullReconciliation ? null : filePath;
17073
+ this.nativeInvalidatedPaths.set(invalidatedPath, invalidatedPath !== null);
17074
+ if (this.nativeReconcileTimer) {
17075
+ clearTimeout(this.nativeReconcileTimer);
17076
+ }
17077
+ this.nativeReconcileTimer = setTimeout(() => {
17078
+ this.nativeReconcileTimer = null;
17079
+ void this.reconcileNativeWatcherFromQueue(generation);
17080
+ }, 100);
17081
+ }
17082
+ reconcileNativeWatcherFromQueue(generation) {
17083
+ if (!this.isCurrentNativeSetup(generation) || this.nativeInitializing) return;
17084
+ const invalidatedPaths = this.popNativeInvalidations();
17085
+ if (invalidatedPaths.length === 0) return;
17086
+ void this.reconcileNativeWatcher(generation, invalidatedPaths);
17087
+ }
17088
+ async reconcileNativeWatcher(generation, invalidatedPaths) {
17089
+ if (!this.isCurrentNativeSetup(generation) || !this.nativeReconciler) return;
17090
+ try {
17091
+ const reconciler = this.nativeReconciler;
17092
+ const changes = await reconciler.reconcile(invalidatedPaths);
17093
+ if (!this.isCurrentNativeSetup(generation) || this.nativeReconciler !== reconciler) return;
17094
+ this.recordChanges(changes);
17095
+ } catch (error) {
17096
+ await this.fallbackFromNativeWatcher(generation, error);
17097
+ }
17098
+ }
17099
+ async reconcileNativeWatcherWithPendingInvalidations(generation) {
17100
+ const invalidatedPaths = this.popNativeInvalidations();
17101
+ if (invalidatedPaths.length === 0) return;
17102
+ await this.reconcileNativeWatcher(generation, invalidatedPaths);
17103
+ }
17104
+ popNativeInvalidations() {
17105
+ if (this.nativeInvalidatedPaths.size === 0) return [];
17106
+ const invalidations = [...this.nativeInvalidatedPaths].map(([invalidatedPath, forceChange]) => ({
17107
+ path: invalidatedPath,
17108
+ forceChange
17109
+ }));
17110
+ this.nativeInvalidatedPaths.clear();
17111
+ return invalidations;
17112
+ }
17113
+ async fallbackFromNativeWatcher(generation, error) {
17114
+ if (!this.isCurrentNativeSetup(generation)) return;
17115
+ const watcher = this.nativeWatcher;
17116
+ const externalWatcher = this.watcher;
17117
+ this.nativeWatcher = null;
17118
+ this.watcher = null;
17119
+ this.nativeReconciler = null;
17120
+ this.nativeStarting = false;
17121
+ this.nativeInitializing = false;
17122
+ this.nativeSetupGeneration += 1;
17123
+ if (this.nativeReconcileTimer) {
17124
+ clearTimeout(this.nativeReconcileTimer);
17125
+ this.nativeReconcileTimer = null;
17126
+ }
17127
+ this.nativeInvalidatedPaths.clear();
17128
+ this.setStartupReadySignals(1);
17129
+ console.warn("[codebase-index] Native recursive watcher failed; using Chokidar fallback.", error);
17130
+ await watcher?.stop();
17131
+ await externalWatcher?.close();
17132
+ if (this.onChanges) {
17133
+ this.createWatcher();
17134
+ }
16726
17135
  }
16727
17136
  handleChange(watcher, type, filePath) {
16728
17137
  if (this.watcher !== watcher) {
16729
17138
  return;
16730
17139
  }
16731
17140
  if (this.isProjectConfigPath(filePath)) {
17141
+ this.updateConfigPathState(filePath);
16732
17142
  this.pendingChanges.set(filePath, type);
16733
17143
  this.scheduleFlush();
16734
17144
  return;
@@ -16743,27 +17153,33 @@ var FileWatcher = class {
16743
17153
  )) {
16744
17154
  return;
16745
17155
  }
16746
- this.pendingChanges.set(filePath, type);
17156
+ this.recordChanges([{ path: filePath, type }]);
17157
+ }
17158
+ recordChanges(changes) {
17159
+ if (changes.length === 0) return;
17160
+ for (const change of changes) {
17161
+ this.pendingChanges.set(change.path, change.type);
17162
+ }
16747
17163
  this.scheduleFlush();
16748
17164
  }
16749
17165
  isProjectConfigPath(filePath) {
16750
- const relativePath = path21.relative(this.projectRoot, filePath);
16751
- const normalizedRelativePath = path21.normalize(relativePath);
17166
+ const relativePath = path23.relative(this.projectRoot, filePath);
17167
+ const normalizedRelativePath = path23.normalize(relativePath);
16752
17168
  return this.getProjectConfigRelativePaths().some((configPath) => configPath === normalizedRelativePath);
16753
17169
  }
16754
17170
  isProjectConfigPathOrAncestor(relativePath) {
16755
- const normalizedRelativePath = path21.normalize(relativePath);
17171
+ const normalizedRelativePath = path23.normalize(relativePath);
16756
17172
  return this.getProjectConfigRelativePaths().some(
16757
- (configPath) => configPath === normalizedRelativePath || configPath.startsWith(`${normalizedRelativePath}${path21.sep}`)
17173
+ (configPath) => configPath === normalizedRelativePath || configPath.startsWith(`${normalizedRelativePath}${path23.sep}`)
16758
17174
  );
16759
17175
  }
16760
17176
  isOutsideProjectPath(relativePath) {
16761
- return relativePath === ".." || relativePath.startsWith(`..${path21.sep}`) || path21.isAbsolute(relativePath);
17177
+ return relativePath === ".." || relativePath.startsWith(`..${path23.sep}`) || path23.isAbsolute(relativePath);
16762
17178
  }
16763
17179
  getNearestExistingDirectory(directoryPath) {
16764
17180
  let candidate = directoryPath;
16765
17181
  while (!existsSync13(candidate)) {
16766
- const parent = path21.dirname(candidate);
17182
+ const parent = path23.dirname(candidate);
16767
17183
  if (parent === candidate) break;
16768
17184
  candidate = parent;
16769
17185
  }
@@ -16771,9 +17187,51 @@ var FileWatcher = class {
16771
17187
  }
16772
17188
  getProjectConfigRelativePaths() {
16773
17189
  return this.projectConfigPaths.map(
16774
- (configPath) => path21.normalize(path21.relative(this.projectRoot, configPath))
17190
+ (configPath) => path23.normalize(path23.relative(this.projectRoot, configPath))
16775
17191
  );
16776
17192
  }
17193
+ getConfigPathStates() {
17194
+ const states = /* @__PURE__ */ new Map();
17195
+ for (const configPath of this.projectConfigPaths) {
17196
+ const state = this.getConfigPathState(configPath);
17197
+ if (state) states.set(configPath, state);
17198
+ }
17199
+ return states;
17200
+ }
17201
+ getConfigPathState(configPath) {
17202
+ try {
17203
+ const stats = statSync6(configPath);
17204
+ return stats.isFile() ? { mtimeMs: stats.mtimeMs, size: stats.size } : void 0;
17205
+ } catch (error) {
17206
+ void error;
17207
+ return void 0;
17208
+ }
17209
+ }
17210
+ updateConfigPathState(configPath) {
17211
+ const state = this.getConfigPathState(configPath);
17212
+ if (state) {
17213
+ this.configPathStates.set(configPath, state);
17214
+ } else {
17215
+ this.configPathStates.delete(configPath);
17216
+ }
17217
+ }
17218
+ reconcileConfigPathStates() {
17219
+ const nextStates = this.getConfigPathStates();
17220
+ const changes = [];
17221
+ for (const configPath of this.projectConfigPaths) {
17222
+ const previous = this.configPathStates.get(configPath);
17223
+ const next = nextStates.get(configPath);
17224
+ if (!previous && next) {
17225
+ changes.push({ path: configPath, type: "add" });
17226
+ } else if (previous && !next) {
17227
+ changes.push({ path: configPath, type: "unlink" });
17228
+ } else if (previous && next && (previous.size !== next.size || previous.mtimeMs !== next.mtimeMs)) {
17229
+ changes.push({ path: configPath, type: "change" });
17230
+ }
17231
+ }
17232
+ this.configPathStates = nextStates;
17233
+ this.recordChanges(changes);
17234
+ }
16777
17235
  scheduleFlush() {
16778
17236
  if (this.debounceTimer) {
16779
17237
  clearTimeout(this.debounceTimer);
@@ -16787,7 +17245,7 @@ var FileWatcher = class {
16787
17245
  return;
16788
17246
  }
16789
17247
  const changes = Array.from(this.pendingChanges.entries()).map(
16790
- ([path28, type]) => ({ path: path28, type })
17248
+ ([path30, type]) => ({ path: path30, type })
16791
17249
  );
16792
17250
  this.pendingChanges.clear();
16793
17251
  try {
@@ -16801,20 +17259,31 @@ var FileWatcher = class {
16801
17259
  clearTimeout(this.debounceTimer);
16802
17260
  this.debounceTimer = null;
16803
17261
  }
17262
+ if (this.nativeReconcileTimer) {
17263
+ clearTimeout(this.nativeReconcileTimer);
17264
+ this.nativeReconcileTimer = null;
17265
+ }
17266
+ this.nativeInvalidatedPaths.clear();
16804
17267
  const watcher = this.watcher;
17268
+ const nativeWatcher = this.nativeWatcher;
16805
17269
  const pendingClose = this.pendingClose;
16806
17270
  const resolveReady = this.resolveReady;
16807
17271
  this.watcher = null;
17272
+ this.nativeWatcher = null;
17273
+ this.nativeReconciler = null;
17274
+ this.nativeStarting = false;
17275
+ this.nativeInitializing = false;
17276
+ this.nativeSetupGeneration += 1;
16808
17277
  this.pendingClose = null;
16809
17278
  this.resolveReady = null;
16810
17279
  this.readyPromise = null;
16811
17280
  this.pendingChanges.clear();
16812
17281
  this.onChanges = null;
16813
- await Promise.all([watcher?.close(), pendingClose]);
17282
+ await Promise.all([watcher?.close(), nativeWatcher?.stop(), pendingClose]);
16814
17283
  resolveReady?.();
16815
17284
  }
16816
17285
  isRunning() {
16817
- return this.watcher !== null;
17286
+ return this.watcher !== null || this.nativeWatcher !== null || this.nativeStarting;
16818
17287
  }
16819
17288
  async waitUntilReady() {
16820
17289
  await (this.readyPromise ?? Promise.resolve());
@@ -16822,7 +17291,7 @@ var FileWatcher = class {
16822
17291
  };
16823
17292
 
16824
17293
  // src/watcher/git-head-watcher.ts
16825
- import * as path22 from "path";
17294
+ import * as path24 from "path";
16826
17295
  var GitHeadWatcher = class {
16827
17296
  watcher = null;
16828
17297
  projectRoot;
@@ -16844,13 +17313,13 @@ var GitHeadWatcher = class {
16844
17313
  this.readyPromise = Promise.resolve();
16845
17314
  return;
16846
17315
  }
16847
- this.readyPromise = new Promise((resolve15) => {
16848
- this.resolveReady = resolve15;
17316
+ this.readyPromise = new Promise((resolve17) => {
17317
+ this.resolveReady = resolve17;
16849
17318
  });
16850
17319
  this.onBranchChange = handler;
16851
17320
  this.currentBranch = getCurrentBranch(this.projectRoot);
16852
17321
  const headPath = getHeadPath(this.projectRoot);
16853
- const refsPath = path22.join(this.projectRoot, ".git", "refs", "heads");
17322
+ const refsPath = path24.join(this.projectRoot, ".git", "refs", "heads");
16854
17323
  this.watcher = chokidar_default.watch([headPath, refsPath], {
16855
17324
  persistent: true,
16856
17325
  ignoreInitial: true,
@@ -17706,7 +18175,7 @@ async function resolveCodebaseContextUnmeasured(projectRoot, host, input) {
17706
18175
  const directory = input.directory ?? void 0;
17707
18176
  const tokenBudget = input.tokenBudget ?? void 0;
17708
18177
  if (from && to) {
17709
- const path28 = await getCallGraphPath(
18178
+ const path30 = await getCallGraphPath(
17710
18179
  projectRoot,
17711
18180
  host,
17712
18181
  from,
@@ -17715,25 +18184,25 @@ async function resolveCodebaseContextUnmeasured(projectRoot, host, input) {
17715
18184
  fromFilePath,
17716
18185
  toFilePath
17717
18186
  );
17718
- const pathText = formatCallGraphPathResult(path28);
17719
- if (path28.path.length > 0) {
18187
+ const pathText = formatCallGraphPathResult(path30);
18188
+ if (path30.path.length > 0) {
17720
18189
  const fitted2 = fitTextToContextBudget(
17721
18190
  pathText,
17722
18191
  tokenBudget
17723
18192
  );
17724
18193
  return {
17725
18194
  text: fitted2.text,
17726
- details: fittedDetails("path", fitted2, path28.path.length)
18195
+ details: fittedDetails("path", fitted2, path30.path.length)
17727
18196
  };
17728
18197
  }
17729
- if (path28.from.status !== "resolved" || path28.to.status !== "resolved") {
18198
+ if (path30.from.status !== "resolved" || path30.to.status !== "resolved") {
17730
18199
  const fitted2 = fitTextToContextBudget(pathText, tokenBudget);
17731
18200
  return {
17732
18201
  text: fitted2.text,
17733
18202
  details: fittedDetails("path", fitted2, 0)
17734
18203
  };
17735
18204
  }
17736
- const resolvedFrom = path28.from;
18205
+ const resolvedFrom = path30.from;
17737
18206
  const { callers } = await getCallGraphData(projectRoot, host, {
17738
18207
  name: to,
17739
18208
  direction: "callers",
@@ -17892,7 +18361,7 @@ async function executeCallGraph(projectRoot, host, args) {
17892
18361
  return { text: formatCallGraphResult(await getCallGraphData(projectRoot, host, args)) };
17893
18362
  }
17894
18363
  async function executeCallGraphPath(projectRoot, host, args) {
17895
- const path28 = await getCallGraphPath(
18364
+ const path30 = await getCallGraphPath(
17896
18365
  projectRoot,
17897
18366
  host,
17898
18367
  args.from,
@@ -17901,7 +18370,7 @@ async function executeCallGraphPath(projectRoot, host, args) {
17901
18370
  args.fromFilePath,
17902
18371
  args.toFilePath
17903
18372
  );
17904
- return { text: formatCallGraphPathResult(path28) };
18373
+ return { text: formatCallGraphPathResult(path30) };
17905
18374
  }
17906
18375
  async function executeCodeCommunities(projectRoot, host, args) {
17907
18376
  const result = await getCodeCommunities(projectRoot, host, args);
@@ -17911,11 +18380,11 @@ async function executeCodeCommunities(projectRoot, host, args) {
17911
18380
  // src/adapters/opencode/tools.ts
17912
18381
  import { writeFileSync as writeFileSync4 } from "fs";
17913
18382
  import * as os7 from "os";
17914
- import * as path25 from "path";
18383
+ import * as path27 from "path";
17915
18384
 
17916
18385
  // src/tools/visualize/activity.ts
17917
18386
  import { execFileSync } from "child_process";
17918
- import * as path23 from "path";
18387
+ import * as path25 from "path";
17919
18388
  function attachRecentActivity(data, projectRoot) {
17920
18389
  const activity = readGitActivity(projectRoot);
17921
18390
  const changes = activity.size > 0 ? buildGitChanges(data, activity, projectRoot) : buildGraphChanges(data);
@@ -18077,7 +18546,7 @@ function normalizePath3(filePath) {
18077
18546
  return filePath.replace(/\\/g, "/");
18078
18547
  }
18079
18548
  function toGitRelativePath(projectRoot, filePath) {
18080
- const relativePath = path23.isAbsolute(filePath) ? path23.relative(projectRoot, filePath) : filePath;
18549
+ const relativePath = path25.isAbsolute(filePath) ? path25.relative(projectRoot, filePath) : filePath;
18081
18550
  return normalizePath3(relativePath);
18082
18551
  }
18083
18552
 
@@ -18335,7 +18804,7 @@ render();
18335
18804
  }
18336
18805
 
18337
18806
  // src/tools/visualize/transform.ts
18338
- import * as path24 from "path";
18807
+ import * as path26 from "path";
18339
18808
 
18340
18809
  // src/tools/visualize/modules.ts
18341
18810
  var MAX_MODULES = 18;
@@ -18468,8 +18937,8 @@ function compactModules(prefixToNodes) {
18468
18937
  function deriveModules(nodes) {
18469
18938
  const initial = /* @__PURE__ */ new Map();
18470
18939
  for (const node of nodes) {
18471
- const relative12 = stripToProjectRelative(node.filePath);
18472
- const prefix = modulePrefixFromRelativePath(relative12);
18940
+ const relative14 = stripToProjectRelative(node.filePath);
18941
+ const prefix = modulePrefixFromRelativePath(relative14);
18473
18942
  if (!initial.has(prefix)) initial.set(prefix, []);
18474
18943
  initial.get(prefix)?.push(node);
18475
18944
  }
@@ -18595,7 +19064,7 @@ function transformForVisualization(symbols, edges, options = {}) {
18595
19064
  filePath: s.filePath,
18596
19065
  kind: s.kind,
18597
19066
  line: s.startLine,
18598
- directory: path24.dirname(s.filePath),
19067
+ directory: path26.dirname(s.filePath),
18599
19068
  moduleId: "",
18600
19069
  moduleLabel: ""
18601
19070
  }));
@@ -18915,7 +19384,7 @@ var index_visualize = tool({
18915
19384
  return "No connected symbols found for visualization. Try including orphans with includeOrphans=true, or check that the call graph has resolved edges.";
18916
19385
  }
18917
19386
  const html = generateVisualizationHtml(vizData);
18918
- const outputPath = path25.join(os7.tmpdir(), `call-graph-${Date.now()}.html`);
19387
+ const outputPath = path27.join(os7.tmpdir(), `call-graph-${Date.now()}.html`);
18919
19388
  writeFileSync4(outputPath, html, "utf-8");
18920
19389
  let result = `Temporal call graph visualization generated: ${outputPath}
18921
19390
 
@@ -19022,7 +19491,7 @@ var PI_TOOL_NAMES = [
19022
19491
 
19023
19492
  // src/commands/loader.ts
19024
19493
  import { existsSync as existsSync14, readdirSync as readdirSync3, readFileSync as readFileSync9 } from "fs";
19025
- import * as path26 from "path";
19494
+ import * as path28 from "path";
19026
19495
  function parseFrontmatter(content) {
19027
19496
  const frontmatterRegex = /^---\s*\n([\s\S]*?)\n---\s*\n([\s\S]*)$/;
19028
19497
  const match = content.match(frontmatterRegex);
@@ -19048,7 +19517,7 @@ function loadCommandsFromDirectory(commandsDir) {
19048
19517
  }
19049
19518
  const files = readdirSync3(commandsDir).filter((f) => f.endsWith(".md"));
19050
19519
  for (const file of files) {
19051
- const filePath = path26.join(commandsDir, file);
19520
+ const filePath = path28.join(commandsDir, file);
19052
19521
  let content;
19053
19522
  try {
19054
19523
  content = readFileSync9(filePath, "utf-8");
@@ -19057,7 +19526,7 @@ function loadCommandsFromDirectory(commandsDir) {
19057
19526
  throw new Error(`Failed to load command file ${filePath}: ${message}`);
19058
19527
  }
19059
19528
  const { frontmatter, body } = parseFrontmatter(content);
19060
- const name = path26.basename(file, ".md");
19529
+ const name = path28.basename(file, ".md");
19061
19530
  const description = frontmatter.description || `Run the ${name} command`;
19062
19531
  commands.set(name, {
19063
19532
  description,
@@ -19391,23 +19860,41 @@ var RoutingHintController = class {
19391
19860
 
19392
19861
  // src/adapters/opencode.ts
19393
19862
  var activeWatchers = /* @__PURE__ */ new Map();
19394
- function replaceActiveWatcher(projectRoot, nextWatcher) {
19395
- const existing = activeWatchers.get(projectRoot);
19396
- if (existing) {
19397
- existing.stop();
19398
- activeWatchers.delete(projectRoot);
19399
- }
19400
- if (nextWatcher) {
19401
- activeWatchers.set(projectRoot, nextWatcher);
19863
+ var watcherReplacementChains = /* @__PURE__ */ new Map();
19864
+ async function replaceActiveWatcher(projectRoot, createNextWatcher) {
19865
+ const chain = (watcherReplacementChains.get(projectRoot) ?? Promise.resolve()).catch(() => void 0).then(async () => {
19866
+ const existing = activeWatchers.get(projectRoot);
19867
+ if (existing) {
19868
+ try {
19869
+ await existing.stop();
19870
+ } catch (error) {
19871
+ console.error("[codebase-index] Failed to stop replaced watcher:", error);
19872
+ throw error;
19873
+ }
19874
+ if (activeWatchers.get(projectRoot) === existing) {
19875
+ activeWatchers.delete(projectRoot);
19876
+ }
19877
+ }
19878
+ if (createNextWatcher) {
19879
+ activeWatchers.set(projectRoot, createNextWatcher());
19880
+ }
19881
+ });
19882
+ watcherReplacementChains.set(projectRoot, chain);
19883
+ try {
19884
+ await chain;
19885
+ } finally {
19886
+ if (watcherReplacementChains.get(projectRoot) === chain) {
19887
+ watcherReplacementChains.delete(projectRoot);
19888
+ }
19402
19889
  }
19403
19890
  }
19404
19891
  function getCommandsDir() {
19405
19892
  let currentDir = process.cwd();
19406
19893
  if (typeof import.meta !== "undefined" && import.meta.url) {
19407
- currentDir = path27.dirname(fileURLToPath2(import.meta.url));
19894
+ currentDir = path29.dirname(fileURLToPath2(import.meta.url));
19408
19895
  }
19409
- const packageRoot = path27.basename(currentDir) === "adapters" ? path27.join(currentDir, "..", "..") : path27.join(currentDir, "..");
19410
- return path27.join(packageRoot, "commands");
19896
+ const packageRoot = path29.basename(currentDir) === "adapters" ? path29.join(currentDir, "..", "..") : path29.join(currentDir, "..");
19897
+ return path29.join(packageRoot, "commands");
19411
19898
  }
19412
19899
  function appendRoutingHints(output, hints, preferredRole) {
19413
19900
  const preferredBucket = preferredRole === "developer" ? output.developer : output.system;
@@ -19448,9 +19935,12 @@ var plugin = async ({ directory, worktree }) => {
19448
19935
  startAutoIndex(projectRoot, "opencode", "startup");
19449
19936
  }
19450
19937
  if (config.indexing.watchFiles && isValidProject) {
19451
- replaceActiveWatcher(projectRoot, createWatcherWithIndexer(getProjectIndexer, projectRoot, config, "opencode"));
19938
+ await replaceActiveWatcher(
19939
+ projectRoot,
19940
+ () => createWatcherWithIndexer(getProjectIndexer, projectRoot, config, "opencode")
19941
+ );
19452
19942
  } else {
19453
- replaceActiveWatcher(projectRoot, null);
19943
+ await replaceActiveWatcher(projectRoot, null);
19454
19944
  }
19455
19945
  return {
19456
19946
  tool: {