opencode-codebase-index 0.24.0 → 0.25.1

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.
@@ -333,7 +333,7 @@ var require_ignore = __commonJS({
333
333
  // path matching.
334
334
  // - check `string` either `MODE_IGNORE` or `MODE_CHECK_IGNORE`
335
335
  // @returns {TestResult} true if a file is ignored
336
- test(path25, checkUnignored, mode) {
336
+ test(path26, checkUnignored, mode) {
337
337
  let ignored = false;
338
338
  let unignored = false;
339
339
  let matchedRule;
@@ -342,7 +342,7 @@ var require_ignore = __commonJS({
342
342
  if (unignored === negative && ignored !== unignored || negative && !ignored && !unignored && !checkUnignored) {
343
343
  return;
344
344
  }
345
- const matched = rule[mode].test(path25);
345
+ const matched = rule[mode].test(path26);
346
346
  if (!matched) {
347
347
  return;
348
348
  }
@@ -363,17 +363,17 @@ var require_ignore = __commonJS({
363
363
  var throwError = (message, Ctor) => {
364
364
  throw new Ctor(message);
365
365
  };
366
- var checkPath = (path25, originalPath, doThrow) => {
367
- if (!isString(path25)) {
366
+ var checkPath = (path26, originalPath, doThrow) => {
367
+ if (!isString(path26)) {
368
368
  return doThrow(
369
369
  `path must be a string, but got \`${originalPath}\``,
370
370
  TypeError
371
371
  );
372
372
  }
373
- if (!path25) {
373
+ if (!path26) {
374
374
  return doThrow(`path must not be empty`, TypeError);
375
375
  }
376
- if (checkPath.isNotRelative(path25)) {
376
+ if (checkPath.isNotRelative(path26)) {
377
377
  const r = "`path.relative()`d";
378
378
  return doThrow(
379
379
  `path should be a ${r} string, but got "${originalPath}"`,
@@ -382,7 +382,7 @@ var require_ignore = __commonJS({
382
382
  }
383
383
  return true;
384
384
  };
385
- var isNotRelative = (path25) => REGEX_TEST_INVALID_PATH.test(path25);
385
+ var isNotRelative = (path26) => REGEX_TEST_INVALID_PATH.test(path26);
386
386
  checkPath.isNotRelative = isNotRelative;
387
387
  checkPath.convert = (p) => p;
388
388
  var Ignore2 = class {
@@ -412,19 +412,19 @@ var require_ignore = __commonJS({
412
412
  }
413
413
  // @returns {TestResult}
414
414
  _test(originalPath, cache, checkUnignored, slices) {
415
- const path25 = originalPath && checkPath.convert(originalPath);
415
+ const path26 = originalPath && checkPath.convert(originalPath);
416
416
  checkPath(
417
- path25,
417
+ path26,
418
418
  originalPath,
419
419
  this._strictPathCheck ? throwError : RETURN_FALSE
420
420
  );
421
- return this._t(path25, cache, checkUnignored, slices);
421
+ return this._t(path26, cache, checkUnignored, slices);
422
422
  }
423
- checkIgnore(path25) {
424
- if (!REGEX_TEST_TRAILING_SLASH.test(path25)) {
425
- return this.test(path25);
423
+ checkIgnore(path26) {
424
+ if (!REGEX_TEST_TRAILING_SLASH.test(path26)) {
425
+ return this.test(path26);
426
426
  }
427
- const slices = path25.split(SLASH2).filter(Boolean);
427
+ const slices = path26.split(SLASH2).filter(Boolean);
428
428
  slices.pop();
429
429
  if (slices.length) {
430
430
  const parent = this._t(
@@ -437,18 +437,18 @@ var require_ignore = __commonJS({
437
437
  return parent;
438
438
  }
439
439
  }
440
- return this._rules.test(path25, false, MODE_CHECK_IGNORE);
440
+ return this._rules.test(path26, false, MODE_CHECK_IGNORE);
441
441
  }
442
- _t(path25, cache, checkUnignored, slices) {
443
- if (path25 in cache) {
444
- return cache[path25];
442
+ _t(path26, cache, checkUnignored, slices) {
443
+ if (path26 in cache) {
444
+ return cache[path26];
445
445
  }
446
446
  if (!slices) {
447
- slices = path25.split(SLASH2).filter(Boolean);
447
+ slices = path26.split(SLASH2).filter(Boolean);
448
448
  }
449
449
  slices.pop();
450
450
  if (!slices.length) {
451
- return cache[path25] = this._rules.test(path25, checkUnignored, MODE_IGNORE);
451
+ return cache[path26] = this._rules.test(path26, checkUnignored, MODE_IGNORE);
452
452
  }
453
453
  const parent = this._t(
454
454
  slices.join(SLASH2) + SLASH2,
@@ -456,29 +456,29 @@ var require_ignore = __commonJS({
456
456
  checkUnignored,
457
457
  slices
458
458
  );
459
- return cache[path25] = parent.ignored ? parent : this._rules.test(path25, checkUnignored, MODE_IGNORE);
459
+ return cache[path26] = parent.ignored ? parent : this._rules.test(path26, checkUnignored, MODE_IGNORE);
460
460
  }
461
- ignores(path25) {
462
- return this._test(path25, this._ignoreCache, false).ignored;
461
+ ignores(path26) {
462
+ return this._test(path26, this._ignoreCache, false).ignored;
463
463
  }
464
464
  createFilter() {
465
- return (path25) => !this.ignores(path25);
465
+ return (path26) => !this.ignores(path26);
466
466
  }
467
467
  filter(paths) {
468
468
  return makeArray(paths).filter(this.createFilter());
469
469
  }
470
470
  // @returns {TestResult}
471
- test(path25) {
472
- return this._test(path25, this._testCache, true);
471
+ test(path26) {
472
+ return this._test(path26, this._testCache, true);
473
473
  }
474
474
  };
475
475
  var factory = (options) => new Ignore2(options);
476
- var isPathValid = (path25) => checkPath(path25 && checkPath.convert(path25), path25, RETURN_FALSE);
476
+ var isPathValid = (path26) => checkPath(path26 && checkPath.convert(path26), path26, RETURN_FALSE);
477
477
  var setupWindows = () => {
478
478
  const makePosix = (str) => /^\\\\\?\\/.test(str) || /["<>|\u0000-\u001F]+/u.test(str) ? str : str.replace(/\\/g, "/");
479
479
  checkPath.convert = makePosix;
480
480
  const REGEX_TEST_WINDOWS_PATH_ABSOLUTE = /^[a-z]:\//i;
481
- checkPath.isNotRelative = (path25) => REGEX_TEST_WINDOWS_PATH_ABSOLUTE.test(path25) || isNotRelative(path25);
481
+ checkPath.isNotRelative = (path26) => REGEX_TEST_WINDOWS_PATH_ABSOLUTE.test(path26) || isNotRelative(path26);
482
482
  };
483
483
  if (
484
484
  // Detect `process` so that it can run in browsers.
@@ -2055,6 +2055,26 @@ function formatCostEstimate(estimate) {
2055
2055
  \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518
2056
2056
  `;
2057
2057
  }
2058
+ function formatDryRunEstimate(estimate) {
2059
+ return `Dry run: parsed the file set to measure the embedding workload. No embedding requests were made and the index was not changed.
2060
+
2061
+ Files to embed: ${estimate.filesCount.toLocaleString()}
2062
+ Chunks to embed: ${estimate.chunksCount.toLocaleString()}
2063
+ Tokens to embed: ${estimate.tokensToEmbed.toLocaleString()}
2064
+
2065
+ The "Tokens to embed" value uses the local estimateTokens(text) = ceil(len/4). It
2066
+ matches the live "Tokens used" counter only for providers that report usage on the
2067
+ same basis (ollama); for providers that report a server tokenizer count (OpenAI,
2068
+ Gemini, custom) it is only an estimate.
2069
+
2070
+ For a matching provider and a project-scoped force index, the force pass clears its
2071
+ own cached embeddings, so the live counter climbs to this number. A force index on a
2072
+ shared global index can reuse cached embeddings from other projects, and an
2073
+ incremental index counts cached chunks that are not re-embedded; in both cases this
2074
+ number is an upper bound on the live counter, so a progress percent against this
2075
+ total tops out below 100%.
2076
+ `;
2077
+ }
2058
2078
  function formatBytes(bytes) {
2059
2079
  if (bytes === 0) return "0 B";
2060
2080
  const k = 1024;
@@ -2234,7 +2254,7 @@ function formatCodeCommunities(result) {
2234
2254
 
2235
2255
  // src/tools/operations.ts
2236
2256
  var import_fs13 = require("fs");
2237
- var path20 = __toESM(require("path"), 1);
2257
+ var path21 = __toESM(require("path"), 1);
2238
2258
 
2239
2259
  // src/tools/knowledge-base-paths.ts
2240
2260
  var path9 = __toESM(require("path"), 1);
@@ -2946,8 +2966,8 @@ function formatExactSearchHandoff(results) {
2946
2966
  }
2947
2967
  function formatContextEvidence(result, index) {
2948
2968
  const symbol = result.name ? ` ${JSON.stringify(compactEvidenceValue(result.name, 80))}` : "";
2949
- const path25 = compactEvidenceValue(result.filePath, 120);
2950
- return `[${index}] ${result.chunkType}${symbol} in ${path25}:${result.startLine}-${result.endLine} (score ${result.score.toFixed(2)})`;
2969
+ const path26 = compactEvidenceValue(result.filePath, 120);
2970
+ return `[${index}] ${result.chunkType}${symbol} in ${path26}:${result.startLine}-${result.endLine} (score ${result.score.toFixed(2)})`;
2951
2971
  }
2952
2972
  function formatContextPack(heading, selected, candidateCount, duplicateCount, limitOmittedCount, budgetOmittedCount, includeExactSearchHandoff) {
2953
2973
  const lines = selected.map((result, index) => formatContextEvidence(result, index + 1));
@@ -3590,8 +3610,8 @@ function formatEffectivenessMetrics(snapshot) {
3590
3610
 
3591
3611
  // src/utils/auto-index.ts
3592
3612
  var import_fs8 = require("fs");
3593
- var os4 = __toESM(require("os"), 1);
3594
- var path12 = __toESM(require("path"), 1);
3613
+ var os5 = __toESM(require("os"), 1);
3614
+ var path13 = __toESM(require("path"), 1);
3595
3615
 
3596
3616
  // src/indexer/index-lock.ts
3597
3617
  var import_crypto = require("crypto");
@@ -3838,7 +3858,7 @@ function removeDeadReclaimMarker(lockPath, expectedOwner) {
3838
3858
  return true;
3839
3859
  }
3840
3860
  function reclaimDeadOwner(indexPath, lockPath, expectedOwner) {
3841
- const reclaimPath = path10.join(lockPath, RECLAIM_DIRECTORY_NAME);
3861
+ const reclaimPath2 = path10.join(lockPath, RECLAIM_DIRECTORY_NAME);
3842
3862
  const reclaimOwner = {
3843
3863
  pid: process.pid,
3844
3864
  hostname: os3.hostname(),
@@ -3847,19 +3867,19 @@ function reclaimDeadOwner(indexPath, lockPath, expectedOwner) {
3847
3867
  expectedOwnerToken: expectedOwner.token
3848
3868
  };
3849
3869
  for (let attempt = 0; attempt < 2; attempt += 1) {
3850
- if (publishJsonDirectory(reclaimPath, reclaimOwner)) break;
3870
+ if (publishJsonDirectory(reclaimPath2, reclaimOwner)) break;
3851
3871
  if (attempt === 0 && removeDeadReclaimMarker(lockPath, expectedOwner)) continue;
3852
3872
  return false;
3853
3873
  }
3854
3874
  try {
3855
- const currentReclaimer = readReclaimOwner(reclaimPath);
3875
+ const currentReclaimer = readReclaimOwner(reclaimPath2);
3856
3876
  const currentOwner = readDirectoryOwner(lockPath);
3857
3877
  if (!currentReclaimer || !sameReclaimOwner(currentReclaimer, reclaimOwner) || !currentOwner || !sameOwner(currentOwner, expectedOwner) || getOwnerLiveness(currentOwner) !== "dead") {
3858
3878
  return false;
3859
3879
  }
3860
3880
  publishRecoveryMarker(indexPath, expectedOwner);
3861
3881
  const ownerBeforeQuarantine = readDirectoryOwner(lockPath);
3862
- const reclaimerBeforeQuarantine = readReclaimOwner(reclaimPath);
3882
+ const reclaimerBeforeQuarantine = readReclaimOwner(reclaimPath2);
3863
3883
  if (!ownerBeforeQuarantine || !sameOwner(ownerBeforeQuarantine, expectedOwner) || getOwnerLiveness(ownerBeforeQuarantine) !== "dead" || !reclaimerBeforeQuarantine || !sameReclaimOwner(reclaimerBeforeQuarantine, reclaimOwner)) {
3864
3884
  return false;
3865
3885
  }
@@ -4029,10 +4049,884 @@ function completeLeaseRecovery(lease) {
4029
4049
  }
4030
4050
  }
4031
4051
 
4052
+ // src/utils/background-worker.ts
4053
+ var import_node_crypto = require("crypto");
4054
+ var import_node_fs = require("fs");
4055
+ var os4 = __toESM(require("os"), 1);
4056
+ var path11 = __toESM(require("path"), 1);
4057
+ var OWNER_FILE_NAME2 = "owner.json";
4058
+ var HEARTBEAT_FILE_PREFIX = "heartbeat.";
4059
+ var RECLAIM_DIRECTORY_NAME2 = "reclaim";
4060
+ var REFRESH_REQUEST_FILE_NAME = "refresh-request.json";
4061
+ var HEARTBEAT_INTERVAL_MS = 5e3;
4062
+ var STALE_LEASE_MS = 3e4;
4063
+ var RETRY_DELAY_MS = 5e3;
4064
+ var UUID_PATTERN2 = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
4065
+ var BackgroundWorkerStopError = class extends Error {
4066
+ constructor(watcherError, autoIndexError) {
4067
+ super("Failed to stop background worker");
4068
+ this.watcherError = watcherError;
4069
+ this.autoIndexError = autoIndexError;
4070
+ this.name = "BackgroundWorkerStopError";
4071
+ }
4072
+ watcherError;
4073
+ autoIndexError;
4074
+ };
4075
+ var workers = /* @__PURE__ */ new Map();
4076
+ var workerKeysByProject = /* @__PURE__ */ new Map();
4077
+ var workerReplacementBarriers = /* @__PURE__ */ new Map();
4078
+ function getErrorCode2(error) {
4079
+ return typeof error === "object" && error !== null && "code" in error ? String(error.code) : void 0;
4080
+ }
4081
+ function canonicalizePath(targetPath) {
4082
+ const resolved = path11.resolve(targetPath);
4083
+ if ((0, import_node_fs.existsSync)(resolved)) {
4084
+ try {
4085
+ return import_node_fs.realpathSync.native(resolved);
4086
+ } catch {
4087
+ return resolved;
4088
+ }
4089
+ }
4090
+ const parent = path11.dirname(resolved);
4091
+ if (parent === resolved) return resolved;
4092
+ return path11.join(canonicalizePath(parent), path11.basename(resolved));
4093
+ }
4094
+ function projectLookupKey(projectRoot3, host) {
4095
+ return `${host}::${canonicalizePath(projectRoot3)}`;
4096
+ }
4097
+ function resolveIdentity(projectRoot3, config, host) {
4098
+ const canonicalProjectRoot = canonicalizePath(projectRoot3);
4099
+ const canonicalIndexPath = canonicalizePath(resolveProjectIndexPath(projectRoot3, config.scope, host));
4100
+ return {
4101
+ canonicalIndexPath,
4102
+ canonicalProjectRoot,
4103
+ key: `${canonicalIndexPath}::${canonicalProjectRoot}`
4104
+ };
4105
+ }
4106
+ function controllerKey(identity, host) {
4107
+ return `${identity.key}::${host}`;
4108
+ }
4109
+ function leaseDirectoryName(identity) {
4110
+ const hash = (0, import_node_crypto.createHash)("sha256").update(identity.key).digest("hex").slice(0, 32);
4111
+ return `background-worker.${hash}.lease`;
4112
+ }
4113
+ function leasePathFor(identity) {
4114
+ return path11.join(identity.canonicalIndexPath, leaseDirectoryName(identity));
4115
+ }
4116
+ function parseOwner2(value) {
4117
+ if (typeof value !== "object" || value === null) return null;
4118
+ const candidate = value;
4119
+ if (candidate.version !== 1) return null;
4120
+ if (!Number.isInteger(candidate.pid) || (candidate.pid ?? 0) <= 0) return null;
4121
+ if (typeof candidate.hostname !== "string" || candidate.hostname.length === 0) return null;
4122
+ if (typeof candidate.startedAt !== "string" || Number.isNaN(Date.parse(candidate.startedAt))) return null;
4123
+ if (typeof candidate.heartbeatAt !== "string" || Number.isNaN(Date.parse(candidate.heartbeatAt))) return null;
4124
+ if (typeof candidate.projectRoot !== "string" || candidate.projectRoot.length === 0) return null;
4125
+ if (typeof candidate.indexPath !== "string" || candidate.indexPath.length === 0) return null;
4126
+ if (typeof candidate.token !== "string" || !UUID_PATTERN2.test(candidate.token)) return null;
4127
+ return candidate;
4128
+ }
4129
+ function parseHeartbeat(value, expectedToken) {
4130
+ if (typeof value !== "object" || value === null) return null;
4131
+ const candidate = value;
4132
+ if (candidate.version !== 1 || candidate.token !== expectedToken) return null;
4133
+ if (typeof candidate.heartbeatAt !== "string" || Number.isNaN(Date.parse(candidate.heartbeatAt))) return null;
4134
+ return candidate;
4135
+ }
4136
+ function parseReclaimOwner2(value) {
4137
+ if (typeof value !== "object" || value === null) return null;
4138
+ const candidate = value;
4139
+ if (candidate.version !== 1) return null;
4140
+ if (!Number.isInteger(candidate.pid) || (candidate.pid ?? 0) <= 0) return null;
4141
+ if (typeof candidate.hostname !== "string" || candidate.hostname.length === 0) return null;
4142
+ if (typeof candidate.startedAt !== "string" || Number.isNaN(Date.parse(candidate.startedAt))) return null;
4143
+ if (typeof candidate.token !== "string" || !UUID_PATTERN2.test(candidate.token)) return null;
4144
+ if (candidate.expectedOwnerToken !== null && (typeof candidate.expectedOwnerToken !== "string" || !UUID_PATTERN2.test(candidate.expectedOwnerToken))) return null;
4145
+ return candidate;
4146
+ }
4147
+ function heartbeatPath(leasePath, token) {
4148
+ return path11.join(leasePath, `${HEARTBEAT_FILE_PREFIX}${token}.json`);
4149
+ }
4150
+ function reclaimPath(leasePath) {
4151
+ return path11.join(leasePath, RECLAIM_DIRECTORY_NAME2);
4152
+ }
4153
+ function refreshRequestPath(leasePath) {
4154
+ return path11.join(leasePath, REFRESH_REQUEST_FILE_NAME);
4155
+ }
4156
+ function readLeaseOwner(leasePath) {
4157
+ try {
4158
+ return parseOwner2(JSON.parse((0, import_node_fs.readFileSync)(path11.join(leasePath, OWNER_FILE_NAME2), "utf-8")));
4159
+ } catch {
4160
+ return null;
4161
+ }
4162
+ }
4163
+ function readOwner(leasePath) {
4164
+ const owner = readLeaseOwner(leasePath);
4165
+ if (!owner) return null;
4166
+ try {
4167
+ const heartbeat = parseHeartbeat(
4168
+ JSON.parse((0, import_node_fs.readFileSync)(heartbeatPath(leasePath, owner.token), "utf-8")),
4169
+ owner.token
4170
+ );
4171
+ return heartbeat ? { ...owner, heartbeatAt: heartbeat.heartbeatAt } : owner;
4172
+ } catch {
4173
+ return owner;
4174
+ }
4175
+ }
4176
+ function readReclaimOwner2(leasePath) {
4177
+ try {
4178
+ return parseReclaimOwner2(JSON.parse((0, import_node_fs.readFileSync)(path11.join(reclaimPath(leasePath), OWNER_FILE_NAME2), "utf-8")));
4179
+ } catch {
4180
+ return null;
4181
+ }
4182
+ }
4183
+ function ownerLiveness(owner) {
4184
+ if (owner.hostname !== os4.hostname()) return "unknown";
4185
+ try {
4186
+ process.kill(owner.pid, 0);
4187
+ return "alive";
4188
+ } catch (error) {
4189
+ const code = getErrorCode2(error);
4190
+ if (code === "ESRCH") return "dead";
4191
+ if (code === "EPERM") return "alive";
4192
+ return "unknown";
4193
+ }
4194
+ }
4195
+ function isHeartbeatExpired(owner) {
4196
+ return Date.now() - Date.parse(owner.heartbeatAt) >= STALE_LEASE_MS;
4197
+ }
4198
+ function sameOwner2(left, right) {
4199
+ return left.pid === right.pid && left.hostname === right.hostname && left.token === right.token;
4200
+ }
4201
+ function writeHeartbeat(leasePath, owner) {
4202
+ const targetPath = heartbeatPath(leasePath, owner.token);
4203
+ const temporaryPath = `${targetPath}.tmp.${process.pid}.${owner.token}.${(0, import_node_crypto.randomUUID)()}`;
4204
+ const heartbeat = {
4205
+ version: 1,
4206
+ token: owner.token,
4207
+ heartbeatAt: owner.heartbeatAt
4208
+ };
4209
+ try {
4210
+ (0, import_node_fs.writeFileSync)(temporaryPath, JSON.stringify(heartbeat), {
4211
+ encoding: "utf-8",
4212
+ flag: "wx",
4213
+ mode: 384
4214
+ });
4215
+ (0, import_node_fs.renameSync)(temporaryPath, targetPath);
4216
+ const currentOwner = readLeaseOwner(leasePath);
4217
+ return currentOwner !== null && sameOwner2(currentOwner, owner);
4218
+ } finally {
4219
+ if ((0, import_node_fs.existsSync)(temporaryPath)) (0, import_node_fs.rmSync)(temporaryPath, { force: true });
4220
+ }
4221
+ }
4222
+ function requestRefreshFromLeader(leasePath, allowDisabledAutoIndex) {
4223
+ const requestPath = refreshRequestPath(leasePath);
4224
+ const temporaryPath = `${requestPath}.tmp.${process.pid}.${(0, import_node_crypto.randomUUID)()}`;
4225
+ try {
4226
+ const request = {
4227
+ allowDisabledAutoIndex,
4228
+ requestedAt: (/* @__PURE__ */ new Date()).toISOString(),
4229
+ version: 1
4230
+ };
4231
+ (0, import_node_fs.writeFileSync)(temporaryPath, JSON.stringify(request), {
4232
+ encoding: "utf-8",
4233
+ flag: "wx",
4234
+ mode: 384
4235
+ });
4236
+ (0, import_node_fs.renameSync)(temporaryPath, requestPath);
4237
+ } catch (error) {
4238
+ if (getErrorCode2(error) !== "ENOENT") {
4239
+ console.error("[codebase-index] Failed to request background index refresh from the project worker:", error);
4240
+ }
4241
+ } finally {
4242
+ if ((0, import_node_fs.existsSync)(temporaryPath)) (0, import_node_fs.rmSync)(temporaryPath, { force: true });
4243
+ }
4244
+ }
4245
+ function consumeRefreshRequest(leasePath) {
4246
+ const requestPath = refreshRequestPath(leasePath);
4247
+ const claimedPath = `${requestPath}.handling.${process.pid}.${(0, import_node_crypto.randomUUID)()}`;
4248
+ try {
4249
+ (0, import_node_fs.renameSync)(requestPath, claimedPath);
4250
+ } catch (error) {
4251
+ if (getErrorCode2(error) === "ENOENT") return null;
4252
+ throw error;
4253
+ }
4254
+ try {
4255
+ const value = JSON.parse((0, import_node_fs.readFileSync)(claimedPath, "utf-8"));
4256
+ return {
4257
+ allowDisabledAutoIndex: value.version === 1 && value.allowDisabledAutoIndex === true,
4258
+ requestedAt: typeof value.requestedAt === "string" ? value.requestedAt : (/* @__PURE__ */ new Date()).toISOString(),
4259
+ version: 1
4260
+ };
4261
+ } catch {
4262
+ return { allowDisabledAutoIndex: false, requestedAt: (/* @__PURE__ */ new Date()).toISOString(), version: 1 };
4263
+ } finally {
4264
+ (0, import_node_fs.rmSync)(claimedPath, { force: true });
4265
+ }
4266
+ }
4267
+ function publishLease(leasePath, owner) {
4268
+ const candidatePath = `${leasePath}.candidate.${process.pid}.${owner.token}`;
4269
+ try {
4270
+ (0, import_node_fs.mkdirSync)(candidatePath, { mode: 448 });
4271
+ } catch (error) {
4272
+ if (getErrorCode2(error) === "ENOENT") return false;
4273
+ throw error;
4274
+ }
4275
+ try {
4276
+ (0, import_node_fs.writeFileSync)(path11.join(candidatePath, OWNER_FILE_NAME2), JSON.stringify(owner), {
4277
+ encoding: "utf-8",
4278
+ flag: "wx",
4279
+ mode: 384
4280
+ });
4281
+ if ((0, import_node_fs.existsSync)(leasePath)) return false;
4282
+ try {
4283
+ (0, import_node_fs.renameSync)(candidatePath, leasePath);
4284
+ return true;
4285
+ } catch (error) {
4286
+ if ((0, import_node_fs.existsSync)(leasePath) || getErrorCode2(error) === "ENOENT") return false;
4287
+ throw error;
4288
+ }
4289
+ } finally {
4290
+ if ((0, import_node_fs.existsSync)(candidatePath)) (0, import_node_fs.rmSync)(candidatePath, { recursive: true, force: true });
4291
+ }
4292
+ }
4293
+ function sameReclaimOwner2(left, right) {
4294
+ return left.pid === right.pid && left.hostname === right.hostname && left.token === right.token && left.expectedOwnerToken === right.expectedOwnerToken;
4295
+ }
4296
+ function reclaimerLiveness(owner) {
4297
+ return ownerLiveness(owner);
4298
+ }
4299
+ function isReclaimMarkerExpired(leasePath, owner) {
4300
+ const startedAt = owner ? Date.parse(owner.startedAt) : (() => {
4301
+ try {
4302
+ return (0, import_node_fs.lstatSync)(reclaimPath(leasePath)).mtimeMs;
4303
+ } catch {
4304
+ return Date.now();
4305
+ }
4306
+ })();
4307
+ return Date.now() - startedAt >= STALE_LEASE_MS;
4308
+ }
4309
+ function hasActiveReclaimMarker(leasePath, owner) {
4310
+ const marker = readReclaimOwner2(leasePath);
4311
+ return marker !== null && marker.expectedOwnerToken === owner.token && (marker.hostname !== os4.hostname() || ownerLiveness(owner) !== "alive");
4312
+ }
4313
+ function publishReclaimMarker(leasePath, expectedOwner) {
4314
+ const markerPath = reclaimPath(leasePath);
4315
+ const owner = {
4316
+ version: 1,
4317
+ pid: process.pid,
4318
+ hostname: os4.hostname(),
4319
+ startedAt: (/* @__PURE__ */ new Date()).toISOString(),
4320
+ token: (0, import_node_crypto.randomUUID)(),
4321
+ expectedOwnerToken: expectedOwner?.token ?? null
4322
+ };
4323
+ try {
4324
+ (0, import_node_fs.mkdirSync)(markerPath, { mode: 448 });
4325
+ } catch (error) {
4326
+ if (getErrorCode2(error) === "EEXIST" || getErrorCode2(error) === "ENOENT") return null;
4327
+ throw error;
4328
+ }
4329
+ try {
4330
+ (0, import_node_fs.writeFileSync)(path11.join(markerPath, OWNER_FILE_NAME2), JSON.stringify(owner), {
4331
+ encoding: "utf-8",
4332
+ flag: "wx",
4333
+ mode: 384
4334
+ });
4335
+ return owner;
4336
+ } catch (error) {
4337
+ (0, import_node_fs.rmSync)(markerPath, { recursive: true, force: true });
4338
+ throw error;
4339
+ }
4340
+ }
4341
+ function removeExpiredReclaimMarker(leasePath, expectedOwner) {
4342
+ const marker = readReclaimOwner2(leasePath);
4343
+ const markerPath = reclaimPath(leasePath);
4344
+ if (!(0, import_node_fs.existsSync)(markerPath)) return false;
4345
+ if (marker && marker.expectedOwnerToken !== (expectedOwner?.token ?? null)) return false;
4346
+ if (marker && (reclaimerLiveness(marker) === "alive" || !isReclaimMarkerExpired(leasePath, marker))) return false;
4347
+ if (!marker && !isReclaimMarkerExpired(leasePath, null)) return false;
4348
+ const staleMarkerPath = `${markerPath}.stale.${marker?.pid ?? process.pid}.${marker?.token ?? (0, import_node_crypto.randomUUID)()}.${(0, import_node_crypto.randomUUID)()}`;
4349
+ try {
4350
+ (0, import_node_fs.renameSync)(markerPath, staleMarkerPath);
4351
+ } catch (error) {
4352
+ if (getErrorCode2(error) === "ENOENT") return false;
4353
+ throw error;
4354
+ }
4355
+ try {
4356
+ let claimedMarker = null;
4357
+ try {
4358
+ claimedMarker = parseReclaimOwner2(
4359
+ JSON.parse((0, import_node_fs.readFileSync)(path11.join(staleMarkerPath, OWNER_FILE_NAME2), "utf-8"))
4360
+ );
4361
+ } catch {
4362
+ claimedMarker = null;
4363
+ }
4364
+ const markerMatches = marker ? claimedMarker !== null && sameReclaimOwner2(claimedMarker, marker) : claimedMarker === null;
4365
+ if (!markerMatches || !canReclaimLease(leasePath, expectedOwner)) {
4366
+ if (!(0, import_node_fs.existsSync)(markerPath) && (0, import_node_fs.existsSync)(staleMarkerPath)) (0, import_node_fs.renameSync)(staleMarkerPath, markerPath);
4367
+ return false;
4368
+ }
4369
+ (0, import_node_fs.rmSync)(staleMarkerPath, { recursive: true, force: true });
4370
+ return true;
4371
+ } catch (error) {
4372
+ if (getErrorCode2(error) === "ENOENT") return false;
4373
+ throw error;
4374
+ }
4375
+ }
4376
+ function canReclaimLease(leasePath, expectedOwner) {
4377
+ if (!(0, import_node_fs.existsSync)(leasePath)) return false;
4378
+ if (!expectedOwner) return false;
4379
+ const currentOwner = readOwner(leasePath);
4380
+ if (!currentOwner || !sameOwner2(currentOwner, expectedOwner)) return false;
4381
+ if (currentOwner.hostname === os4.hostname()) {
4382
+ return ownerLiveness(currentOwner) === "dead";
4383
+ }
4384
+ return isHeartbeatExpired(currentOwner);
4385
+ }
4386
+ function reclaimLease(leasePath, expectedOwner) {
4387
+ let marker = null;
4388
+ for (let attempt = 0; attempt < 2; attempt += 1) {
4389
+ marker = publishReclaimMarker(leasePath, expectedOwner);
4390
+ if (marker) break;
4391
+ if (attempt === 0 && removeExpiredReclaimMarker(leasePath, expectedOwner)) continue;
4392
+ return false;
4393
+ }
4394
+ if (!marker) return false;
4395
+ const markerPath = reclaimPath(leasePath);
4396
+ try {
4397
+ const currentMarker = readReclaimOwner2(leasePath);
4398
+ if (!currentMarker || !sameReclaimOwner2(currentMarker, marker) || !canReclaimLease(leasePath, expectedOwner)) {
4399
+ return false;
4400
+ }
4401
+ const stalePath = `${leasePath}.stale.${process.pid}.${marker.token}`;
4402
+ (0, import_node_fs.renameSync)(leasePath, stalePath);
4403
+ const quarantinedOwner = readOwner(stalePath);
4404
+ const quarantinedMarker = readReclaimOwner2(stalePath);
4405
+ if (!quarantinedMarker || !sameReclaimOwner2(quarantinedMarker, marker) || expectedOwner !== null && (!quarantinedOwner || !sameOwner2(quarantinedOwner, expectedOwner))) {
4406
+ if (!(0, import_node_fs.existsSync)(leasePath) && (0, import_node_fs.existsSync)(stalePath)) (0, import_node_fs.renameSync)(stalePath, leasePath);
4407
+ return false;
4408
+ }
4409
+ (0, import_node_fs.rmSync)(stalePath, { recursive: true, force: true });
4410
+ return true;
4411
+ } catch (error) {
4412
+ if (getErrorCode2(error) === "ENOENT") return false;
4413
+ throw error;
4414
+ } finally {
4415
+ const currentMarker = readReclaimOwner2(leasePath);
4416
+ if (currentMarker && sameReclaimOwner2(currentMarker, marker)) {
4417
+ (0, import_node_fs.rmSync)(markerPath, { recursive: true, force: true });
4418
+ }
4419
+ }
4420
+ }
4421
+ function acquireLease(identity) {
4422
+ (0, import_node_fs.mkdirSync)(identity.canonicalIndexPath, { recursive: true, mode: 448 });
4423
+ const canonicalIndexPath = import_node_fs.realpathSync.native(identity.canonicalIndexPath);
4424
+ const leasePath = path11.join(canonicalIndexPath, leaseDirectoryName({ ...identity, canonicalIndexPath }));
4425
+ for (let attempt = 0; attempt < 4; attempt += 1) {
4426
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
4427
+ const owner = {
4428
+ version: 1,
4429
+ pid: process.pid,
4430
+ hostname: os4.hostname(),
4431
+ startedAt: timestamp,
4432
+ heartbeatAt: timestamp,
4433
+ projectRoot: identity.canonicalProjectRoot,
4434
+ indexPath: canonicalIndexPath,
4435
+ token: (0, import_node_crypto.randomUUID)()
4436
+ };
4437
+ if (publishLease(leasePath, owner)) {
4438
+ return { leasePath, owner };
4439
+ }
4440
+ const existingOwner = readOwner(leasePath);
4441
+ if (existingOwner) {
4442
+ if (canReclaimLease(leasePath, existingOwner) && reclaimLease(leasePath, existingOwner)) continue;
4443
+ return null;
4444
+ }
4445
+ return null;
4446
+ }
4447
+ return null;
4448
+ }
4449
+ function releaseLease(lease) {
4450
+ const currentOwner = readOwner(lease.leasePath);
4451
+ if (!currentOwner || !sameOwner2(currentOwner, lease.owner)) return false;
4452
+ const releasePath = `${lease.leasePath}.release.${lease.owner.pid}.${lease.owner.token}`;
4453
+ try {
4454
+ (0, import_node_fs.renameSync)(lease.leasePath, releasePath);
4455
+ } catch (error) {
4456
+ if (getErrorCode2(error) === "ENOENT") return false;
4457
+ throw error;
4458
+ }
4459
+ const claimedOwner = readOwner(releasePath);
4460
+ if (!claimedOwner || !sameOwner2(claimedOwner, lease.owner)) {
4461
+ if (!(0, import_node_fs.existsSync)(lease.leasePath) && (0, import_node_fs.existsSync)(releasePath)) {
4462
+ (0, import_node_fs.renameSync)(releasePath, lease.leasePath);
4463
+ }
4464
+ return false;
4465
+ }
4466
+ (0, import_node_fs.rmSync)(releasePath, { recursive: true, force: true });
4467
+ return true;
4468
+ }
4469
+ var BackgroundWorkerController = class {
4470
+ constructor(projectRoot3, host, config, hooks, identity) {
4471
+ this.projectRoot = projectRoot3;
4472
+ this.host = host;
4473
+ this.config = config;
4474
+ this.hooks = hooks;
4475
+ this.identity = identity;
4476
+ }
4477
+ projectRoot;
4478
+ host;
4479
+ config;
4480
+ hooks;
4481
+ identity;
4482
+ lease = null;
4483
+ watcher = null;
4484
+ leaderReady = Promise.resolve();
4485
+ heartbeatTimer = null;
4486
+ retryTimer = null;
4487
+ teardownRetryTimer = null;
4488
+ transition = Promise.resolve();
4489
+ stopPromise = null;
4490
+ stopped = false;
4491
+ stopping = false;
4492
+ losingLeadership = false;
4493
+ restartAfterStop = false;
4494
+ leaderWorkStopped = false;
4495
+ startingLeaderWork = false;
4496
+ stopAutoIndexOnTeardown = true;
4497
+ autoIndexStarted = false;
4498
+ reportedError = null;
4499
+ update(config, hooks, options) {
4500
+ const autoIndexWasEnabled = this.config.indexing.autoIndex;
4501
+ const shouldReplaceWatcher = this.watcher !== null && hooks.watcherFactory !== void 0 && (hooks.watcherFactory === null || hooks.replaceWatcher === true);
4502
+ this.config = config;
4503
+ this.hooks = {
4504
+ ...this.hooks,
4505
+ ...hooks,
4506
+ watcherFactory: hooks.watcherFactory === void 0 ? this.hooks.watcherFactory : hooks.watcherFactory,
4507
+ watcherFactoryForConfig: hooks.watcherFactoryForConfig === void 0 ? this.hooks.watcherFactoryForConfig : hooks.watcherFactoryForConfig
4508
+ };
4509
+ if (autoIndexWasEnabled && !config.indexing.autoIndex || options.restartAutoIndex === true && config.indexing.autoIndex && !this.startingLeaderWork) {
4510
+ this.autoIndexStarted = false;
4511
+ }
4512
+ if (!this.canRun()) {
4513
+ void this.stop().catch((error) => {
4514
+ console.error("[codebase-index] Failed to stop background worker after disabling automatic work:", error);
4515
+ });
4516
+ return;
4517
+ }
4518
+ if (shouldReplaceWatcher) {
4519
+ void this.enqueue(async () => {
4520
+ const watcher = this.watcher;
4521
+ if (watcher) {
4522
+ await watcher.stop();
4523
+ if (this.watcher === watcher) this.watcher = null;
4524
+ }
4525
+ if (this.lease && !this.stopped) this.startLeaderWork();
4526
+ }).catch((error) => {
4527
+ console.error("[codebase-index] Failed to replace background file watcher:", error);
4528
+ });
4529
+ }
4530
+ this.start();
4531
+ }
4532
+ startAfter(activation) {
4533
+ this.transition = activation.catch(() => void 0);
4534
+ this.start();
4535
+ }
4536
+ start() {
4537
+ if (!this.canRun() || this.losingLeadership) return;
4538
+ if (this.stopping) {
4539
+ this.restartAfterStop = true;
4540
+ return;
4541
+ }
4542
+ this.stopped = false;
4543
+ void this.enqueue(async () => {
4544
+ if (this.stopped || this.stopping || this.losingLeadership || !this.canRun()) return;
4545
+ if (!this.lease) {
4546
+ try {
4547
+ this.lease = acquireLease(this.identity);
4548
+ this.reportedError = null;
4549
+ } catch (error) {
4550
+ this.reportAcquireError(error);
4551
+ this.scheduleRetry();
4552
+ return;
4553
+ }
4554
+ }
4555
+ if (!this.lease) {
4556
+ this.scheduleRetry();
4557
+ return;
4558
+ }
4559
+ this.startHeartbeat();
4560
+ this.startLeaderWork();
4561
+ });
4562
+ }
4563
+ waitForStart() {
4564
+ return this.transition.catch(() => void 0).then(() => this.leaderReady);
4565
+ }
4566
+ requestRefresh(allowDisabledAutoIndex = false) {
4567
+ this.start();
4568
+ if (!this.isLeader()) {
4569
+ requestRefreshFromLeader(leasePathFor(this.identity), allowDisabledAutoIndex);
4570
+ return;
4571
+ }
4572
+ void this.enqueue(async () => {
4573
+ if (this.stopped || !this.lease) return;
4574
+ this.hooks.startAutoIndex("retrieval", allowDisabledAutoIndex);
4575
+ });
4576
+ }
4577
+ isLeader() {
4578
+ return this.lease !== null && !this.stopping && !this.losingLeadership;
4579
+ }
4580
+ isStopping() {
4581
+ return this.stopping;
4582
+ }
4583
+ getHooksForConfig(config) {
4584
+ const watcherFactoryForConfig = this.hooks.watcherFactoryForConfig;
4585
+ if (!watcherFactoryForConfig) return this.hooks;
4586
+ return {
4587
+ ...this.hooks,
4588
+ watcherFactory: watcherFactoryForConfig(config),
4589
+ replaceWatcher: true
4590
+ };
4591
+ }
4592
+ attachWatcher(watcherFactory, watcherFactoryForConfig) {
4593
+ if (this.hooks.watcherFactory !== void 0) return;
4594
+ this.hooks = {
4595
+ ...this.hooks,
4596
+ watcherFactory,
4597
+ watcherFactoryForConfig: watcherFactoryForConfig ?? this.hooks.watcherFactoryForConfig
4598
+ };
4599
+ this.start();
4600
+ }
4601
+ async stop(stopAutoIndex = true) {
4602
+ if (this.stopPromise) return this.stopPromise;
4603
+ this.stopped = true;
4604
+ this.stopping = true;
4605
+ this.stopAutoIndexOnTeardown &&= stopAutoIndex;
4606
+ this.clearRetryTimer();
4607
+ const attempt = this.enqueue(async () => {
4608
+ try {
4609
+ const lease = this.lease;
4610
+ if (this.leaderWorkStopped) {
4611
+ if (lease) {
4612
+ this.releaseStoppedLease(lease);
4613
+ } else {
4614
+ this.finishStoppedLease();
4615
+ }
4616
+ return;
4617
+ }
4618
+ const hadLeaderWork = lease !== null || this.watcher !== null || this.autoIndexStarted;
4619
+ const stopped = await this.stopLeaderWork(hadLeaderWork && this.stopAutoIndexOnTeardown);
4620
+ if (!lease) {
4621
+ this.finishStoppedLease();
4622
+ return;
4623
+ }
4624
+ if (!stopped.completed) {
4625
+ this.releaseLeaseWhenAutoIndexStops(lease, stopped.completion);
4626
+ return;
4627
+ }
4628
+ this.leaderWorkStopped = true;
4629
+ this.releaseStoppedLease(lease);
4630
+ } catch (error) {
4631
+ this.scheduleTeardownRetry();
4632
+ throw error;
4633
+ }
4634
+ });
4635
+ const completion = attempt.finally(() => {
4636
+ if (this.stopPromise === completion) this.stopPromise = null;
4637
+ });
4638
+ this.stopPromise = completion;
4639
+ return completion;
4640
+ }
4641
+ canRun() {
4642
+ return this.config.indexing.autoIndex || this.hooks.watcherFactory != null;
4643
+ }
4644
+ enqueue(operation) {
4645
+ const next = this.transition.catch(() => void 0).then(operation);
4646
+ this.transition = next;
4647
+ return next;
4648
+ }
4649
+ startLeaderWork() {
4650
+ if (this.stopped || this.stopping || this.losingLeadership) return;
4651
+ this.startingLeaderWork = true;
4652
+ try {
4653
+ if (this.config.indexing.autoIndex && !this.autoIndexStarted) {
4654
+ this.autoIndexStarted = true;
4655
+ this.hooks.startAutoIndex("startup");
4656
+ }
4657
+ if (!this.watcher && this.hooks.watcherFactory) {
4658
+ try {
4659
+ const watcher = this.hooks.watcherFactory();
4660
+ this.watcher = watcher;
4661
+ this.leaderReady = watcher.whenReady?.().catch((error) => {
4662
+ console.error("[codebase-index] Failed while waiting for background file watcher startup:", error);
4663
+ }) ?? Promise.resolve();
4664
+ } catch (error) {
4665
+ console.error("[codebase-index] Failed to start background file watcher:", error);
4666
+ this.leaderReady = Promise.resolve();
4667
+ }
4668
+ }
4669
+ } finally {
4670
+ this.startingLeaderWork = false;
4671
+ }
4672
+ }
4673
+ async stopLeaderWork(stopAutoIndex) {
4674
+ const watcher = this.watcher;
4675
+ let watcherError;
4676
+ if (watcher) {
4677
+ try {
4678
+ await watcher.stop();
4679
+ if (this.watcher === watcher) this.watcher = null;
4680
+ } catch (error) {
4681
+ watcherError = error;
4682
+ }
4683
+ }
4684
+ let autoIndexError;
4685
+ let autoIndexStop = {
4686
+ completed: true,
4687
+ completion: Promise.resolve()
4688
+ };
4689
+ if (stopAutoIndex) {
4690
+ try {
4691
+ autoIndexStop = await this.hooks.stopAutoIndex();
4692
+ this.autoIndexStarted = false;
4693
+ } catch (error) {
4694
+ autoIndexError = error;
4695
+ }
4696
+ }
4697
+ if (watcherError !== void 0 || autoIndexError !== void 0) {
4698
+ throw new BackgroundWorkerStopError(watcherError, autoIndexError);
4699
+ }
4700
+ return autoIndexStop;
4701
+ }
4702
+ releaseLeaseWhenAutoIndexStops(lease, completion) {
4703
+ void completion.then(
4704
+ () => {
4705
+ void this.enqueue(async () => {
4706
+ if (this.lease !== lease || !this.stopping) return;
4707
+ this.leaderWorkStopped = true;
4708
+ this.releaseStoppedLease(lease);
4709
+ }).catch((error) => {
4710
+ console.error("[codebase-index] Failed to release background worker lease after automatic indexing stopped:", error);
4711
+ this.scheduleTeardownRetry();
4712
+ });
4713
+ },
4714
+ (error) => {
4715
+ console.error("[codebase-index] Failed while waiting for automatic indexing to stop:", error);
4716
+ this.scheduleTeardownRetry();
4717
+ }
4718
+ );
4719
+ }
4720
+ releaseStoppedLease(lease) {
4721
+ if (this.lease !== lease) {
4722
+ this.finishStoppedLease();
4723
+ return;
4724
+ }
4725
+ releaseLease(lease);
4726
+ this.lease = null;
4727
+ this.finishStoppedLease();
4728
+ }
4729
+ finishStoppedLease() {
4730
+ this.leaderWorkStopped = false;
4731
+ this.stopAutoIndexOnTeardown = true;
4732
+ this.stopping = false;
4733
+ this.clearTimers();
4734
+ this.restartAfterTeardown();
4735
+ if (!this.stopped || this.stopping) return;
4736
+ const projectKey = projectLookupKey(this.projectRoot, this.host);
4737
+ const key = controllerKey(this.identity, this.host);
4738
+ if (workers.get(key) === this) workers.delete(key);
4739
+ if (workerKeysByProject.get(projectKey) === key) workerKeysByProject.delete(projectKey);
4740
+ }
4741
+ startHeartbeat() {
4742
+ if (this.heartbeatTimer) return;
4743
+ const heartbeat = () => {
4744
+ void this.heartbeat();
4745
+ };
4746
+ this.heartbeatTimer = setInterval(heartbeat, HEARTBEAT_INTERVAL_MS);
4747
+ this.heartbeatTimer.unref?.();
4748
+ }
4749
+ async heartbeat() {
4750
+ const lease = this.lease;
4751
+ if (!lease || this.losingLeadership || this.stopped && !this.stopping) return;
4752
+ if (hasActiveReclaimMarker(lease.leasePath, lease.owner)) {
4753
+ await this.loseLeadership();
4754
+ return;
4755
+ }
4756
+ const currentOwner = readOwner(lease.leasePath);
4757
+ if (!currentOwner || !sameOwner2(currentOwner, lease.owner)) {
4758
+ await this.loseLeadership();
4759
+ return;
4760
+ }
4761
+ try {
4762
+ const nextOwner = { ...lease.owner, heartbeatAt: (/* @__PURE__ */ new Date()).toISOString() };
4763
+ if (!writeHeartbeat(lease.leasePath, nextOwner)) {
4764
+ await this.loseLeadership();
4765
+ return;
4766
+ }
4767
+ lease.owner = nextOwner;
4768
+ const refreshRequest = !this.stopping ? consumeRefreshRequest(lease.leasePath) : null;
4769
+ if (refreshRequest) {
4770
+ this.hooks.startAutoIndex("retrieval", refreshRequest.allowDisabledAutoIndex);
4771
+ }
4772
+ } catch (error) {
4773
+ const ownerAfterError = readOwner(lease.leasePath);
4774
+ if (hasActiveReclaimMarker(lease.leasePath, lease.owner) || !ownerAfterError || !sameOwner2(ownerAfterError, lease.owner)) {
4775
+ await this.loseLeadership();
4776
+ return;
4777
+ }
4778
+ console.error("[codebase-index] Failed to renew background worker lease:", error);
4779
+ }
4780
+ }
4781
+ async loseLeadership() {
4782
+ if (this.losingLeadership) return;
4783
+ this.losingLeadership = true;
4784
+ this.clearHeartbeat();
4785
+ await this.enqueue(async () => this.stopAfterLeadershipLoss());
4786
+ }
4787
+ async stopAfterLeadershipLoss() {
4788
+ const lease = this.lease;
4789
+ if (!lease) {
4790
+ this.losingLeadership = false;
4791
+ return;
4792
+ }
4793
+ try {
4794
+ const stopped = await this.stopLeaderWork(true);
4795
+ this.lease = null;
4796
+ this.losingLeadership = false;
4797
+ if (stopped.completed) {
4798
+ this.scheduleRetry();
4799
+ } else {
4800
+ void stopped.completion.then(() => this.scheduleRetry());
4801
+ }
4802
+ } catch (error) {
4803
+ console.error("[codebase-index] Failed to stop background work after losing its lease:", error);
4804
+ this.scheduleLostLeadershipTeardownRetry();
4805
+ }
4806
+ }
4807
+ scheduleRetry() {
4808
+ if (this.stopped || !this.canRun() || this.retryTimer) return;
4809
+ this.retryTimer = setTimeout(() => {
4810
+ this.retryTimer = null;
4811
+ this.start();
4812
+ }, RETRY_DELAY_MS);
4813
+ this.retryTimer.unref?.();
4814
+ }
4815
+ scheduleTeardownRetry() {
4816
+ if (!this.stopping || this.teardownRetryTimer) return;
4817
+ this.teardownRetryTimer = setTimeout(() => {
4818
+ this.teardownRetryTimer = null;
4819
+ void this.stop(this.stopAutoIndexOnTeardown).catch((error) => {
4820
+ console.error("[codebase-index] Failed to retry background worker teardown:", error);
4821
+ });
4822
+ }, RETRY_DELAY_MS);
4823
+ this.teardownRetryTimer.unref?.();
4824
+ }
4825
+ restartAfterTeardown() {
4826
+ if (!this.restartAfterStop || !this.canRun() || this.losingLeadership) return;
4827
+ this.restartAfterStop = false;
4828
+ this.stopped = false;
4829
+ this.start();
4830
+ }
4831
+ scheduleLostLeadershipTeardownRetry() {
4832
+ if (this.stopped || !this.losingLeadership || this.retryTimer) return;
4833
+ this.retryTimer = setTimeout(() => {
4834
+ this.retryTimer = null;
4835
+ void this.enqueue(async () => this.stopAfterLeadershipLoss());
4836
+ }, RETRY_DELAY_MS);
4837
+ this.retryTimer.unref?.();
4838
+ }
4839
+ clearHeartbeat() {
4840
+ if (!this.heartbeatTimer) return;
4841
+ clearInterval(this.heartbeatTimer);
4842
+ this.heartbeatTimer = null;
4843
+ }
4844
+ clearTimers() {
4845
+ this.clearHeartbeat();
4846
+ this.clearRetryTimer();
4847
+ if (this.teardownRetryTimer) {
4848
+ clearTimeout(this.teardownRetryTimer);
4849
+ this.teardownRetryTimer = null;
4850
+ }
4851
+ }
4852
+ clearRetryTimer() {
4853
+ if (!this.retryTimer) return;
4854
+ clearTimeout(this.retryTimer);
4855
+ this.retryTimer = null;
4856
+ }
4857
+ reportAcquireError(error) {
4858
+ const message = error instanceof Error ? error.message : String(error);
4859
+ if (this.reportedError === message) return;
4860
+ this.reportedError = message;
4861
+ console.error("[codebase-index] Failed to acquire background worker lease:", error);
4862
+ }
4863
+ };
4864
+ function configureBackgroundWorker(projectRoot3, host, config, hooks, options = {}) {
4865
+ const projectKey = projectLookupKey(projectRoot3, host);
4866
+ const identity = resolveIdentity(projectRoot3, config, host);
4867
+ const key = controllerKey(identity, host);
4868
+ const previousKey = workerKeysByProject.get(projectKey);
4869
+ if (previousKey && previousKey !== key) {
4870
+ const previous = workers.get(previousKey);
4871
+ const previousBarrier = workerReplacementBarriers.get(projectKey) ?? Promise.resolve();
4872
+ const stopPrevious = previous?.stop(options.stopPreviousAutoIndex ?? true) ?? Promise.resolve();
4873
+ const activation = Promise.all([previousBarrier, stopPrevious]).then(() => void 0);
4874
+ workerReplacementBarriers.set(projectKey, activation);
4875
+ workers.delete(previousKey);
4876
+ const worker2 = new BackgroundWorkerController(projectRoot3, host, config, hooks, identity);
4877
+ worker2.startAfter(activation);
4878
+ workers.set(key, worker2);
4879
+ workerKeysByProject.set(projectKey, key);
4880
+ return;
4881
+ }
4882
+ let worker = workers.get(key);
4883
+ if (!worker) {
4884
+ worker = new BackgroundWorkerController(projectRoot3, host, config, hooks, identity);
4885
+ workers.set(key, worker);
4886
+ } else {
4887
+ worker.update(config, hooks, options);
4888
+ }
4889
+ workerKeysByProject.set(projectKey, key);
4890
+ worker.start();
4891
+ }
4892
+ function updateBackgroundWorkerConfig(projectRoot3, host, config) {
4893
+ const projectKey = projectLookupKey(projectRoot3, host);
4894
+ const key = workerKeysByProject.get(projectKey);
4895
+ const worker = key ? workers.get(key) : void 0;
4896
+ if (!worker) return;
4897
+ configureBackgroundWorker(projectRoot3, host, config, worker.getHooksForConfig(config), {
4898
+ stopPreviousAutoIndex: false,
4899
+ restartAutoIndex: true
4900
+ });
4901
+ }
4902
+ function waitForBackgroundWorkerStart(projectRoot3, host) {
4903
+ const key = workerKeysByProject.get(projectLookupKey(projectRoot3, host));
4904
+ return workers.get(key ?? "")?.waitForStart() ?? Promise.resolve();
4905
+ }
4906
+ function requestBackgroundWorkerRefresh(projectRoot3, host, allowDisabledAutoIndex = false) {
4907
+ const key = workerKeysByProject.get(projectLookupKey(projectRoot3, host));
4908
+ workers.get(key ?? "")?.requestRefresh(allowDisabledAutoIndex);
4909
+ }
4910
+ function isBackgroundWorkerManaged(projectRoot3, host) {
4911
+ const key = workerKeysByProject.get(projectLookupKey(projectRoot3, host));
4912
+ return key !== void 0 && workers.has(key);
4913
+ }
4914
+ function isBackgroundWorkerLeader(projectRoot3, host) {
4915
+ const key = workerKeysByProject.get(projectLookupKey(projectRoot3, host));
4916
+ return key !== void 0 && workers.get(key)?.isLeader() === true;
4917
+ }
4918
+ async function stopBackgroundWorker(projectRoot3, host) {
4919
+ const projectKey = projectLookupKey(projectRoot3, host);
4920
+ const key = workerKeysByProject.get(projectKey);
4921
+ const worker = key ? workers.get(key) : void 0;
4922
+ if (!worker) return;
4923
+ await worker.stop();
4924
+ }
4925
+
4032
4926
  // src/utils/files.ts
4033
4927
  var import_ignore = __toESM(require_ignore(), 1);
4034
4928
  var import_fs7 = require("fs");
4035
- var path11 = __toESM(require("path"), 1);
4929
+ var path12 = __toESM(require("path"), 1);
4036
4930
  var PROJECT_MARKERS = [
4037
4931
  ".git",
4038
4932
  "package.json",
@@ -4050,7 +4944,7 @@ var PROJECT_MARKERS = [
4050
4944
  ];
4051
4945
  function hasProjectMarker(projectRoot3) {
4052
4946
  for (const marker of PROJECT_MARKERS) {
4053
- if ((0, import_fs7.existsSync)(path11.join(projectRoot3, marker))) {
4947
+ if ((0, import_fs7.existsSync)(path12.join(projectRoot3, marker))) {
4054
4948
  return true;
4055
4949
  }
4056
4950
  }
@@ -4077,33 +4971,53 @@ function createIgnoreFilter(projectRoot3) {
4077
4971
  "**/*build*/**"
4078
4972
  ];
4079
4973
  ig.add(defaultIgnores);
4080
- const gitignorePath = path11.join(projectRoot3, ".gitignore");
4974
+ const gitignorePath = path12.join(projectRoot3, ".gitignore");
4081
4975
  if ((0, import_fs7.existsSync)(gitignorePath)) {
4082
4976
  const gitignoreContent = (0, import_fs7.readFileSync)(gitignorePath, "utf-8");
4083
4977
  ig.add(gitignoreContent);
4084
4978
  }
4085
4979
  return ig;
4086
4980
  }
4087
- function shouldIncludeFile(filePath, projectRoot3, includePatterns, excludePatterns, ignoreFilter) {
4088
- const relativePath = path11.relative(projectRoot3, filePath);
4089
- if (hasFilteredPathSegment(relativePath, path11.sep)) {
4090
- return false;
4091
- }
4092
- if (ignoreFilter.ignores(relativePath)) {
4093
- return false;
4981
+ function toPosixRelativePath(relativePath) {
4982
+ return relativePath.split(path12.sep).join("/");
4983
+ }
4984
+ function matchesAnyGlob(filePath, patterns) {
4985
+ const normalized = toPosixRelativePath(filePath);
4986
+ return patterns.some((pattern) => matchGlob(normalized, pattern));
4987
+ }
4988
+ function isExcludedByPatterns(relativePath, excludePatterns) {
4989
+ return matchesAnyGlob(relativePath, excludePatterns);
4990
+ }
4991
+ function isExcludedDirectory(relativePath, excludePatterns) {
4992
+ const normalized = toPosixRelativePath(relativePath);
4993
+ if (matchesAnyGlob(normalized, excludePatterns)) {
4994
+ return true;
4094
4995
  }
4095
4996
  for (const pattern of excludePatterns) {
4096
- if (matchGlob(relativePath, pattern)) {
4097
- return false;
4997
+ const posixPattern = toPosixRelativePath(pattern).replace(/\/+$/, "");
4998
+ if (!posixPattern.endsWith("/**")) {
4999
+ continue;
4098
5000
  }
4099
- }
4100
- for (const pattern of includePatterns) {
4101
- if (matchGlob(relativePath, pattern)) {
5001
+ const directoryPattern = posixPattern.slice(0, -3);
5002
+ if (directoryPattern && matchesAnyGlob(normalized, [directoryPattern])) {
4102
5003
  return true;
4103
5004
  }
4104
5005
  }
4105
5006
  return false;
4106
5007
  }
5008
+ function shouldIncludeFile(filePath, projectRoot3, includePatterns, excludePatterns, ignoreFilter) {
5009
+ const relativePath = toPosixRelativePath(path12.relative(projectRoot3, filePath));
5010
+ if (hasFilteredPathSegment(relativePath, "/")) {
5011
+ return false;
5012
+ }
5013
+ if (ignoreFilter.ignores(relativePath)) {
5014
+ return false;
5015
+ }
5016
+ if (isExcludedByPatterns(relativePath, excludePatterns)) {
5017
+ return false;
5018
+ }
5019
+ return matchesAnyGlob(relativePath, includePatterns);
5020
+ }
4107
5021
  function matchGlob(filePath, pattern) {
4108
5022
  if (pattern.startsWith("**/")) {
4109
5023
  const withoutPrefix = pattern.slice(3);
@@ -4124,8 +5038,8 @@ async function* walkDirectory(dir, projectRoot3, includePatterns, excludePattern
4124
5038
  const filesInDir = [];
4125
5039
  const subdirs = [];
4126
5040
  for (const entry of entries) {
4127
- const fullPath = path11.join(dir, entry.name);
4128
- const relativePath = path11.relative(projectRoot3, fullPath);
5041
+ const fullPath = path12.join(dir, entry.name);
5042
+ const relativePath = toPosixRelativePath(path12.relative(projectRoot3, fullPath));
4129
5043
  if (isHiddenPathSegment(entry.name)) {
4130
5044
  if (entry.isDirectory()) {
4131
5045
  skipped.push({ path: relativePath, reason: "excluded" });
@@ -4143,6 +5057,10 @@ async function* walkDirectory(dir, projectRoot3, includePatterns, excludePattern
4143
5057
  continue;
4144
5058
  }
4145
5059
  if (entry.isDirectory()) {
5060
+ if (isExcludedDirectory(relativePath, excludePatterns)) {
5061
+ skipped.push({ path: relativePath, reason: "excluded" });
5062
+ continue;
5063
+ }
4146
5064
  subdirs.push({ fullPath, relativePath });
4147
5065
  } else if (entry.isFile()) {
4148
5066
  const stat5 = await import_fs7.promises.stat(fullPath);
@@ -4150,20 +5068,11 @@ async function* walkDirectory(dir, projectRoot3, includePatterns, excludePattern
4150
5068
  skipped.push({ path: relativePath, reason: "too_large" });
4151
5069
  continue;
4152
5070
  }
4153
- for (const pattern of excludePatterns) {
4154
- if (matchGlob(relativePath, pattern)) {
4155
- skipped.push({ path: relativePath, reason: "excluded" });
4156
- continue;
4157
- }
4158
- }
4159
- let matched = false;
4160
- for (const pattern of includePatterns) {
4161
- if (matchGlob(relativePath, pattern)) {
4162
- matched = true;
4163
- break;
4164
- }
5071
+ if (isExcludedByPatterns(relativePath, excludePatterns)) {
5072
+ skipped.push({ path: relativePath, reason: "excluded" });
5073
+ continue;
4165
5074
  }
4166
- if (matched) {
5075
+ if (matchesAnyGlob(relativePath, includePatterns)) {
4167
5076
  filesInDir.push({ path: fullPath, size: stat5.size });
4168
5077
  }
4169
5078
  }
@@ -4174,7 +5083,7 @@ async function* walkDirectory(dir, projectRoot3, includePatterns, excludePattern
4174
5083
  yield f;
4175
5084
  }
4176
5085
  for (let i = options.maxFilesPerDirectory; i < filesInDir.length; i++) {
4177
- skipped.push({ path: path11.relative(projectRoot3, filesInDir[i].path), reason: "excluded" });
5086
+ skipped.push({ path: toPosixRelativePath(path12.relative(projectRoot3, filesInDir[i].path)), reason: "excluded" });
4178
5087
  }
4179
5088
  const canRecurse = options.maxDepth === -1 || currentDepth < options.maxDepth;
4180
5089
  if (canRecurse) {
@@ -4214,8 +5123,8 @@ async function collectFiles(projectRoot3, includePatterns, excludePatterns, maxF
4214
5123
  if (additionalRoots && additionalRoots.length > 0) {
4215
5124
  const normalizedRoots = /* @__PURE__ */ new Set();
4216
5125
  for (const kbRoot of additionalRoots) {
4217
- const resolved = path11.normalize(
4218
- path11.isAbsolute(kbRoot) ? kbRoot : path11.resolve(projectRoot3, kbRoot)
5126
+ const resolved = path12.normalize(
5127
+ path12.isAbsolute(kbRoot) ? kbRoot : path12.resolve(projectRoot3, kbRoot)
4219
5128
  );
4220
5129
  normalizedRoots.add(resolved);
4221
5130
  }
@@ -4256,7 +5165,7 @@ function getErrorMessage(error) {
4256
5165
  return error instanceof Error ? error.message : String(error);
4257
5166
  }
4258
5167
  function runCommand(file, args, options) {
4259
- return new Promise((resolve17, reject) => {
5168
+ return new Promise((resolve18, reject) => {
4260
5169
  childProcess.execFile(
4261
5170
  file,
4262
5171
  args,
@@ -4266,7 +5175,7 @@ function runCommand(file, args, options) {
4266
5175
  reject(error);
4267
5176
  return;
4268
5177
  }
4269
- resolve17(stdout);
5178
+ resolve18(stdout);
4270
5179
  }
4271
5180
  );
4272
5181
  });
@@ -4358,8 +5267,8 @@ var AutoIndexCancelledError = class extends Error {
4358
5267
  function now() {
4359
5268
  return (/* @__PURE__ */ new Date()).toISOString();
4360
5269
  }
4361
- function canonicalizePath(targetPath) {
4362
- const resolved = path12.resolve(targetPath);
5270
+ function canonicalizePath2(targetPath) {
5271
+ const resolved = path13.resolve(targetPath);
4363
5272
  if ((0, import_fs8.existsSync)(resolved)) {
4364
5273
  try {
4365
5274
  return import_fs8.realpathSync.native(resolved);
@@ -4367,20 +5276,20 @@ function canonicalizePath(targetPath) {
4367
5276
  return resolved;
4368
5277
  }
4369
5278
  }
4370
- const parent = path12.dirname(resolved);
5279
+ const parent = path13.dirname(resolved);
4371
5280
  if (parent === resolved) return resolved;
4372
- return path12.join(canonicalizePath(parent), path12.basename(resolved));
5281
+ return path13.join(canonicalizePath2(parent), path13.basename(resolved));
4373
5282
  }
4374
5283
  function isHomeDirectory(projectRoot3) {
4375
- return canonicalizePath(projectRoot3) === canonicalizePath(os4.homedir());
5284
+ return canonicalizePath2(projectRoot3) === canonicalizePath2(os5.homedir());
4376
5285
  }
4377
- function projectLookupKey(projectRoot3, host) {
4378
- return `${host}::${canonicalizePath(projectRoot3)}`;
5286
+ function projectLookupKey2(projectRoot3, host) {
5287
+ return `${host}::${canonicalizePath2(projectRoot3)}`;
4379
5288
  }
4380
5289
  function coordinatorKey(projectRoot3, config, host) {
4381
- const canonicalProjectRoot = canonicalizePath(projectRoot3);
5290
+ const canonicalProjectRoot = canonicalizePath2(projectRoot3);
4382
5291
  const indexPath = resolveProjectIndexPath(projectRoot3, config.scope, host);
4383
- return `${canonicalizePath(indexPath)}::${canonicalProjectRoot}`;
5292
+ return `${canonicalizePath2(indexPath)}::${canonicalProjectRoot}`;
4384
5293
  }
4385
5294
  function getProjectSafety(projectRoot3, config) {
4386
5295
  if (isHomeDirectory(projectRoot3)) {
@@ -4411,10 +5320,10 @@ function safeFailureMessage(error) {
4411
5320
  }
4412
5321
  function cancellableDelay(delayMs, signal) {
4413
5322
  if (signal.aborted) return Promise.reject(new AutoIndexCancelledError());
4414
- return new Promise((resolve17, reject) => {
5323
+ return new Promise((resolve18, reject) => {
4415
5324
  const timer = setTimeout(() => {
4416
5325
  signal.removeEventListener("abort", onAbort);
4417
- resolve17();
5326
+ resolve18();
4418
5327
  }, delayMs);
4419
5328
  timer.unref?.();
4420
5329
  const onAbort = () => {
@@ -4426,18 +5335,44 @@ function cancellableDelay(delayMs, signal) {
4426
5335
  }
4427
5336
  function withTimeout(promise, timeoutMs) {
4428
5337
  if (timeoutMs <= 0) return Promise.resolve(void 0);
4429
- return new Promise((resolve17) => {
4430
- const timer = setTimeout(() => resolve17(void 0), timeoutMs);
5338
+ return new Promise((resolve18) => {
5339
+ const timer = setTimeout(() => resolve18(void 0), timeoutMs);
4431
5340
  timer.unref?.();
4432
5341
  void promise.then((value) => {
4433
5342
  clearTimeout(timer);
4434
- resolve17(value);
5343
+ resolve18(value);
4435
5344
  }, () => {
4436
5345
  clearTimeout(timer);
4437
- resolve17(void 0);
5346
+ resolve18(void 0);
4438
5347
  });
4439
5348
  });
4440
5349
  }
5350
+ function settlesWithin(promise, timeoutMs) {
5351
+ if (timeoutMs <= 0) return Promise.resolve(false);
5352
+ return new Promise((resolve18) => {
5353
+ let settled = false;
5354
+ const timer = setTimeout(() => {
5355
+ if (settled) return;
5356
+ settled = true;
5357
+ resolve18(false);
5358
+ }, timeoutMs);
5359
+ timer.unref?.();
5360
+ void promise.then(
5361
+ () => {
5362
+ if (settled) return;
5363
+ settled = true;
5364
+ clearTimeout(timer);
5365
+ resolve18(true);
5366
+ },
5367
+ () => {
5368
+ if (settled) return;
5369
+ settled = true;
5370
+ clearTimeout(timer);
5371
+ resolve18(true);
5372
+ }
5373
+ );
5374
+ });
5375
+ }
4441
5376
  function requestPriority(request) {
4442
5377
  if (request.force) return 4;
4443
5378
  if (request.source === "manual") return 3;
@@ -4448,6 +5383,7 @@ function mergeRequests(current, next) {
4448
5383
  if (!current) return next;
4449
5384
  const preferred = requestPriority(next) > requestPriority(current) ? next : current;
4450
5385
  return {
5386
+ allowDisabledAutoIndex: current.allowDisabledAutoIndex || next.allowDisabledAutoIndex,
4451
5387
  checkFreshness: current.checkFreshness && next.checkFreshness,
4452
5388
  force: current.force || next.force,
4453
5389
  onProgress: next.onProgress ?? current.onProgress,
@@ -4509,11 +5445,11 @@ var AutoIndexCoordinator = class {
4509
5445
  progress: this.status.progress ? { ...this.status.progress } : void 0
4510
5446
  };
4511
5447
  }
4512
- start(source) {
5448
+ start(source, allowDisabledAutoIndex = false) {
4513
5449
  this.refreshSafety();
4514
- if (!this.registration.config.indexing.autoIndex || !this.registration.safeToRun) return null;
5450
+ if (!this.registration.config.indexing.autoIndex && !allowDisabledAutoIndex || !this.registration.safeToRun) return null;
4515
5451
  if (this.status.state === "failed") return this.inFlight;
4516
- return this.request({ checkFreshness: true, force: false, source });
5452
+ return this.request({ allowDisabledAutoIndex, checkFreshness: true, force: false, source });
4517
5453
  }
4518
5454
  request(request) {
4519
5455
  if (this.stopped) {
@@ -4588,13 +5524,15 @@ var AutoIndexCoordinator = class {
4588
5524
  retryAttempt: void 0
4589
5525
  });
4590
5526
  const inFlight = this.inFlight;
4591
- if (inFlight) {
4592
- if (waitForCompletion) {
4593
- await inFlight;
4594
- } else {
4595
- await withTimeout(inFlight, SHUTDOWN_WAIT_MS);
4596
- }
5527
+ const completion = inFlight ? inFlight.then(() => void 0, () => void 0) : Promise.resolve();
5528
+ if (!inFlight) {
5529
+ return { completed: true, completion };
5530
+ }
5531
+ if (waitForCompletion) {
5532
+ await completion;
5533
+ return { completed: true, completion };
4597
5534
  }
5535
+ return { completed: await settlesWithin(completion, SHUTDOWN_WAIT_MS), completion };
4598
5536
  }
4599
5537
  startRequest(request) {
4600
5538
  if (this.stopped || !this.canRun(request)) {
@@ -4783,7 +5721,7 @@ var AutoIndexCoordinator = class {
4783
5721
  if (request.source === "manual" || request.source === "watcher") {
4784
5722
  return true;
4785
5723
  }
4786
- return this.registration.safeToRun && this.registration.config.indexing.autoIndex;
5724
+ return this.registration.safeToRun && (this.registration.config.indexing.autoIndex || request.allowDisabledAutoIndex === true);
4787
5725
  }
4788
5726
  shouldDeferForBattery(request) {
4789
5727
  return this.registration.backgroundIndexingPolicy !== null && (request.source === "startup" || request.source === "watcher");
@@ -4816,17 +5754,17 @@ var AutoIndexCoordinator = class {
4816
5754
  }
4817
5755
  }
4818
5756
  waitForBatteryRetry(delayMs) {
4819
- return new Promise((resolve17) => {
5757
+ return new Promise((resolve18) => {
4820
5758
  const timer = setTimeout(() => {
4821
5759
  if (this.batteryRetryTimer === timer) {
4822
5760
  this.batteryRetryTimer = null;
4823
5761
  this.resolveBatteryRetry = null;
4824
5762
  }
4825
- resolve17();
5763
+ resolve18();
4826
5764
  }, delayMs);
4827
5765
  timer.unref?.();
4828
5766
  this.batteryRetryTimer = timer;
4829
- this.resolveBatteryRetry = resolve17;
5767
+ this.resolveBatteryRetry = resolve18;
4830
5768
  });
4831
5769
  }
4832
5770
  cancelBatteryRetry() {
@@ -4834,9 +5772,9 @@ var AutoIndexCoordinator = class {
4834
5772
  clearTimeout(this.batteryRetryTimer);
4835
5773
  this.batteryRetryTimer = null;
4836
5774
  }
4837
- const resolve17 = this.resolveBatteryRetry;
5775
+ const resolve18 = this.resolveBatteryRetry;
4838
5776
  this.resolveBatteryRetry = null;
4839
- resolve17?.();
5777
+ resolve18?.();
4840
5778
  }
4841
5779
  finishBatteryCheck(batteryCheck) {
4842
5780
  if (this.batteryCheck !== batteryCheck) return;
@@ -4849,12 +5787,25 @@ var AutoIndexCoordinator = class {
4849
5787
  }
4850
5788
  };
4851
5789
  function getCoordinator(projectRoot3, host) {
4852
- const key = coordinatorKeysByProject.get(projectLookupKey(projectRoot3, host));
5790
+ const key = coordinatorKeysByProject.get(projectLookupKey2(projectRoot3, host));
4853
5791
  return key ? coordinators.get(key) ?? null : null;
4854
5792
  }
4855
- function configureAutoIndex(projectRoot3, host, config, getIndexer) {
4856
- const projectKey = projectLookupKey(projectRoot3, host);
5793
+ function synchronizeBackgroundWorker(projectRoot3, host, config, safeToRun) {
5794
+ if (safeToRun) {
5795
+ updateBackgroundWorkerConfig(projectRoot3, host, config);
5796
+ return;
5797
+ }
5798
+ void stopBackgroundWorker(projectRoot3, host).catch((error) => {
5799
+ console.error("[codebase-index] Failed to stop background worker after project safety changed:", error);
5800
+ });
5801
+ }
5802
+ function configureAutoIndex(projectRoot3, host, config, getIndexer, options = {}) {
5803
+ const projectKey = projectLookupKey2(projectRoot3, host);
4857
5804
  const safety = getProjectSafety(projectRoot3, config);
5805
+ const synchronizeWorker = options.synchronizeBackgroundWorker ?? true;
5806
+ if (options.preserveManagedWorker === true && isBackgroundWorkerManaged(projectRoot3, host)) {
5807
+ return;
5808
+ }
4858
5809
  const registration = {
4859
5810
  backgroundIndexingPolicy: createBackgroundIndexingPolicy(
4860
5811
  config.indexing.pauseBackgroundIndexingOnBattery
@@ -4872,6 +5823,9 @@ function configureAutoIndex(projectRoot3, host, config, getIndexer) {
4872
5823
  const stopPrevious = previousCoordinator?.stop(true) ?? Promise.resolve();
4873
5824
  const activation = Promise.all([previousBarrier, stopPrevious]).then(() => void 0);
4874
5825
  coordinatorReplacementBarriers.set(projectKey, activation);
5826
+ if (synchronizeWorker) {
5827
+ synchronizeBackgroundWorker(projectRoot3, host, config, safety.safeToRun);
5828
+ }
4875
5829
  coordinators.delete(previousKey);
4876
5830
  const coordinator2 = new AutoIndexCoordinator(registration);
4877
5831
  coordinator2.activateAfter(activation);
@@ -4887,8 +5841,17 @@ function configureAutoIndex(projectRoot3, host, config, getIndexer) {
4887
5841
  coordinator.update(registration);
4888
5842
  }
4889
5843
  coordinatorKeysByProject.set(projectKey, key);
5844
+ if (synchronizeWorker) {
5845
+ synchronizeBackgroundWorker(projectRoot3, host, config, safety.safeToRun);
5846
+ }
5847
+ }
5848
+ function startAutoIndexForBackgroundWorker(projectRoot3, host, source = "startup", allowDisabledAutoIndex = false) {
5849
+ return getCoordinator(projectRoot3, host)?.start(source, allowDisabledAutoIndex) ?? null;
4890
5850
  }
4891
5851
  function requestBackgroundIndex(projectRoot3, host) {
5852
+ if (isBackgroundWorkerManaged(projectRoot3, host) && !isBackgroundWorkerLeader(projectRoot3, host)) {
5853
+ return null;
5854
+ }
4892
5855
  return getCoordinator(projectRoot3, host)?.request({
4893
5856
  checkFreshness: false,
4894
5857
  force: false,
@@ -4928,15 +5891,23 @@ async function waitForAutoIndexForRetrieval(projectRoot3, host) {
4928
5891
  };
4929
5892
  }
4930
5893
  try {
4931
- if (await hasReadableCurrentIndex(coordinator)) return { ready: true };
5894
+ const readiness = await getSearchReadiness(coordinator);
5895
+ if (readiness.searchable) {
5896
+ return { ready: true };
5897
+ }
5898
+ if (readiness.blocked) return unavailableSnapshotResult(readiness.reason);
4932
5899
  } catch {
4933
5900
  }
4934
- const job = coordinator.start("retrieval") ?? coordinator.currentJob();
5901
+ const job = startRetrievalRefresh(projectRoot3, host, coordinator);
4935
5902
  if (job) {
4936
5903
  await withTimeout(job, coordinator.getWaitMs());
5904
+ } else if (isBackgroundWorkerManaged(projectRoot3, host)) {
5905
+ await waitForPublishedSnapshot(coordinator, coordinator.getWaitMs());
4937
5906
  }
4938
5907
  try {
4939
- if (await hasReadableCurrentIndex(coordinator)) return { ready: true };
5908
+ const readiness = await getSearchReadiness(coordinator);
5909
+ if (readiness.searchable) return { ready: true };
5910
+ if (readiness.blocked) return unavailableSnapshotResult(readiness.reason);
4940
5911
  } catch {
4941
5912
  }
4942
5913
  const status = coordinator.snapshot();
@@ -4957,21 +5928,52 @@ async function waitForAutoIndexForRetrieval(projectRoot3, host) {
4957
5928
  text: `Automatic indexing is ${status.state}. Retry shortly or call index_status for progress. You can also run index_codebase explicitly.`
4958
5929
  };
4959
5930
  }
4960
- async function stopAutoIndex(projectRoot3, host) {
4961
- await getCoordinator(projectRoot3, host)?.stop();
5931
+ async function stopAutoIndexForBackgroundWorker(projectRoot3, host, waitForCompletion = false) {
5932
+ const coordinator = getCoordinator(projectRoot3, host);
5933
+ if (!coordinator) {
5934
+ return { completed: true, completion: Promise.resolve() };
5935
+ }
5936
+ return coordinator.stop(waitForCompletion);
4962
5937
  }
4963
- async function hasReadableCurrentIndex(coordinator) {
5938
+ async function getSearchReadiness(coordinator) {
4964
5939
  const indexer = coordinator.getIndexer();
4965
5940
  if (indexer.getIndexFreshness) {
4966
5941
  const freshness = await indexer.getIndexFreshness();
4967
- return freshness.readable && freshness.current;
5942
+ const searchable = freshness.readable && freshness.current && freshness.reason === "current";
5943
+ return {
5944
+ blocked: freshness.reason === "unreadable" || freshness.reason === "incompatible" || freshness.reason === "failed-batches" || freshness.reason === "migration-required",
5945
+ reason: freshness.reason,
5946
+ searchable
5947
+ };
5948
+ }
5949
+ const indexed = (await indexer.getStatus()).indexed;
5950
+ return { blocked: false, searchable: indexed };
5951
+ }
5952
+ function unavailableSnapshotResult(reason) {
5953
+ const detail = reason === "incompatible" ? "The existing index is incompatible with the configured embedding provider." : reason === "migration-required" ? "The existing index requires a storage migration." : reason === "failed-batches" ? "The existing index has failed embedding batches." : "The existing index is unreadable.";
5954
+ return {
5955
+ ready: false,
5956
+ text: `${detail} Run index_codebase before retrying retrieval.`
5957
+ };
5958
+ }
5959
+ function startRetrievalRefresh(projectRoot3, host, coordinator) {
5960
+ if (isBackgroundWorkerManaged(projectRoot3, host)) {
5961
+ requestBackgroundWorkerRefresh(projectRoot3, host, true);
5962
+ return isBackgroundWorkerLeader(projectRoot3, host) ? coordinator.currentJob() : null;
5963
+ }
5964
+ return coordinator.start("retrieval") ?? coordinator.currentJob();
5965
+ }
5966
+ async function waitForPublishedSnapshot(coordinator, waitMs) {
5967
+ const deadline = Date.now() + waitMs;
5968
+ while (Date.now() < deadline) {
5969
+ if ((await getSearchReadiness(coordinator)).searchable) return;
5970
+ await new Promise((resolve18) => setTimeout(resolve18, Math.min(250, deadline - Date.now())));
4968
5971
  }
4969
- return (await indexer.getStatus()).indexed;
4970
5972
  }
4971
5973
 
4972
5974
  // src/tools/config-state.ts
4973
5975
  var import_fs9 = require("fs");
4974
- var path13 = __toESM(require("path"), 1);
5976
+ var path14 = __toESM(require("path"), 1);
4975
5977
  function normalizeKnowledgeBasePaths(config, projectRoot3) {
4976
5978
  const normalized = { ...config };
4977
5979
  if (Array.isArray(normalized.knowledgeBases)) {
@@ -4998,8 +6000,8 @@ function loadEditableConfig(projectRoot3, host) {
4998
6000
  }
4999
6001
  function saveConfig(projectRoot3, config, host) {
5000
6002
  const configPath = getConfigPath(projectRoot3, host);
5001
- const configDir = path13.dirname(configPath);
5002
- const configBaseDir = path13.dirname(configDir);
6003
+ const configDir = path14.dirname(configPath);
6004
+ const configBaseDir = path14.dirname(configDir);
5003
6005
  if (!(0, import_fs9.existsSync)(configDir)) {
5004
6006
  (0, import_fs9.mkdirSync)(configDir, { recursive: true });
5005
6007
  }
@@ -5014,7 +6016,7 @@ function saveConfig(projectRoot3, config, host) {
5014
6016
 
5015
6017
  // src/indexer/index.ts
5016
6018
  var import_fs12 = require("fs");
5017
- var path19 = __toESM(require("path"), 1);
6019
+ var path20 = __toESM(require("path"), 1);
5018
6020
  var import_perf_hooks = require("perf_hooks");
5019
6021
  var import_child_process4 = require("child_process");
5020
6022
  var import_util4 = require("util");
@@ -5041,7 +6043,7 @@ function pTimeout(promise, options) {
5041
6043
  } = options;
5042
6044
  let timer;
5043
6045
  let abortHandler;
5044
- const wrappedPromise = new Promise((resolve17, reject) => {
6046
+ const wrappedPromise = new Promise((resolve18, reject) => {
5045
6047
  if (typeof milliseconds !== "number" || Math.sign(milliseconds) !== 1) {
5046
6048
  throw new TypeError(`Expected \`milliseconds\` to be a positive number, got \`${milliseconds}\``);
5047
6049
  }
@@ -5055,7 +6057,7 @@ function pTimeout(promise, options) {
5055
6057
  };
5056
6058
  signal.addEventListener("abort", abortHandler, { once: true });
5057
6059
  }
5058
- promise.then(resolve17, reject);
6060
+ promise.then(resolve18, reject);
5059
6061
  if (milliseconds === Number.POSITIVE_INFINITY) {
5060
6062
  return;
5061
6063
  }
@@ -5063,7 +6065,7 @@ function pTimeout(promise, options) {
5063
6065
  timer = customTimers.setTimeout.call(void 0, () => {
5064
6066
  if (fallback) {
5065
6067
  try {
5066
- resolve17(fallback());
6068
+ resolve18(fallback());
5067
6069
  } catch (error) {
5068
6070
  reject(error);
5069
6071
  }
@@ -5073,7 +6075,7 @@ function pTimeout(promise, options) {
5073
6075
  promise.cancel();
5074
6076
  }
5075
6077
  if (message === false) {
5076
- resolve17();
6078
+ resolve18();
5077
6079
  } else if (message instanceof Error) {
5078
6080
  reject(message);
5079
6081
  } else {
@@ -5475,7 +6477,7 @@ var PQueue = class extends import_index.default {
5475
6477
  // Assign unique ID if not provided
5476
6478
  id: options.id ?? (this.#idAssigner++).toString()
5477
6479
  };
5478
- return new Promise((resolve17, reject) => {
6480
+ return new Promise((resolve18, reject) => {
5479
6481
  const taskSymbol = /* @__PURE__ */ Symbol(`task-${options.id}`);
5480
6482
  let cleanupQueueAbortHandler = () => void 0;
5481
6483
  const run = async () => {
@@ -5515,7 +6517,7 @@ var PQueue = class extends import_index.default {
5515
6517
  })]);
5516
6518
  }
5517
6519
  const result = await operation;
5518
- resolve17(result);
6520
+ resolve18(result);
5519
6521
  this.emit("completed", result);
5520
6522
  } catch (error) {
5521
6523
  reject(error);
@@ -5703,13 +6705,13 @@ var PQueue = class extends import_index.default {
5703
6705
  });
5704
6706
  }
5705
6707
  async #onEvent(event, filter) {
5706
- return new Promise((resolve17) => {
6708
+ return new Promise((resolve18) => {
5707
6709
  const listener = () => {
5708
6710
  if (filter && !filter()) {
5709
6711
  return;
5710
6712
  }
5711
6713
  this.off(event, listener);
5712
- resolve17();
6714
+ resolve18();
5713
6715
  };
5714
6716
  this.on(event, listener);
5715
6717
  });
@@ -5995,7 +6997,7 @@ async function onAttemptFailure({ error, attemptNumber, retriesConsumed, startTi
5995
6997
  const finalDelay = Math.min(delayTime, remainingTime);
5996
6998
  options.signal?.throwIfAborted();
5997
6999
  if (finalDelay > 0) {
5998
- await new Promise((resolve17, reject) => {
7000
+ await new Promise((resolve18, reject) => {
5999
7001
  const onAbort = () => {
6000
7002
  clearTimeout(timeoutToken);
6001
7003
  options.signal?.removeEventListener("abort", onAbort);
@@ -6003,7 +7005,7 @@ async function onAttemptFailure({ error, attemptNumber, retriesConsumed, startTi
6003
7005
  };
6004
7006
  const timeoutToken = setTimeout(() => {
6005
7007
  options.signal?.removeEventListener("abort", onAbort);
6006
- resolve17();
7008
+ resolve18();
6007
7009
  }, finalDelay);
6008
7010
  if (options.unref) {
6009
7011
  timeoutToken.unref?.();
@@ -6121,17 +7123,17 @@ function validateExternalUrl(urlString) {
6121
7123
  if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
6122
7124
  return { valid: false, reason: `Blocked protocol: ${parsed.protocol}` };
6123
7125
  }
6124
- const hostname2 = parsed.hostname.toLowerCase();
6125
- if (BLOCKED_HOSTNAMES.has(hostname2)) {
6126
- return { valid: false, reason: `Blocked: cloud metadata service (${hostname2})` };
7126
+ const hostname3 = parsed.hostname.toLowerCase();
7127
+ if (BLOCKED_HOSTNAMES.has(hostname3)) {
7128
+ return { valid: false, reason: `Blocked: cloud metadata service (${hostname3})` };
6127
7129
  }
6128
7130
  for (const pattern of BLOCKED_METADATA_IPS) {
6129
- if (pattern.test(hostname2)) {
6130
- return { valid: false, reason: `Blocked: cloud metadata IP (${hostname2})` };
7131
+ if (pattern.test(hostname3)) {
7132
+ return { valid: false, reason: `Blocked: cloud metadata IP (${hostname3})` };
6131
7133
  }
6132
7134
  }
6133
- if (/^169\.254\./.test(hostname2)) {
6134
- return { valid: false, reason: `Blocked: link-local address (${hostname2})` };
7135
+ if (/^169\.254\./.test(hostname3)) {
7136
+ return { valid: false, reason: `Blocked: link-local address (${hostname3})` };
6135
7137
  }
6136
7138
  return { valid: true };
6137
7139
  }
@@ -7143,8 +8145,8 @@ function extractParamNames(params) {
7143
8145
  }
7144
8146
 
7145
8147
  // src/native/binding.ts
7146
- var os5 = __toESM(require("os"), 1);
7147
- var path14 = __toESM(require("path"), 1);
8148
+ var os6 = __toESM(require("os"), 1);
8149
+ var path15 = __toESM(require("path"), 1);
7148
8150
  var module2 = __toESM(require("module"), 1);
7149
8151
  var import_node_url = require("url");
7150
8152
 
@@ -7181,7 +8183,7 @@ var STABLE_NATIVE_BINARY_NAME = IDENTITY_CATALOG.native.binaryName;
7181
8183
 
7182
8184
  // src/native/binding.ts
7183
8185
  var import_meta = {};
7184
- function getNativeBindingFilename(platform2 = os5.platform(), arch2 = os5.arch()) {
8186
+ function getNativeBindingFilename(platform2 = os6.platform(), arch2 = os6.arch()) {
7185
8187
  if (platform2 === "darwin" && arch2 === "arm64") {
7186
8188
  return `${STABLE_NATIVE_BINARY_NAME}.darwin-arm64.node`;
7187
8189
  }
@@ -7199,25 +8201,25 @@ function getNativeBindingFilename(platform2 = os5.platform(), arch2 = os5.arch()
7199
8201
  }
7200
8202
  throw new Error(`Unsupported platform: ${platform2}-${arch2}`);
7201
8203
  }
7202
- function resolveNativeBindingPath(packageRoot, platform2 = os5.platform(), arch2 = os5.arch()) {
7203
- return path14.join(packageRoot, "native", getNativeBindingFilename(platform2, arch2));
8204
+ function resolveNativeBindingPath(packageRoot, platform2 = os6.platform(), arch2 = os6.arch()) {
8205
+ return path15.join(packageRoot, "native", getNativeBindingFilename(platform2, arch2));
7204
8206
  }
7205
8207
  function getNativeBinding() {
7206
8208
  let currentDir;
7207
8209
  let requireTarget;
7208
8210
  if (typeof import_meta !== "undefined" && import_meta.url) {
7209
- currentDir = path14.dirname((0, import_node_url.fileURLToPath)(import_meta.url));
8211
+ currentDir = path15.dirname((0, import_node_url.fileURLToPath)(import_meta.url));
7210
8212
  requireTarget = import_meta.url;
7211
8213
  } else if (typeof __dirname !== "undefined") {
7212
8214
  currentDir = __dirname;
7213
8215
  requireTarget = __filename;
7214
8216
  } else {
7215
8217
  currentDir = process.cwd();
7216
- requireTarget = path14.join(currentDir, "index.js");
8218
+ requireTarget = path15.join(currentDir, "index.js");
7217
8219
  }
7218
8220
  const normalizedDir = currentDir.replace(/\\/g, "/");
7219
- const isDevMode = normalizedDir.includes("/src/native") || currentDir.includes(path14.join("src", "native"));
7220
- const packageRoot = isDevMode ? path14.resolve(currentDir, "../..") : path14.resolve(currentDir, "..");
8221
+ const isDevMode = normalizedDir.includes("/src/native") || currentDir.includes(path15.join("src", "native"));
8222
+ const packageRoot = isDevMode ? path15.resolve(currentDir, "../..") : path15.resolve(currentDir, "..");
7221
8223
  const nativePath = resolveNativeBindingPath(packageRoot);
7222
8224
  const require2 = module2.createRequire(requireTarget);
7223
8225
  return require2(nativePath);
@@ -7801,8 +8803,8 @@ var Database = class _Database {
7801
8803
 
7802
8804
  // src/git/branch-materialization.ts
7803
8805
  var import_fs10 = require("fs");
7804
- var os6 = __toESM(require("os"), 1);
7805
- var path15 = __toESM(require("path"), 1);
8806
+ var os7 = __toESM(require("os"), 1);
8807
+ var path16 = __toESM(require("path"), 1);
7806
8808
 
7807
8809
  // src/git/branch-resolution.ts
7808
8810
  var import_child_process = require("child_process");
@@ -8121,13 +9123,13 @@ async function isWorktreeRegistered(projectRoot3, worktreePath) {
8121
9123
  return false;
8122
9124
  }
8123
9125
  function isPathWithinRoot(filePath, rootPath) {
8124
- const relative13 = path15.relative(path15.resolve(rootPath), path15.resolve(filePath));
8125
- return relative13 === "" || !relative13.startsWith(`..${path15.sep}`) && relative13 !== ".." && !path15.isAbsolute(relative13);
9126
+ const relative13 = path16.relative(path16.resolve(rootPath), path16.resolve(filePath));
9127
+ return relative13 === "" || !relative13.startsWith(`..${path16.sep}`) && relative13 !== ".." && !path16.isAbsolute(relative13);
8126
9128
  }
8127
9129
  async function pruneExactMissingWorktreeRegistration(projectRoot3, worktreePath) {
8128
9130
  if (await pathExists(worktreePath)) return false;
8129
9131
  const commonDir = await runGit(projectRoot3, ["rev-parse", "--path-format=absolute", "--git-common-dir"]);
8130
- const registrationsRoot = path15.join(commonDir, "worktrees");
9132
+ const registrationsRoot = path16.join(commonDir, "worktrees");
8131
9133
  let entries;
8132
9134
  try {
8133
9135
  entries = await import_fs10.promises.readdir(registrationsRoot, { withFileTypes: true });
@@ -8138,16 +9140,16 @@ async function pruneExactMissingWorktreeRegistration(projectRoot3, worktreePath)
8138
9140
  const target = canonicalizePathForComparison(worktreePath);
8139
9141
  for (const entry of entries) {
8140
9142
  if (!entry.isDirectory()) continue;
8141
- const registrationPath = path15.join(registrationsRoot, entry.name);
9143
+ const registrationPath = path16.join(registrationsRoot, entry.name);
8142
9144
  if (!isPathWithinRoot(registrationPath, registrationsRoot)) continue;
8143
9145
  let gitdirPath;
8144
9146
  try {
8145
- gitdirPath = (await import_fs10.promises.readFile(path15.join(registrationPath, "gitdir"), "utf8")).trim();
9147
+ gitdirPath = (await import_fs10.promises.readFile(path16.join(registrationPath, "gitdir"), "utf8")).trim();
8146
9148
  } catch {
8147
9149
  continue;
8148
9150
  }
8149
- const resolvedGitdirPath = path15.isAbsolute(gitdirPath) ? gitdirPath : path15.resolve(registrationPath, gitdirPath);
8150
- if (canonicalizePathForComparison(path15.dirname(resolvedGitdirPath)) !== target) continue;
9151
+ const resolvedGitdirPath = path16.isAbsolute(gitdirPath) ? gitdirPath : path16.resolve(registrationPath, gitdirPath);
9152
+ if (canonicalizePathForComparison(path16.dirname(resolvedGitdirPath)) !== target) continue;
8151
9153
  await import_fs10.promises.rm(registrationPath, { recursive: true, force: true });
8152
9154
  return true;
8153
9155
  }
@@ -8165,7 +9167,7 @@ async function removeWorktree(projectRoot3, worktreePath) {
8165
9167
  registered = await isWorktreeRegistered(projectRoot3, worktreePath);
8166
9168
  } catch (error) {
8167
9169
  errors.push(asError(error));
8168
- throw new AggregateError(errors, `Could not verify temporary worktree deregistration; preserved ${path15.dirname(worktreePath)}`);
9170
+ throw new AggregateError(errors, `Could not verify temporary worktree deregistration; preserved ${path16.dirname(worktreePath)}`);
8169
9171
  }
8170
9172
  if (registered) {
8171
9173
  try {
@@ -8181,7 +9183,7 @@ async function removeWorktree(projectRoot3, worktreePath) {
8181
9183
  registered = await isWorktreeRegistered(projectRoot3, worktreePath);
8182
9184
  } catch (error) {
8183
9185
  errors.push(asError(error));
8184
- throw new AggregateError(errors, `Could not verify temporary worktree deregistration; preserved ${path15.dirname(worktreePath)}`);
9186
+ throw new AggregateError(errors, `Could not verify temporary worktree deregistration; preserved ${path16.dirname(worktreePath)}`);
8185
9187
  }
8186
9188
  }
8187
9189
  if (registered && !await pathExists(worktreePath)) {
@@ -8194,13 +9196,13 @@ async function removeWorktree(projectRoot3, worktreePath) {
8194
9196
  }
8195
9197
  if (registered) {
8196
9198
  errors.push(new Error(`Temporary worktree remains registered: ${worktreePath}`));
8197
- throw new AggregateError(errors, `Failed to deregister temporary worktree; preserved ${path15.dirname(worktreePath)}`);
9199
+ throw new AggregateError(errors, `Failed to deregister temporary worktree; preserved ${path16.dirname(worktreePath)}`);
8198
9200
  }
8199
9201
  try {
8200
- await import_fs10.promises.rm(path15.dirname(worktreePath), { recursive: true, force: true });
9202
+ await import_fs10.promises.rm(path16.dirname(worktreePath), { recursive: true, force: true });
8201
9203
  } catch (error) {
8202
9204
  errors.push(asError(error));
8203
- throw new AggregateError(errors, `Deregistered the temporary worktree but could not remove ${path15.dirname(worktreePath)}`);
9205
+ throw new AggregateError(errors, `Deregistered the temporary worktree but could not remove ${path16.dirname(worktreePath)}`);
8204
9206
  }
8205
9207
  }
8206
9208
  async function cleanupTemporaryWorktree(projectRoot3, worktreePath, temporaryRoot) {
@@ -8236,9 +9238,9 @@ async function withMaterializedBranch(request, callback) {
8236
9238
  `Git ref ${JSON.stringify(request.ref ?? request.branch)} is not available locally. For an unfetched branch, pass a remote-qualified name such as origin/feature.`
8237
9239
  );
8238
9240
  }
8239
- const temporaryRoot = await import_fs10.promises.mkdtemp(path15.join(os6.tmpdir(), "codebase-index-branch-"));
8240
- const worktreePath = path15.join(temporaryRoot, "worktree");
8241
- const hooksPath = path15.join(temporaryRoot, "hooks");
9241
+ const temporaryRoot = await import_fs10.promises.mkdtemp(path16.join(os7.tmpdir(), "codebase-index-branch-"));
9242
+ const worktreePath = path16.join(temporaryRoot, "worktree");
9243
+ const hooksPath = path16.join(temporaryRoot, "hooks");
8242
9244
  await import_fs10.promises.mkdir(hooksPath);
8243
9245
  const info = {
8244
9246
  branch: request.branch,
@@ -8290,7 +9292,7 @@ async function withMaterializedBranch(request, callback) {
8290
9292
  // src/tools/changed-files.ts
8291
9293
  var import_child_process2 = require("child_process");
8292
9294
  var import_fs11 = require("fs");
8293
- var path16 = __toESM(require("path"), 1);
9295
+ var path17 = __toESM(require("path"), 1);
8294
9296
  var import_util2 = require("util");
8295
9297
  var execFileAsync2 = (0, import_util2.promisify)(import_child_process2.execFile);
8296
9298
  var GH_PR_VIEW_FIELDS = [
@@ -8432,7 +9434,7 @@ function getHeadRepositoryIdentity(data, host) {
8432
9434
  return `${host}/${owner.toLowerCase()}/${repository.replace(/\.git$/i, "").toLowerCase()}`;
8433
9435
  }
8434
9436
  function getLocalRepositoryIdentity(projectRoot3) {
8435
- let canonicalRoot = path16.resolve(projectRoot3);
9437
+ let canonicalRoot = path17.resolve(projectRoot3);
8436
9438
  try {
8437
9439
  canonicalRoot = import_fs11.realpathSync.native(canonicalRoot);
8438
9440
  } catch {
@@ -8493,17 +9495,17 @@ async function getMergeBase(projectRoot3, baseCommit, headCommit) {
8493
9495
  return commit;
8494
9496
  }
8495
9497
  function normalizeFiles(rawFiles, projectRoot3) {
8496
- const root = path16.resolve(projectRoot3);
9498
+ const root = path17.resolve(projectRoot3);
8497
9499
  const seen = /* @__PURE__ */ new Set();
8498
9500
  const result = [];
8499
9501
  for (const raw of rawFiles) {
8500
9502
  if (raw.length === 0) continue;
8501
- const absolute = path16.resolve(root, raw);
8502
- const relative13 = path16.relative(root, absolute);
8503
- if (path16.isAbsolute(raw) || relative13 === ".." || relative13.startsWith(`..${path16.sep}`) || path16.isAbsolute(relative13)) {
9503
+ const absolute = path17.resolve(root, raw);
9504
+ const relative13 = path17.relative(root, absolute);
9505
+ if (path17.isAbsolute(raw) || relative13 === ".." || relative13.startsWith(`..${path17.sep}`) || path17.isAbsolute(relative13)) {
8504
9506
  throw new Error(`Changed file escapes the project root: ${JSON.stringify(raw)}`);
8505
9507
  }
8506
- const cleaned = relative13.startsWith(`.${path16.sep}`) ? relative13.slice(2) : relative13;
9508
+ const cleaned = relative13.startsWith(`.${path17.sep}`) ? relative13.slice(2) : relative13;
8507
9509
  if (!seen.has(cleaned)) {
8508
9510
  seen.add(cleaned);
8509
9511
  result.push(cleaned);
@@ -8514,7 +9516,7 @@ function normalizeFiles(rawFiles, projectRoot3) {
8514
9516
 
8515
9517
  // src/indexer/git-blame.ts
8516
9518
  var import_child_process3 = require("child_process");
8517
- var path17 = __toESM(require("path"), 1);
9519
+ var path18 = __toESM(require("path"), 1);
8518
9520
  var import_util3 = require("util");
8519
9521
  var execFileAsync3 = (0, import_util3.promisify)(import_child_process3.execFile);
8520
9522
  function parseGitBlamePorcelain(output) {
@@ -8552,7 +9554,7 @@ function parseGitBlamePorcelain(output) {
8552
9554
  return Array.from(commits.values()).filter((commit) => commit.lines > 0).sort((a, b) => b.lines - a.lines || b.committedAt - a.committedAt)[0];
8553
9555
  }
8554
9556
  async function getChunkGitBlame(projectRoot3, filePath, startLine, endLine) {
8555
- const relativePath = path17.relative(projectRoot3, filePath);
9557
+ const relativePath = path18.relative(projectRoot3, filePath);
8556
9558
  try {
8557
9559
  const { stdout } = await execFileAsync3(
8558
9560
  "git",
@@ -8884,6 +9886,9 @@ var CALL_GRAPH_SYMBOL_CHUNK_TYPES = /* @__PURE__ */ new Set([
8884
9886
  "enum_declaration",
8885
9887
  "function_definition",
8886
9888
  "class_definition",
9889
+ // Ruby module/class symbols that are declaration-bearing and navigable.
9890
+ "class",
9891
+ "module",
8887
9892
  "class_specifier",
8888
9893
  "struct_specifier",
8889
9894
  "namespace_definition",
@@ -9128,8 +10133,8 @@ function pathSegmentsForAffinityMatch(filePath) {
9128
10133
  if (segments.length === 0) {
9129
10134
  return [];
9130
10135
  }
9131
- const basename7 = segments[segments.length - 1] ?? "";
9132
- const basenameWithoutExt = basename7.replace(/\.[^/.]+$/u, "");
10136
+ const basename8 = segments[segments.length - 1] ?? "";
10137
+ const basenameWithoutExt = basename8.replace(/\.[^/.]+$/u, "");
9133
10138
  const normalizedSegments = segments.map((segment) => segment.toLowerCase());
9134
10139
  return Array.from(/* @__PURE__ */ new Set([
9135
10140
  ...normalizedSegments,
@@ -9438,8 +10443,8 @@ function hasAllEmbeddingParts(parts, expectedPartCount) {
9438
10443
 
9439
10444
  // src/indexer/failed-state-persistence.ts
9440
10445
  var fs2 = __toESM(require("fs"), 1);
9441
- var import_node_crypto = require("crypto");
9442
- var path18 = __toESM(require("path"), 1);
10446
+ var import_node_crypto2 = require("crypto");
10447
+ var path19 = __toESM(require("path"), 1);
9443
10448
  var import_node_string_decoder = require("string_decoder");
9444
10449
  var CURRENT_FAILED_BATCH_VERSION = 1;
9445
10450
  var DEFAULT_MALFORMED_LINE_ACTION = "skip";
@@ -9457,7 +10462,7 @@ function* readFailedBatchRecords(filePath, options = {}) {
9457
10462
  function createFailedBatchWriter(targetPath) {
9458
10463
  const temporaryPath = createTemporaryPath(targetPath);
9459
10464
  let finalized = false;
9460
- fs2.mkdirSync(path18.dirname(targetPath), { recursive: true });
10465
+ fs2.mkdirSync(path19.dirname(targetPath), { recursive: true });
9461
10466
  fs2.closeSync(fs2.openSync(temporaryPath, "w"));
9462
10467
  const write = (record) => {
9463
10468
  if (finalized) {
@@ -9476,7 +10481,7 @@ function createFailedBatchWriter(targetPath) {
9476
10481
  if (lines.length === 0) {
9477
10482
  return;
9478
10483
  }
9479
- fs2.mkdirSync(path18.dirname(targetPath), { recursive: true });
10484
+ fs2.mkdirSync(path19.dirname(targetPath), { recursive: true });
9480
10485
  fs2.appendFileSync(temporaryPath, `${lines.join("\n")}
9481
10486
  `, "utf-8");
9482
10487
  };
@@ -9484,7 +10489,7 @@ function createFailedBatchWriter(targetPath) {
9484
10489
  if (finalized) {
9485
10490
  return;
9486
10491
  }
9487
- fs2.mkdirSync(path18.dirname(targetPath), { recursive: true });
10492
+ fs2.mkdirSync(path19.dirname(targetPath), { recursive: true });
9488
10493
  fs2.renameSync(temporaryPath, targetPath);
9489
10494
  finalized = true;
9490
10495
  };
@@ -9630,10 +10635,10 @@ function stripLeadingBomAndWhitespace(value) {
9630
10635
  return result;
9631
10636
  }
9632
10637
  function createTemporaryPath(targetPath) {
9633
- const randomId = (0, import_node_crypto.createHash)("sha1").update(`${Date.now()}:${(0, import_node_crypto.randomBytes)(8).toString("hex")}`).digest("hex");
9634
- const targetDir = path18.dirname(targetPath);
9635
- const baseName = path18.basename(targetPath);
9636
- return path18.join(targetDir, `.${baseName}.${randomId}.tmp`);
10638
+ const randomId = (0, import_node_crypto2.createHash)("sha1").update(`${Date.now()}:${(0, import_node_crypto2.randomBytes)(8).toString("hex")}`).digest("hex");
10639
+ const targetDir = path19.dirname(targetPath);
10640
+ const baseName = path19.basename(targetPath);
10641
+ return path19.join(targetDir, `.${baseName}.${randomId}.tmp`);
9637
10642
  }
9638
10643
  function handleMalformedLine(filePath, lineNumber, line, error, options) {
9639
10644
  const action = options.malformedLineAction ?? DEFAULT_MALFORMED_LINE_ACTION;
@@ -9879,9 +10884,9 @@ var SWIFT_PARSER_VERSION = "1";
9879
10884
  var METAL_PARSER_VERSION = "1";
9880
10885
  var SYMBOL_EXTRACTOR_VERSION = "1";
9881
10886
  function isPathWithinRoot2(filePath, rootPath) {
9882
- const normalizedFilePath = path19.resolve(filePath);
9883
- const normalizedRoot = path19.resolve(rootPath);
9884
- return normalizedFilePath === normalizedRoot || normalizedFilePath.startsWith(`${normalizedRoot}${path19.sep}`);
10887
+ const normalizedFilePath = path20.resolve(filePath);
10888
+ const normalizedRoot = path20.resolve(rootPath);
10889
+ return normalizedFilePath === normalizedRoot || normalizedFilePath.startsWith(`${normalizedRoot}${path20.sep}`);
9885
10890
  }
9886
10891
  function promoteIdentifierMatches(query, combined, semanticCandidates, keywordCandidates, database, branchChunkIds, prioritizeSourcePaths = classifyQueryIntentRaw(query) === "source") {
9887
10892
  if (combined.length === 0) {
@@ -10212,10 +11217,10 @@ function matchesHardSearchFilters(candidate, options, projectRoot3) {
10212
11217
  }
10213
11218
  if (options?.directory) {
10214
11219
  const candidatePath = canonicalizePathForComparison(
10215
- path19.resolve(projectRoot3, candidate.metadata.filePath.replace(/\\/g, path19.sep))
11220
+ path20.resolve(projectRoot3, candidate.metadata.filePath.replace(/\\/g, path20.sep))
10216
11221
  );
10217
11222
  const directoryPath = canonicalizePathForComparison(
10218
- path19.resolve(projectRoot3, options.directory.trim().replace(/\\/g, path19.sep))
11223
+ path20.resolve(projectRoot3, options.directory.trim().replace(/\\/g, path20.sep))
10219
11224
  );
10220
11225
  if (!isPathWithinRoot2(candidatePath, directoryPath)) return false;
10221
11226
  }
@@ -10328,26 +11333,37 @@ var Indexer = class _Indexer {
10328
11333
  return this.indexPathOverride ?? resolveProjectIndexPath(this.projectRoot, this.config.scope, this.host);
10329
11334
  }
10330
11335
  toCanonicalFilePath(filePath) {
10331
- if (!path19.isAbsolute(filePath)) {
11336
+ if (!path20.isAbsolute(filePath)) {
10332
11337
  return this.resolveStoredFilePath(filePath, this.projectRoot);
10333
11338
  }
10334
- if (path19.resolve(this.materializedProjectRoot) === path19.resolve(this.projectRoot) || !isPathWithinRoot2(filePath, this.materializedProjectRoot)) {
11339
+ if (path20.resolve(this.materializedProjectRoot) === path20.resolve(this.projectRoot) || !isPathWithinRoot2(filePath, this.materializedProjectRoot)) {
10335
11340
  return filePath;
10336
11341
  }
10337
- return path19.resolve(this.projectRoot, path19.relative(this.materializedProjectRoot, filePath));
11342
+ return path20.resolve(this.projectRoot, path20.relative(this.materializedProjectRoot, filePath));
10338
11343
  }
10339
11344
  toStoredFilePath(filePath) {
10340
11345
  const canonicalFilePath = this.toCanonicalFilePath(filePath);
10341
11346
  if (this.config.scope !== "project" || !isPathWithinRoot2(canonicalFilePath, this.projectRoot)) {
10342
11347
  return canonicalFilePath;
10343
11348
  }
10344
- return path19.relative(this.projectRoot, canonicalFilePath).split(path19.sep).join("/");
11349
+ return path20.relative(this.projectRoot, canonicalFilePath).split(path20.sep).join("/");
11350
+ }
11351
+ isStoredPathExcluded(storedPath) {
11352
+ let matchPath = storedPath.split(path20.sep).join("/");
11353
+ if (path20.isAbsolute(storedPath)) {
11354
+ const relativePath = path20.relative(this.projectRoot, storedPath).split(path20.sep).join("/");
11355
+ if (relativePath.startsWith("..") || path20.isAbsolute(relativePath)) {
11356
+ return false;
11357
+ }
11358
+ matchPath = relativePath;
11359
+ }
11360
+ return isExcludedByPatterns(matchPath, this.config.exclude);
10345
11361
  }
10346
11362
  resolveStoredFilePath(filePath, rootPath = this.projectRoot) {
10347
- if (path19.isAbsolute(filePath)) {
11363
+ if (path20.isAbsolute(filePath)) {
10348
11364
  return filePath;
10349
11365
  }
10350
- const resolvedPath = path19.resolve(rootPath, ...filePath.split("/"));
11366
+ const resolvedPath = path20.resolve(rootPath, ...filePath.split("/"));
10351
11367
  if (!isPathWithinRoot2(resolvedPath, rootPath)) {
10352
11368
  throw new Error(`Stored project path escapes project root: ${JSON.stringify(filePath)}`);
10353
11369
  }
@@ -10371,7 +11387,7 @@ var Indexer = class _Indexer {
10371
11387
  }
10372
11388
  toMaterializedFilePath(filePath) {
10373
11389
  const storedFilePath = this.toStoredFilePath(filePath);
10374
- if (path19.isAbsolute(storedFilePath)) {
11390
+ if (path20.isAbsolute(storedFilePath)) {
10375
11391
  return storedFilePath;
10376
11392
  }
10377
11393
  return this.resolveStoredFilePath(storedFilePath, this.materializedProjectRoot);
@@ -10388,10 +11404,10 @@ var Indexer = class _Indexer {
10388
11404
  }
10389
11405
  getRuntimeArtifactPath(fileName) {
10390
11406
  const namespace = this.getRuntimeArtifactNamespace();
10391
- if (!namespace) return path19.join(this.indexPath, fileName);
10392
- const extension = path19.extname(fileName);
11407
+ if (!namespace) return path20.join(this.indexPath, fileName);
11408
+ const extension = path20.extname(fileName);
10393
11409
  const baseName = fileName.slice(0, fileName.length - extension.length);
10394
- return path19.join(this.indexPath, `${baseName}.${namespace}${extension}`);
11410
+ return path20.join(this.indexPath, `${baseName}.${namespace}${extension}`);
10395
11411
  }
10396
11412
  refreshRuntimeArtifactPaths() {
10397
11413
  this.fileHashCachePath = this.getRuntimeArtifactPath("file-hashes.json");
@@ -10404,14 +11420,14 @@ var Indexer = class _Indexer {
10404
11420
  getMaterializedKnowledgeBases() {
10405
11421
  const canonicalProjectRoot = this.getCanonicalPath(this.projectRoot);
10406
11422
  return this.config.knowledgeBases.map((knowledgeBase) => {
10407
- const configuredPath = path19.isAbsolute(knowledgeBase) ? knowledgeBase : path19.resolve(this.projectRoot, knowledgeBase);
11423
+ const configuredPath = path20.isAbsolute(knowledgeBase) ? knowledgeBase : path20.resolve(this.projectRoot, knowledgeBase);
10408
11424
  const canonicalPath = this.getCanonicalPath(configuredPath);
10409
11425
  if (!isPathWithinRoot2(canonicalPath, canonicalProjectRoot)) {
10410
11426
  return canonicalPath;
10411
11427
  }
10412
- return path19.resolve(
11428
+ return path20.resolve(
10413
11429
  this.materializedProjectRoot,
10414
- path19.relative(canonicalProjectRoot, canonicalPath)
11430
+ path20.relative(canonicalProjectRoot, canonicalPath)
10415
11431
  );
10416
11432
  });
10417
11433
  }
@@ -10419,7 +11435,7 @@ var Indexer = class _Indexer {
10419
11435
  try {
10420
11436
  return canonicalizePathForComparison(targetPath);
10421
11437
  } catch {
10422
- return path19.resolve(targetPath);
11438
+ return path20.resolve(targetPath);
10423
11439
  }
10424
11440
  }
10425
11441
  getProjectIdentityHash(projectRoot3) {
@@ -10545,7 +11561,7 @@ var Indexer = class _Indexer {
10545
11561
  atomicWriteSync(targetPath, data) {
10546
11562
  const lease = this.requireActiveLease();
10547
11563
  const tempPath = createLeaseTemporaryPath(targetPath, lease.owner, "tmp");
10548
- (0, import_fs12.mkdirSync)(path19.dirname(targetPath), { recursive: true });
11564
+ (0, import_fs12.mkdirSync)(path20.dirname(targetPath), { recursive: true });
10549
11565
  try {
10550
11566
  (0, import_fs12.writeFileSync)(tempPath, data);
10551
11567
  (0, import_fs12.renameSync)(tempPath, targetPath);
@@ -10555,14 +11571,14 @@ var Indexer = class _Indexer {
10555
11571
  }
10556
11572
  saveInvertedIndex(invertedIndex) {
10557
11573
  this.atomicWriteSync(
10558
- path19.join(this.indexPath, "inverted-index.json"),
11574
+ path20.join(this.indexPath, "inverted-index.json"),
10559
11575
  invertedIndex.serialize()
10560
11576
  );
10561
11577
  }
10562
11578
  getScopedRoots(projectRoot3 = this.projectRoot) {
10563
11579
  const roots = /* @__PURE__ */ new Set([this.getCanonicalPath(projectRoot3)]);
10564
11580
  for (const kbRoot of this.config.knowledgeBases) {
10565
- roots.add(this.getCanonicalPath(path19.resolve(projectRoot3, kbRoot)));
11581
+ roots.add(this.getCanonicalPath(path20.resolve(projectRoot3, kbRoot)));
10566
11582
  }
10567
11583
  return Array.from(roots);
10568
11584
  }
@@ -10979,7 +11995,7 @@ var Indexer = class _Indexer {
10979
11995
  return configuredProviderInfo !== null && recovery.embeddingProvider === configuredProviderInfo.provider && recovery.embeddingModel === configuredProviderInfo.modelInfo.model && recovery.embeddingDimensions === configuredProviderInfo.modelInfo.dimensions && recovery.embeddingStrategyVersion === EMBEDDING_STRATEGY_VERSION;
10980
11996
  }
10981
11997
  hasUnknownLegacyForceIndexClear(owner) {
10982
- return owner.operation === "force-index" && owner.clearRecovery === void 0 && owner.recoveryProtocolVersion !== 1 && (0, import_fs12.existsSync)(path19.join(this.indexPath, "force-index-phase"));
11998
+ return owner.operation === "force-index" && owner.clearRecovery === void 0 && owner.recoveryProtocolVersion !== 1 && (0, import_fs12.existsSync)(path20.join(this.indexPath, "force-index-phase"));
10983
11999
  }
10984
12000
  async recoverFromInterruptedIndexingUnlocked(owners) {
10985
12001
  for (const owner of owners) {
@@ -11367,7 +12383,7 @@ var Indexer = class _Indexer {
11367
12383
  await options.queue.onSizeLessThan(Math.max(1, options.providerRateLimits.concurrency));
11368
12384
  const task = options.queue.add(async () => {
11369
12385
  if (options.rateLimitState.backoffMs > 0) {
11370
- await new Promise((resolve17) => setTimeout(resolve17, options.rateLimitState.backoffMs));
12386
+ await new Promise((resolve18) => setTimeout(resolve18, options.rateLimitState.backoffMs));
11371
12387
  }
11372
12388
  try {
11373
12389
  const embeddingResult = await pRetry(
@@ -11786,12 +12802,12 @@ var Indexer = class _Indexer {
11786
12802
  }
11787
12803
  }
11788
12804
  captureReaderArtifactFingerprint() {
11789
- const storePath = path19.join(this.indexPath, "vectors");
12805
+ const storePath = path20.join(this.indexPath, "vectors");
11790
12806
  return {
11791
12807
  vectors: `${this.getReaderFileFingerprint(storePath)}|${this.getReaderFileFingerprint(`${storePath}.meta.json`)}`,
11792
- keyword: this.getReaderFileFingerprint(path19.join(this.indexPath, "inverted-index.json")),
11793
- database: this.getReaderFileFingerprint(path19.join(this.indexPath, "codebase.db")),
11794
- databaseIdentity: this.getReaderFileFingerprint(path19.join(this.indexPath, "codebase.db"), true)
12808
+ keyword: this.getReaderFileFingerprint(path20.join(this.indexPath, "inverted-index.json")),
12809
+ database: this.getReaderFileFingerprint(path20.join(this.indexPath, "codebase.db")),
12810
+ databaseIdentity: this.getReaderFileFingerprint(path20.join(this.indexPath, "codebase.db"), true)
11795
12811
  };
11796
12812
  }
11797
12813
  refreshReaderArtifacts() {
@@ -11816,10 +12832,10 @@ var Indexer = class _Indexer {
11816
12832
  issues.set(component, this.createReadIssue(component, message));
11817
12833
  this.readerArtifactRetryAfter.set(component, Date.now() + READER_ARTIFACT_RETRY_INTERVAL_MS);
11818
12834
  };
11819
- const storePath = path19.join(this.indexPath, "vectors");
12835
+ const storePath = path20.join(this.indexPath, "vectors");
11820
12836
  const vectorMetadataPath = `${storePath}.meta.json`;
11821
- const invertedIndexPath = path19.join(this.indexPath, "inverted-index.json");
11822
- const dbPath = path19.join(this.indexPath, "codebase.db");
12837
+ const invertedIndexPath = path20.join(this.indexPath, "inverted-index.json");
12838
+ const dbPath = path20.join(this.indexPath, "codebase.db");
11823
12839
  if (vectorsChanged || retryDue("vectors")) {
11824
12840
  const vectorStoreExists = (0, import_fs12.existsSync)(storePath);
11825
12841
  const vectorMetadataExists = (0, import_fs12.existsSync)(vectorMetadataPath);
@@ -11935,10 +12951,10 @@ var Indexer = class _Indexer {
11935
12951
  });
11936
12952
  this.provider = createEmbeddingProvider(this.configuredProviderInfo);
11937
12953
  const dimensions = this.configuredProviderInfo.modelInfo.dimensions;
11938
- const storePath = path19.join(this.indexPath, "vectors");
12954
+ const storePath = path20.join(this.indexPath, "vectors");
11939
12955
  const vectorMetadataPath = `${storePath}.meta.json`;
11940
- const invertedIndexPath = path19.join(this.indexPath, "inverted-index.json");
11941
- const dbPath = path19.join(this.indexPath, "codebase.db");
12956
+ const invertedIndexPath = path20.join(this.indexPath, "inverted-index.json");
12957
+ const dbPath = path20.join(this.indexPath, "codebase.db");
11942
12958
  let dbIsNew = !(0, import_fs12.existsSync)(dbPath);
11943
12959
  const readerArtifactFingerprint = mode === "reader" ? this.captureReaderArtifactFingerprint() : null;
11944
12960
  if (mode === "writer") {
@@ -12116,7 +13132,7 @@ var Indexer = class _Indexer {
12116
13132
  if (await this.tryResetCorruptedIndex("running automatic orphan garbage collection", error)) {
12117
13133
  return {
12118
13134
  resetCorruptedIndex: true,
12119
- warning: this.getCorruptedIndexWarning(path19.join(this.indexPath, "codebase.db"))
13135
+ warning: this.getCorruptedIndexWarning(path20.join(this.indexPath, "codebase.db"))
12120
13136
  };
12121
13137
  }
12122
13138
  throw error;
@@ -12131,7 +13147,7 @@ var Indexer = class _Indexer {
12131
13147
  return;
12132
13148
  }
12133
13149
  const retainedEntries = store.getAllMetadata().filter(({ key }) => !excludedSet.has(key));
12134
- const storeBasePath = path19.join(this.indexPath, "vectors");
13150
+ const storeBasePath = path20.join(this.indexPath, "vectors");
12135
13151
  const storeIndexPath = storeBasePath;
12136
13152
  const storeMetadataPath = `${storeBasePath}.meta.json`;
12137
13153
  const lease = this.requireActiveLease();
@@ -12218,7 +13234,7 @@ var Indexer = class _Indexer {
12218
13234
  const names = await import_fs12.promises.readdir(this.indexPath);
12219
13235
  const runtimeStatePattern = /^(?:file-hashes|failed-batches)(?:\.[a-f0-9]{16})?\.json$/;
12220
13236
  await Promise.all(
12221
- names.filter((name) => runtimeStatePattern.test(name)).map((name) => import_fs12.promises.rm(path19.join(this.indexPath, name), { force: true }))
13237
+ names.filter((name) => runtimeStatePattern.test(name)).map((name) => import_fs12.promises.rm(path20.join(this.indexPath, name), { force: true }))
12222
13238
  );
12223
13239
  }
12224
13240
  async resetLocalIndexArtifacts() {
@@ -12234,13 +13250,13 @@ var Indexer = class _Indexer {
12234
13250
  this.readerArtifactRetryAfter.clear();
12235
13251
  this.fileHashCache.clear();
12236
13252
  const resetPaths = [
12237
- path19.join(this.indexPath, "codebase.db"),
12238
- path19.join(this.indexPath, "codebase.db-shm"),
12239
- path19.join(this.indexPath, "codebase.db-wal"),
12240
- path19.join(this.indexPath, "vectors"),
12241
- path19.join(this.indexPath, "vectors.usearch"),
12242
- path19.join(this.indexPath, "vectors.meta.json"),
12243
- path19.join(this.indexPath, "inverted-index.json")
13253
+ path20.join(this.indexPath, "codebase.db"),
13254
+ path20.join(this.indexPath, "codebase.db-shm"),
13255
+ path20.join(this.indexPath, "codebase.db-wal"),
13256
+ path20.join(this.indexPath, "vectors"),
13257
+ path20.join(this.indexPath, "vectors.usearch"),
13258
+ path20.join(this.indexPath, "vectors.meta.json"),
13259
+ path20.join(this.indexPath, "inverted-index.json")
12244
13260
  ];
12245
13261
  await Promise.all(resetPaths.map((targetPath) => import_fs12.promises.rm(targetPath, { recursive: true, force: true })));
12246
13262
  await this.removeProjectRuntimeStateArtifacts();
@@ -12250,7 +13266,7 @@ var Indexer = class _Indexer {
12250
13266
  if (!isSqliteCorruptionError(error)) {
12251
13267
  return false;
12252
13268
  }
12253
- const dbPath = path19.join(this.indexPath, "codebase.db");
13269
+ const dbPath = path20.join(this.indexPath, "codebase.db");
12254
13270
  const warning = this.getCorruptedIndexWarning(dbPath);
12255
13271
  const errorMessage = getErrorMessage4(error);
12256
13272
  if (this.config.scope === "global") {
@@ -12484,6 +13500,70 @@ var Indexer = class _Indexer {
12484
13500
  );
12485
13501
  return createCostEstimate(files, configuredProviderInfo);
12486
13502
  }
13503
+ // Dry-run counterpart to index()/forceIndex(): parse the real file set and sum
13504
+ // estimateTokens over the embedding text of every indexable chunk, without
13505
+ // calling the embedding provider or writing to the index. Read-only and
13506
+ // lock-free (mirrors estimateCost). The token sum is the exact value "Tokens
13507
+ // used" climbs to for a force index (cache bypassed); for an incremental it is
13508
+ // an upper bound because cached chunks are counted here but not re-embedded.
13509
+ // Used by index_codebase(dryRun:true) to give a stable, monotonic progress
13510
+ // denominator that matches the live "Tokens used" basis.
13511
+ async dryRunCost() {
13512
+ const { configuredProviderInfo } = await this.ensureInitialized();
13513
+ const maxChunkTokens = getSafeEmbeddingChunkTokenLimit(configuredProviderInfo);
13514
+ const includePatterns = [...this.config.include, ...this.config.additionalInclude];
13515
+ const { files } = await collectFiles(
13516
+ this.materializedProjectRoot,
13517
+ includePatterns,
13518
+ this.config.exclude,
13519
+ this.config.indexing.maxFileSize,
13520
+ this.getMaterializedKnowledgeBases(),
13521
+ { maxDepth: this.config.indexing.maxDepth, maxFilesPerDirectory: this.config.indexing.maxFilesPerDirectory }
13522
+ );
13523
+ let filesCount = 0;
13524
+ let chunksCount = 0;
13525
+ let tokensToEmbed = 0;
13526
+ for (const batch of iterateOrderedFileBatches(files, (f) => f.size, this.fileBatchLimits)) {
13527
+ const loadedFiles = await Promise.all(batch.map(async (f) => {
13528
+ try {
13529
+ return {
13530
+ path: this.toStoredFilePath(f.path),
13531
+ content: await import_fs12.promises.readFile(f.path, "utf-8")
13532
+ };
13533
+ } catch {
13534
+ return null;
13535
+ }
13536
+ }));
13537
+ const readable = loadedFiles.filter(
13538
+ (f) => f !== null
13539
+ );
13540
+ filesCount += readable.length;
13541
+ const contentByPath = new Map(readable.map((f) => [f.path, f.content]));
13542
+ const parsedFiles = parseFiles(readable, this.config.indexing.linesPerChunk);
13543
+ for (const parsed of parsedFiles) {
13544
+ let chunksToProcess = parsed.chunks;
13545
+ if (this.config.indexing.fallbackToTextOnMaxChunks && chunksToProcess.length > this.config.indexing.maxChunksPerFile) {
13546
+ const content = contentByPath.get(parsed.path);
13547
+ if (content !== void 0) {
13548
+ chunksToProcess = parseFileAsText(parsed.path, content, this.config.indexing.linesPerChunk);
13549
+ }
13550
+ }
13551
+ chunksToProcess = selectIndexableChunks(
13552
+ chunksToProcess,
13553
+ this.config.indexing.maxChunksPerFile,
13554
+ this.config.indexing.semanticOnly
13555
+ );
13556
+ for (const chunk of chunksToProcess) {
13557
+ const texts = createEmbeddingTexts(chunk, parsed.path, maxChunkTokens);
13558
+ chunksCount += 1;
13559
+ for (const text3 of texts) {
13560
+ tokensToEmbed += estimateTokens(text3);
13561
+ }
13562
+ }
13563
+ }
13564
+ }
13565
+ return { filesCount, chunksCount, tokensToEmbed };
13566
+ }
12487
13567
  async index(onProgress) {
12488
13568
  return this.withIndexMutationLease("index", async (recoveredOwners) => {
12489
13569
  return this.indexUnlocked(onProgress, recoveredOwners);
@@ -12573,10 +13653,10 @@ var Indexer = class _Indexer {
12573
13653
  const reparseCachedMetalFiles = database.getMetadata(metalParserMetadataKey) !== METAL_PARSER_VERSION;
12574
13654
  const symbolExtractorMetadataKey = this.getSymbolExtractorVersionMetadataKey();
12575
13655
  const refreshCachedSymbols = database.getMetadata(symbolExtractorMetadataKey) !== SYMBOL_EXTRACTOR_VERSION;
12576
- if (reparseCachedSwiftFiles && Array.from(this.fileHashCache.keys()).some((filePath) => path19.extname(filePath).toLowerCase() === ".swift")) {
13656
+ if (reparseCachedSwiftFiles && Array.from(this.fileHashCache.keys()).some((filePath) => path20.extname(filePath).toLowerCase() === ".swift")) {
12577
13657
  this.logger.info("Reindexing cached Swift files for parser support");
12578
13658
  }
12579
- if (reparseCachedMetalFiles && Array.from(this.fileHashCache.keys()).some((filePath) => path19.extname(filePath).toLowerCase() === ".metal")) {
13659
+ if (reparseCachedMetalFiles && Array.from(this.fileHashCache.keys()).some((filePath) => path20.extname(filePath).toLowerCase() === ".metal")) {
12580
13660
  this.logger.info("Reindexing cached Metal files for parser support");
12581
13661
  }
12582
13662
  const includePatterns = [...this.config.include, ...this.config.additionalInclude];
@@ -12620,8 +13700,8 @@ var Indexer = class _Indexer {
12620
13700
  const needsCallGraphRefresh = cachedHashMatches && needsCallGraphResolutionMigration && database.getChunksByFile(storedPath).some(
12621
13701
  (chunk) => chunk.language === "php" || chunk.language === "c" || chunk.language === "cpp"
12622
13702
  );
12623
- const requiresSwiftParserUpgrade = reparseCachedSwiftFiles && path19.extname(storedPath).toLowerCase() === ".swift";
12624
- const requiresMetalParserUpgrade = reparseCachedMetalFiles && path19.extname(storedPath).toLowerCase() === ".metal";
13703
+ const requiresSwiftParserUpgrade = reparseCachedSwiftFiles && path20.extname(storedPath).toLowerCase() === ".swift";
13704
+ const requiresMetalParserUpgrade = reparseCachedMetalFiles && path20.extname(storedPath).toLowerCase() === ".metal";
12625
13705
  const inMigrationScope = forceScopedReembed && scopedRoots !== null && this.isFileInCurrentScope(storedPath, scopedRoots);
12626
13706
  if (cachedHashMatches && !inMigrationScope && !needsCallGraphRefresh && !requiresSwiftParserUpgrade && !requiresMetalParserUpgrade && !refreshCachedSymbols) {
12627
13707
  unchangedFilePaths.add(storedPath);
@@ -12680,7 +13760,7 @@ var Indexer = class _Indexer {
12680
13760
  }
12681
13761
  }
12682
13762
  }
12683
- const shouldRetryFailedPath = (filePath) => filePath !== null && currentFileHashes.has(filePath) && unchangedFilePaths.has(filePath);
13763
+ const shouldRetryFailedPath = (filePath) => filePath !== null && !this.isStoredPathExcluded(filePath) && currentFileHashes.has(filePath) && unchangedFilePaths.has(filePath);
12684
13764
  const failedProcessing = this.prepareFailedBatchProcessing(scopedRoots, shouldRetryFailedPath);
12685
13765
  const maxChunkTokens = getSafeEmbeddingChunkTokenLimit(configuredProviderInfo);
12686
13766
  const providerRateLimits = this.getProviderRateLimits(configuredProviderInfo.provider);
@@ -12784,7 +13864,7 @@ var Indexer = class _Indexer {
12784
13864
  throw new Error(`Parsed file was not present in its source batch: ${parsed.path}`);
12785
13865
  }
12786
13866
  if (parsed.chunks.length === 0) {
12787
- stats.parseFailures.push(path19.isAbsolute(parsed.path) ? path19.relative(this.projectRoot, parsed.path) : parsed.path);
13867
+ stats.parseFailures.push(path20.isAbsolute(parsed.path) ? path20.relative(this.projectRoot, parsed.path) : parsed.path);
12788
13868
  }
12789
13869
  let chunksToProcess = parsed.chunks;
12790
13870
  if (this.config.indexing.fallbackToTextOnMaxChunks && chunksToProcess.length > this.config.indexing.maxChunksPerFile) {
@@ -13119,7 +14199,7 @@ var Indexer = class _Indexer {
13119
14199
  previousBranchSymbolIds,
13120
14200
  Array.from(allSymbolIds)
13121
14201
  );
13122
- const vectorPath = path19.join(this.indexPath, "vectors");
14202
+ const vectorPath = path20.join(this.indexPath, "vectors");
13123
14203
  const shouldFingerprintLegacyPair = !store.hasFingerprint() && (0, import_fs12.existsSync)(vectorPath) && (0, import_fs12.existsSync)(`${vectorPath}.meta.json`);
13124
14204
  if (backfilledBlameMetadata || shouldFingerprintLegacyPair || removedStoredChunks) {
13125
14205
  store.save();
@@ -13978,7 +15058,7 @@ var Indexer = class _Indexer {
13978
15058
  gcOrphanSymbols: 0,
13979
15059
  gcOrphanCallEdges: 0,
13980
15060
  resetCorruptedIndex: true,
13981
- warning: this.getCorruptedIndexWarning(path19.join(this.indexPath, "codebase.db"))
15061
+ warning: this.getCorruptedIndexWarning(path20.join(this.indexPath, "codebase.db"))
13982
15062
  };
13983
15063
  }
13984
15064
  this.logger.recordGc(removedCount, gcOrphanChunks, gcOrphanEmbeddings);
@@ -14008,7 +15088,8 @@ var Indexer = class _Indexer {
14008
15088
  const maxChunkTokens = getSafeEmbeddingChunkTokenLimit(configuredProviderInfo);
14009
15089
  const providerRateLimits = this.getProviderRateLimits(configuredProviderInfo.provider);
14010
15090
  const roots = this.config.scope === "global" ? this.getScopedRoots() : null;
14011
- const failedProcessing = this.prepareFailedBatchProcessing(roots, () => true);
15091
+ const shouldProcessFailedPath = (filePath) => filePath === null || !this.isStoredPathExcluded(filePath);
15092
+ const failedProcessing = this.prepareFailedBatchProcessing(roots, shouldProcessFailedPath);
14012
15093
  if (failedProcessing.latestById.size === 0) {
14013
15094
  this.finalizeFailedBatchWriteState(failedProcessing.state);
14014
15095
  return { succeeded: 0, failed: 0, remaining: 0 };
@@ -14021,7 +15102,7 @@ var Indexer = class _Indexer {
14021
15102
  const retryableChunks = this.iterateLatestFailedChunks(
14022
15103
  failedProcessing.latestById,
14023
15104
  roots,
14024
- () => true,
15105
+ shouldProcessFailedPath,
14025
15106
  maxChunkTokens
14026
15107
  );
14027
15108
  for (const retryBatch of iterateOrderedFileBatches(
@@ -14291,9 +15372,9 @@ var Indexer = class _Indexer {
14291
15372
  this.requireReadableComponents(readIssues, "database");
14292
15373
  let shortest = [];
14293
15374
  for (const branchKey of this.getBranchCatalogKeys()) {
14294
- const path25 = database.findShortestPath(fromName, toName, branchKey, maxDepth);
14295
- if (path25.length > 0 && (shortest.length === 0 || path25.length < shortest.length)) {
14296
- shortest = path25;
15375
+ const path26 = database.findShortestPath(fromName, toName, branchKey, maxDepth);
15376
+ if (path26.length > 0 && (shortest.length === 0 || path26.length < shortest.length)) {
15377
+ shortest = path26;
14297
15378
  }
14298
15379
  }
14299
15380
  return shortest.map((hop) => this.resolveFilePathRecord(hop));
@@ -14341,13 +15422,13 @@ var Indexer = class _Indexer {
14341
15422
  }
14342
15423
  }
14343
15424
  if (!found) continue;
14344
- const path25 = [];
15425
+ const path26 = [];
14345
15426
  let currentSymbolId = toSymbolId;
14346
15427
  while (true) {
14347
15428
  const symbol = symbolsById.get(currentSymbolId);
14348
15429
  if (!symbol) break;
14349
15430
  const parent = parentBySymbolId.get(currentSymbolId);
14350
- path25.push({
15431
+ path26.push({
14351
15432
  symbolId: symbol.id,
14352
15433
  symbolName: symbol.name,
14353
15434
  filePath: symbol.filePath,
@@ -14357,9 +15438,9 @@ var Indexer = class _Indexer {
14357
15438
  if (!parent) break;
14358
15439
  currentSymbolId = parent.parentId;
14359
15440
  }
14360
- path25.reverse();
14361
- if (path25.length > 0 && (shortest.length === 0 || path25.length < shortest.length)) {
14362
- shortest = path25;
15441
+ path26.reverse();
15442
+ if (path26.length > 0 && (shortest.length === 0 || path26.length < shortest.length)) {
15443
+ shortest = path26;
14363
15444
  }
14364
15445
  }
14365
15446
  return shortest.map((hop) => this.resolveFilePathRecord(hop));
@@ -14510,7 +15591,7 @@ var Indexer = class _Indexer {
14510
15591
  );
14511
15592
  }
14512
15593
  }
14513
- const toStoredChangedFiles = (filePaths) => filePaths.map((filePath) => this.toStoredFilePath(path19.resolve(this.projectRoot, filePath)));
15594
+ const toStoredChangedFiles = (filePaths) => filePaths.map((filePath) => this.toStoredFilePath(path20.resolve(this.projectRoot, filePath)));
14514
15595
  const storedChangedFiles = toStoredChangedFiles(changedFiles);
14515
15596
  const directSymbols = database.getSymbolsForFiles(storedChangedFiles, branchKey);
14516
15597
  const directIds = directSymbols.map((s) => s.id);
@@ -14659,12 +15740,12 @@ var Indexer = class _Indexer {
14659
15740
  if (meta.filePath) filePaths.add(meta.filePath);
14660
15741
  }
14661
15742
  const directory = options?.directory?.replace(/\/$/, "");
14662
- const absoluteDirectoryFilter = directory ? path19.resolve(this.projectRoot, directory) : void 0;
15743
+ const absoluteDirectoryFilter = directory ? path20.resolve(this.projectRoot, directory) : void 0;
14663
15744
  for (const filePath of filePaths) {
14664
15745
  if (directory) {
14665
15746
  const absoluteFilePath = this.resolveStoredFilePath(filePath);
14666
15747
  const matchesRelative = filePath === directory || filePath.startsWith(directory + "/");
14667
- const matchesProjectRelative = absoluteDirectoryFilter !== void 0 && (absoluteFilePath === absoluteDirectoryFilter || absoluteFilePath.startsWith(absoluteDirectoryFilter + path19.sep));
15748
+ const matchesProjectRelative = absoluteDirectoryFilter !== void 0 && (absoluteFilePath === absoluteDirectoryFilter || absoluteFilePath.startsWith(absoluteDirectoryFilter + path20.sep));
14668
15749
  if (!matchesRelative && !matchesProjectRelative) {
14669
15750
  continue;
14670
15751
  }
@@ -14781,7 +15862,10 @@ function getOrCreateIndexer(projectRoot3, host) {
14781
15862
  }
14782
15863
  const indexer = new Indexer(projectRoot3, config, host);
14783
15864
  indexerCache.set(key, indexer);
14784
- configureAutoIndex(projectRoot3, host, config, () => getOrCreateIndexer(projectRoot3, host));
15865
+ configureAutoIndex(projectRoot3, host, config, () => getOrCreateIndexer(projectRoot3, host), {
15866
+ preserveManagedWorker: true,
15867
+ synchronizeBackgroundWorker: false
15868
+ });
14785
15869
  return indexer;
14786
15870
  }
14787
15871
  function getIndexerForProject(projectRoot3, host) {
@@ -14796,7 +15880,9 @@ function refreshIndexerForDirectory(projectRoot3, host, config = parseConfig(loa
14796
15880
  const key = getIndexerCacheKey(projectRoot3, host);
14797
15881
  configCache.set(key, config);
14798
15882
  indexerCache.set(key, new Indexer(projectRoot3, config, host));
14799
- configureAutoIndex(projectRoot3, host, config, () => getOrCreateIndexer(projectRoot3, host));
15883
+ configureAutoIndex(projectRoot3, host, config, () => getOrCreateIndexer(projectRoot3, host), {
15884
+ synchronizeBackgroundWorker: true
15885
+ });
14800
15886
  return config;
14801
15887
  }
14802
15888
  var AutoIndexRetrievalUnavailableError = class extends Error {
@@ -14823,7 +15909,7 @@ function trimOrUndefined(value) {
14823
15909
  return normalized || void 0;
14824
15910
  }
14825
15911
  function normalizeCallGraphPath(value) {
14826
- let normalized = path20.posix.normalize(value.trim().replaceAll("\\", "/"));
15912
+ let normalized = path21.posix.normalize(value.trim().replaceAll("\\", "/"));
14827
15913
  if (normalized.startsWith("./")) {
14828
15914
  normalized = normalized.slice(2);
14829
15915
  }
@@ -15016,12 +16102,12 @@ async function getCallGraphPath(projectRoot3, host, from, to, maxDepth, fromFile
15016
16102
  if (fromResolution.status !== "resolved" || toResolution.status !== "resolved") {
15017
16103
  return { from: fromResolution, to: toResolution, path: [] };
15018
16104
  }
15019
- const path25 = await indexer.findCallPathBySymbolIds(
16105
+ const path26 = await indexer.findCallPathBySymbolIds(
15020
16106
  fromResolution.symbolId,
15021
16107
  toResolution.symbolId,
15022
16108
  maxDepth
15023
16109
  );
15024
- return { from: fromResolution, to: toResolution, path: path25 };
16110
+ return { from: fromResolution, to: toResolution, path: path26 };
15025
16111
  }
15026
16112
  async function runIndexCodebase(projectRoot3, host, args, onProgress) {
15027
16113
  const root = getProjectRoot(projectRoot3, host);
@@ -15030,6 +16116,9 @@ async function runIndexCodebase(projectRoot3, host, args, onProgress) {
15030
16116
  if (args.estimateOnly) {
15031
16117
  return { kind: "estimate", estimate: await indexer.estimateCost() };
15032
16118
  }
16119
+ if (args.dryRun) {
16120
+ return { kind: "dryrun", dryrun: await indexer.dryRunCost() };
16121
+ }
15033
16122
  const coordinated = runCoordinatedIndex(root, host, args.force ?? false, (progress) => {
15034
16123
  if (onProgress) {
15035
16124
  void onProgress(formatProgressTitle(progress), {
@@ -15216,8 +16305,8 @@ async function getIndexLogs(projectRoot3, host, args) {
15216
16305
  function addKnowledgeBase(projectRoot3, host, knowledgeBasePath) {
15217
16306
  const root = getProjectRoot(projectRoot3, host);
15218
16307
  const inputPath = knowledgeBasePath.trim();
15219
- const normalizedPath2 = path20.resolve(
15220
- path20.isAbsolute(inputPath) ? inputPath : resolveKnowledgeBasePath(inputPath, root)
16308
+ const normalizedPath2 = path21.resolve(
16309
+ path21.isAbsolute(inputPath) ? inputPath : resolveKnowledgeBasePath(inputPath, root)
15221
16310
  );
15222
16311
  if (!(0, import_fs13.existsSync)(normalizedPath2)) {
15223
16312
  return `Error: Directory does not exist: ${normalizedPath2}`;
@@ -15253,7 +16342,7 @@ function addKnowledgeBase(projectRoot3, host, knowledgeBasePath) {
15253
16342
  }
15254
16343
  }
15255
16344
  for (const dotDir of sensitiveDotDirs) {
15256
- const sensitiveDir = path20.join(homeDir, dotDir);
16345
+ const sensitiveDir = path21.join(homeDir, dotDir);
15257
16346
  if (sensitiveDir && (realPath === sensitiveDir || realPath.startsWith(`${sensitiveDir}/`))) {
15258
16347
  return `Error: Adding sensitive directory as knowledge base is not allowed: ${normalizedPath2}`;
15259
16348
  }
@@ -15316,7 +16405,7 @@ function listKnowledgeBases(projectRoot3, host) {
15316
16405
  }
15317
16406
  result += "\n";
15318
16407
  }
15319
- const hasHostConfig = (0, import_fs13.existsSync)(path20.join(root, getHostProjectConfigRelativePath(host)));
16408
+ const hasHostConfig = (0, import_fs13.existsSync)(path21.join(root, getHostProjectConfigRelativePath(host)));
15320
16409
  if (hasHostConfig) {
15321
16410
  result += `
15322
16411
  Config sources: 1 file(s).`;
@@ -15789,7 +16878,7 @@ async function resolveCodebaseContextUnmeasured(projectRoot3, host, input) {
15789
16878
  const directory = input.directory ?? void 0;
15790
16879
  const tokenBudget = input.tokenBudget ?? void 0;
15791
16880
  if (from && to) {
15792
- const path25 = await getCallGraphPath(
16881
+ const path26 = await getCallGraphPath(
15793
16882
  projectRoot3,
15794
16883
  host,
15795
16884
  from,
@@ -15798,25 +16887,25 @@ async function resolveCodebaseContextUnmeasured(projectRoot3, host, input) {
15798
16887
  fromFilePath,
15799
16888
  toFilePath
15800
16889
  );
15801
- const pathText = formatCallGraphPathResult(path25);
15802
- if (path25.path.length > 0) {
16890
+ const pathText = formatCallGraphPathResult(path26);
16891
+ if (path26.path.length > 0) {
15803
16892
  const fitted2 = fitTextToContextBudget(
15804
16893
  pathText,
15805
16894
  tokenBudget
15806
16895
  );
15807
16896
  return {
15808
16897
  text: fitted2.text,
15809
- details: fittedDetails("path", fitted2, path25.path.length)
16898
+ details: fittedDetails("path", fitted2, path26.path.length)
15810
16899
  };
15811
16900
  }
15812
- if (path25.from.status !== "resolved" || path25.to.status !== "resolved") {
16901
+ if (path26.from.status !== "resolved" || path26.to.status !== "resolved") {
15813
16902
  const fitted2 = fitTextToContextBudget(pathText, tokenBudget);
15814
16903
  return {
15815
16904
  text: fitted2.text,
15816
16905
  details: fittedDetails("path", fitted2, 0)
15817
16906
  };
15818
16907
  }
15819
- const resolvedFrom = path25.from;
16908
+ const resolvedFrom = path26.from;
15820
16909
  const { callers } = await getCallGraphData(projectRoot3, host, {
15821
16910
  name: to,
15822
16911
  direction: "callers",
@@ -16261,7 +17350,7 @@ var import_fs14 = require("fs");
16261
17350
 
16262
17351
  // node_modules/chokidar/index.js
16263
17352
  var import_node_events = require("events");
16264
- var import_node_fs2 = require("fs");
17353
+ var import_node_fs3 = require("fs");
16265
17354
  var import_promises3 = require("fs/promises");
16266
17355
  var sp2 = __toESM(require("path"), 1);
16267
17356
 
@@ -16349,7 +17438,7 @@ var ReaddirpStream = class extends import_node_stream.Readable {
16349
17438
  this._directoryFilter = normalizeFilter(opts.directoryFilter);
16350
17439
  const statMethod = opts.lstat ? import_promises.lstat : import_promises.stat;
16351
17440
  if (wantBigintFsStats) {
16352
- this._stat = (path25) => statMethod(path25, { bigint: true });
17441
+ this._stat = (path26) => statMethod(path26, { bigint: true });
16353
17442
  } else {
16354
17443
  this._stat = statMethod;
16355
17444
  }
@@ -16374,8 +17463,8 @@ var ReaddirpStream = class extends import_node_stream.Readable {
16374
17463
  const par = this.parent;
16375
17464
  const fil = par && par.files;
16376
17465
  if (fil && fil.length > 0) {
16377
- const { path: path25, depth } = par;
16378
- const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path25));
17466
+ const { path: path26, depth } = par;
17467
+ const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path26));
16379
17468
  const awaited = await Promise.all(slice);
16380
17469
  for (const entry of awaited) {
16381
17470
  if (!entry)
@@ -16415,21 +17504,21 @@ var ReaddirpStream = class extends import_node_stream.Readable {
16415
17504
  this.reading = false;
16416
17505
  }
16417
17506
  }
16418
- async _exploreDir(path25, depth) {
17507
+ async _exploreDir(path26, depth) {
16419
17508
  let files;
16420
17509
  try {
16421
- files = await (0, import_promises.readdir)(path25, this._rdOptions);
17510
+ files = await (0, import_promises.readdir)(path26, this._rdOptions);
16422
17511
  } catch (error) {
16423
17512
  this._onError(error);
16424
17513
  }
16425
- return { files, depth, path: path25 };
17514
+ return { files, depth, path: path26 };
16426
17515
  }
16427
- async _formatEntry(dirent, path25) {
17516
+ async _formatEntry(dirent, path26) {
16428
17517
  let entry;
16429
- const basename7 = this._isDirent ? dirent.name : dirent;
17518
+ const basename8 = this._isDirent ? dirent.name : dirent;
16430
17519
  try {
16431
- const fullPath = (0, import_node_path.resolve)((0, import_node_path.join)(path25, basename7));
16432
- entry = { path: (0, import_node_path.relative)(this._root, fullPath), fullPath, basename: basename7 };
17520
+ const fullPath = (0, import_node_path.resolve)((0, import_node_path.join)(path26, basename8));
17521
+ entry = { path: (0, import_node_path.relative)(this._root, fullPath), fullPath, basename: basename8 };
16433
17522
  entry[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath);
16434
17523
  } catch (err) {
16435
17524
  this._onError(err);
@@ -16499,7 +17588,7 @@ function readdirp(root, options = {}) {
16499
17588
  }
16500
17589
 
16501
17590
  // node_modules/chokidar/handler.js
16502
- var import_node_fs = require("fs");
17591
+ var import_node_fs2 = require("fs");
16503
17592
  var import_promises2 = require("fs/promises");
16504
17593
  var import_node_os = require("os");
16505
17594
  var sp = __toESM(require("path"), 1);
@@ -16828,16 +17917,16 @@ var delFromSet = (main, prop, item) => {
16828
17917
  };
16829
17918
  var isEmptySet = (val) => val instanceof Set ? val.size === 0 : !val;
16830
17919
  var FsWatchInstances = /* @__PURE__ */ new Map();
16831
- function createFsWatchInstance(path25, options, listener, errHandler, emitRaw) {
17920
+ function createFsWatchInstance(path26, options, listener, errHandler, emitRaw) {
16832
17921
  const handleEvent = (rawEvent, evPath) => {
16833
- listener(path25);
16834
- emitRaw(rawEvent, evPath, { watchedPath: path25 });
16835
- if (evPath && path25 !== evPath) {
16836
- fsWatchBroadcast(sp.resolve(path25, evPath), KEY_LISTENERS, sp.join(path25, evPath));
17922
+ listener(path26);
17923
+ emitRaw(rawEvent, evPath, { watchedPath: path26 });
17924
+ if (evPath && path26 !== evPath) {
17925
+ fsWatchBroadcast(sp.resolve(path26, evPath), KEY_LISTENERS, sp.join(path26, evPath));
16837
17926
  }
16838
17927
  };
16839
17928
  try {
16840
- return (0, import_node_fs.watch)(path25, {
17929
+ return (0, import_node_fs2.watch)(path26, {
16841
17930
  persistent: options.persistent
16842
17931
  }, handleEvent);
16843
17932
  } catch (error) {
@@ -16853,12 +17942,12 @@ var fsWatchBroadcast = (fullPath, listenerType, val1, val2, val3) => {
16853
17942
  listener(val1, val2, val3);
16854
17943
  });
16855
17944
  };
16856
- var setFsWatchListener = (path25, fullPath, options, handlers) => {
17945
+ var setFsWatchListener = (path26, fullPath, options, handlers) => {
16857
17946
  const { listener, errHandler, rawEmitter } = handlers;
16858
17947
  let cont = FsWatchInstances.get(fullPath);
16859
17948
  let watcher;
16860
17949
  if (!options.persistent) {
16861
- watcher = createFsWatchInstance(path25, options, listener, errHandler, rawEmitter);
17950
+ watcher = createFsWatchInstance(path26, options, listener, errHandler, rawEmitter);
16862
17951
  if (!watcher)
16863
17952
  return;
16864
17953
  return watcher.close.bind(watcher);
@@ -16869,7 +17958,7 @@ var setFsWatchListener = (path25, fullPath, options, handlers) => {
16869
17958
  addAndConvert(cont, KEY_RAW, rawEmitter);
16870
17959
  } else {
16871
17960
  watcher = createFsWatchInstance(
16872
- path25,
17961
+ path26,
16873
17962
  options,
16874
17963
  fsWatchBroadcast.bind(null, fullPath, KEY_LISTENERS),
16875
17964
  errHandler,
@@ -16884,7 +17973,7 @@ var setFsWatchListener = (path25, fullPath, options, handlers) => {
16884
17973
  cont.watcherUnusable = true;
16885
17974
  if (isWindows && error.code === "EPERM") {
16886
17975
  try {
16887
- const fd = await (0, import_promises2.open)(path25, "r");
17976
+ const fd = await (0, import_promises2.open)(path26, "r");
16888
17977
  await fd.close();
16889
17978
  broadcastErr(error);
16890
17979
  } catch (err) {
@@ -16915,12 +18004,12 @@ var setFsWatchListener = (path25, fullPath, options, handlers) => {
16915
18004
  };
16916
18005
  };
16917
18006
  var FsWatchFileInstances = /* @__PURE__ */ new Map();
16918
- var setFsWatchFileListener = (path25, fullPath, options, handlers) => {
18007
+ var setFsWatchFileListener = (path26, fullPath, options, handlers) => {
16919
18008
  const { listener, rawEmitter } = handlers;
16920
18009
  let cont = FsWatchFileInstances.get(fullPath);
16921
18010
  const copts = cont && cont.options;
16922
18011
  if (copts && (copts.persistent < options.persistent || copts.interval > options.interval)) {
16923
- (0, import_node_fs.unwatchFile)(fullPath);
18012
+ (0, import_node_fs2.unwatchFile)(fullPath);
16924
18013
  cont = void 0;
16925
18014
  }
16926
18015
  if (cont) {
@@ -16931,13 +18020,13 @@ var setFsWatchFileListener = (path25, fullPath, options, handlers) => {
16931
18020
  listeners: listener,
16932
18021
  rawEmitters: rawEmitter,
16933
18022
  options,
16934
- watcher: (0, import_node_fs.watchFile)(fullPath, options, (curr, prev) => {
18023
+ watcher: (0, import_node_fs2.watchFile)(fullPath, options, (curr, prev) => {
16935
18024
  foreach(cont.rawEmitters, (rawEmitter2) => {
16936
18025
  rawEmitter2(EV.CHANGE, fullPath, { curr, prev });
16937
18026
  });
16938
18027
  const currmtime = curr.mtimeMs;
16939
18028
  if (curr.size !== prev.size || currmtime > prev.mtimeMs || currmtime === 0) {
16940
- foreach(cont.listeners, (listener2) => listener2(path25, curr));
18029
+ foreach(cont.listeners, (listener2) => listener2(path26, curr));
16941
18030
  }
16942
18031
  })
16943
18032
  };
@@ -16948,7 +18037,7 @@ var setFsWatchFileListener = (path25, fullPath, options, handlers) => {
16948
18037
  delFromSet(cont, KEY_RAW, rawEmitter);
16949
18038
  if (isEmptySet(cont.listeners)) {
16950
18039
  FsWatchFileInstances.delete(fullPath);
16951
- (0, import_node_fs.unwatchFile)(fullPath);
18040
+ (0, import_node_fs2.unwatchFile)(fullPath);
16952
18041
  cont.options = cont.watcher = void 0;
16953
18042
  Object.freeze(cont);
16954
18043
  }
@@ -16967,13 +18056,13 @@ var NodeFsHandler = class {
16967
18056
  * @param listener on fs change
16968
18057
  * @returns closer for the watcher instance
16969
18058
  */
16970
- _watchWithNodeFs(path25, listener) {
18059
+ _watchWithNodeFs(path26, listener) {
16971
18060
  const opts = this.fsw.options;
16972
- const directory = sp.dirname(path25);
16973
- const basename7 = sp.basename(path25);
18061
+ const directory = sp.dirname(path26);
18062
+ const basename8 = sp.basename(path26);
16974
18063
  const parent = this.fsw._getWatchedDir(directory);
16975
- parent.add(basename7);
16976
- const absolutePath = sp.resolve(path25);
18064
+ parent.add(basename8);
18065
+ const absolutePath = sp.resolve(path26);
16977
18066
  const options = {
16978
18067
  persistent: opts.persistent
16979
18068
  };
@@ -16982,13 +18071,13 @@ var NodeFsHandler = class {
16982
18071
  let closer;
16983
18072
  if (opts.usePolling) {
16984
18073
  const enableBin = opts.interval !== opts.binaryInterval;
16985
- options.interval = enableBin && isBinaryPath(basename7) ? opts.binaryInterval : opts.interval;
16986
- closer = setFsWatchFileListener(path25, absolutePath, options, {
18074
+ options.interval = enableBin && isBinaryPath(basename8) ? opts.binaryInterval : opts.interval;
18075
+ closer = setFsWatchFileListener(path26, absolutePath, options, {
16987
18076
  listener,
16988
18077
  rawEmitter: this.fsw._emitRaw
16989
18078
  });
16990
18079
  } else {
16991
- closer = setFsWatchListener(path25, absolutePath, options, {
18080
+ closer = setFsWatchListener(path26, absolutePath, options, {
16992
18081
  listener,
16993
18082
  errHandler: this._boundHandleError,
16994
18083
  rawEmitter: this.fsw._emitRaw
@@ -17004,13 +18093,13 @@ var NodeFsHandler = class {
17004
18093
  if (this.fsw.closed) {
17005
18094
  return;
17006
18095
  }
17007
- const dirname13 = sp.dirname(file);
17008
- const basename7 = sp.basename(file);
17009
- const parent = this.fsw._getWatchedDir(dirname13);
18096
+ const dirname14 = sp.dirname(file);
18097
+ const basename8 = sp.basename(file);
18098
+ const parent = this.fsw._getWatchedDir(dirname14);
17010
18099
  let prevStats = stats;
17011
- if (parent.has(basename7))
18100
+ if (parent.has(basename8))
17012
18101
  return;
17013
- const listener = async (path25, newStats) => {
18102
+ const listener = async (path26, newStats) => {
17014
18103
  if (!this.fsw._throttle(THROTTLE_MODE_WATCH, file, 5))
17015
18104
  return;
17016
18105
  if (!newStats || newStats.mtimeMs === 0) {
@@ -17024,18 +18113,18 @@ var NodeFsHandler = class {
17024
18113
  this.fsw._emit(EV.CHANGE, file, newStats2);
17025
18114
  }
17026
18115
  if ((isMacos || isLinux || isFreeBSD) && prevStats.ino !== newStats2.ino) {
17027
- this.fsw._closeFile(path25);
18116
+ this.fsw._closeFile(path26);
17028
18117
  prevStats = newStats2;
17029
18118
  const closer2 = this._watchWithNodeFs(file, listener);
17030
18119
  if (closer2)
17031
- this.fsw._addPathCloser(path25, closer2);
18120
+ this.fsw._addPathCloser(path26, closer2);
17032
18121
  } else {
17033
18122
  prevStats = newStats2;
17034
18123
  }
17035
18124
  } catch (error) {
17036
- this.fsw._remove(dirname13, basename7);
18125
+ this.fsw._remove(dirname14, basename8);
17037
18126
  }
17038
- } else if (parent.has(basename7)) {
18127
+ } else if (parent.has(basename8)) {
17039
18128
  const at = newStats.atimeMs;
17040
18129
  const mt = newStats.mtimeMs;
17041
18130
  if (!at || at <= mt || mt !== prevStats.mtimeMs) {
@@ -17060,7 +18149,7 @@ var NodeFsHandler = class {
17060
18149
  * @param item basename of this item
17061
18150
  * @returns true if no more processing is needed for this entry.
17062
18151
  */
17063
- async _handleSymlink(entry, directory, path25, item) {
18152
+ async _handleSymlink(entry, directory, path26, item) {
17064
18153
  if (this.fsw.closed) {
17065
18154
  return;
17066
18155
  }
@@ -17070,7 +18159,7 @@ var NodeFsHandler = class {
17070
18159
  this.fsw._incrReadyCount();
17071
18160
  let linkPath;
17072
18161
  try {
17073
- linkPath = await (0, import_promises2.realpath)(path25);
18162
+ linkPath = await (0, import_promises2.realpath)(path26);
17074
18163
  } catch (e) {
17075
18164
  this.fsw._emitReady();
17076
18165
  return true;
@@ -17080,12 +18169,12 @@ var NodeFsHandler = class {
17080
18169
  if (dir.has(item)) {
17081
18170
  if (this.fsw._symlinkPaths.get(full) !== linkPath) {
17082
18171
  this.fsw._symlinkPaths.set(full, linkPath);
17083
- this.fsw._emit(EV.CHANGE, path25, entry.stats);
18172
+ this.fsw._emit(EV.CHANGE, path26, entry.stats);
17084
18173
  }
17085
18174
  } else {
17086
18175
  dir.add(item);
17087
18176
  this.fsw._symlinkPaths.set(full, linkPath);
17088
- this.fsw._emit(EV.ADD, path25, entry.stats);
18177
+ this.fsw._emit(EV.ADD, path26, entry.stats);
17089
18178
  }
17090
18179
  this.fsw._emitReady();
17091
18180
  return true;
@@ -17115,9 +18204,9 @@ var NodeFsHandler = class {
17115
18204
  return;
17116
18205
  }
17117
18206
  const item = entry.path;
17118
- let path25 = sp.join(directory, item);
18207
+ let path26 = sp.join(directory, item);
17119
18208
  current.add(item);
17120
- if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path25, item)) {
18209
+ if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path26, item)) {
17121
18210
  return;
17122
18211
  }
17123
18212
  if (this.fsw.closed) {
@@ -17126,11 +18215,11 @@ var NodeFsHandler = class {
17126
18215
  }
17127
18216
  if (item === target || !target && !previous.has(item)) {
17128
18217
  this.fsw._incrReadyCount();
17129
- path25 = sp.join(dir, sp.relative(dir, path25));
17130
- this._addToNodeFs(path25, initialAdd, wh, depth + 1);
18218
+ path26 = sp.join(dir, sp.relative(dir, path26));
18219
+ this._addToNodeFs(path26, initialAdd, wh, depth + 1);
17131
18220
  }
17132
18221
  }).on(EV.ERROR, this._boundHandleError);
17133
- return new Promise((resolve17, reject) => {
18222
+ return new Promise((resolve18, reject) => {
17134
18223
  if (!stream)
17135
18224
  return reject();
17136
18225
  stream.once(STR_END, () => {
@@ -17139,7 +18228,7 @@ var NodeFsHandler = class {
17139
18228
  return;
17140
18229
  }
17141
18230
  const wasThrottled = throttler ? throttler.clear() : false;
17142
- resolve17(void 0);
18231
+ resolve18(void 0);
17143
18232
  previous.getChildren().filter((item) => {
17144
18233
  return item !== directory && !current.has(item);
17145
18234
  }).forEach((item) => {
@@ -17196,13 +18285,13 @@ var NodeFsHandler = class {
17196
18285
  * @param depth Child path actually targeted for watch
17197
18286
  * @param target Child path actually targeted for watch
17198
18287
  */
17199
- async _addToNodeFs(path25, initialAdd, priorWh, depth, target) {
18288
+ async _addToNodeFs(path26, initialAdd, priorWh, depth, target) {
17200
18289
  const ready = this.fsw._emitReady;
17201
- if (this.fsw._isIgnored(path25) || this.fsw.closed) {
18290
+ if (this.fsw._isIgnored(path26) || this.fsw.closed) {
17202
18291
  ready();
17203
18292
  return false;
17204
18293
  }
17205
- const wh = this.fsw._getWatchHelpers(path25);
18294
+ const wh = this.fsw._getWatchHelpers(path26);
17206
18295
  if (priorWh) {
17207
18296
  wh.filterPath = (entry) => priorWh.filterPath(entry);
17208
18297
  wh.filterDir = (entry) => priorWh.filterDir(entry);
@@ -17218,8 +18307,8 @@ var NodeFsHandler = class {
17218
18307
  const follow = this.fsw.options.followSymlinks;
17219
18308
  let closer;
17220
18309
  if (stats.isDirectory()) {
17221
- const absPath = sp.resolve(path25);
17222
- const targetPath = follow ? await (0, import_promises2.realpath)(path25) : path25;
18310
+ const absPath = sp.resolve(path26);
18311
+ const targetPath = follow ? await (0, import_promises2.realpath)(path26) : path26;
17223
18312
  if (this.fsw.closed)
17224
18313
  return;
17225
18314
  closer = await this._handleDir(wh.watchPath, stats, initialAdd, depth, target, wh, targetPath);
@@ -17229,29 +18318,29 @@ var NodeFsHandler = class {
17229
18318
  this.fsw._symlinkPaths.set(absPath, targetPath);
17230
18319
  }
17231
18320
  } else if (stats.isSymbolicLink()) {
17232
- const targetPath = follow ? await (0, import_promises2.realpath)(path25) : path25;
18321
+ const targetPath = follow ? await (0, import_promises2.realpath)(path26) : path26;
17233
18322
  if (this.fsw.closed)
17234
18323
  return;
17235
18324
  const parent = sp.dirname(wh.watchPath);
17236
18325
  this.fsw._getWatchedDir(parent).add(wh.watchPath);
17237
18326
  this.fsw._emit(EV.ADD, wh.watchPath, stats);
17238
- closer = await this._handleDir(parent, stats, initialAdd, depth, path25, wh, targetPath);
18327
+ closer = await this._handleDir(parent, stats, initialAdd, depth, path26, wh, targetPath);
17239
18328
  if (this.fsw.closed)
17240
18329
  return;
17241
18330
  if (targetPath !== void 0) {
17242
- this.fsw._symlinkPaths.set(sp.resolve(path25), targetPath);
18331
+ this.fsw._symlinkPaths.set(sp.resolve(path26), targetPath);
17243
18332
  }
17244
18333
  } else {
17245
18334
  closer = this._handleFile(wh.watchPath, stats, initialAdd);
17246
18335
  }
17247
18336
  ready();
17248
18337
  if (closer)
17249
- this.fsw._addPathCloser(path25, closer);
18338
+ this.fsw._addPathCloser(path26, closer);
17250
18339
  return false;
17251
18340
  } catch (error) {
17252
18341
  if (this.fsw._handleError(error)) {
17253
18342
  ready();
17254
- return path25;
18343
+ return path26;
17255
18344
  }
17256
18345
  }
17257
18346
  }
@@ -17294,24 +18383,24 @@ function createPattern(matcher) {
17294
18383
  }
17295
18384
  return () => false;
17296
18385
  }
17297
- function normalizePath2(path25) {
17298
- if (typeof path25 !== "string")
18386
+ function normalizePath2(path26) {
18387
+ if (typeof path26 !== "string")
17299
18388
  throw new Error("string expected");
17300
- path25 = sp2.normalize(path25);
17301
- path25 = path25.replace(/\\/g, "/");
18389
+ path26 = sp2.normalize(path26);
18390
+ path26 = path26.replace(/\\/g, "/");
17302
18391
  let prepend = false;
17303
- if (path25.startsWith("//"))
18392
+ if (path26.startsWith("//"))
17304
18393
  prepend = true;
17305
- path25 = path25.replace(DOUBLE_SLASH_RE, "/");
18394
+ path26 = path26.replace(DOUBLE_SLASH_RE, "/");
17306
18395
  if (prepend)
17307
- path25 = "/" + path25;
17308
- return path25;
18396
+ path26 = "/" + path26;
18397
+ return path26;
17309
18398
  }
17310
18399
  function matchPatterns(patterns, testString, stats) {
17311
- const path25 = normalizePath2(testString);
18400
+ const path26 = normalizePath2(testString);
17312
18401
  for (let index = 0; index < patterns.length; index++) {
17313
18402
  const pattern = patterns[index];
17314
- if (pattern(path25, stats)) {
18403
+ if (pattern(path26, stats)) {
17315
18404
  return true;
17316
18405
  }
17317
18406
  }
@@ -17349,19 +18438,19 @@ var toUnix = (string) => {
17349
18438
  }
17350
18439
  return str;
17351
18440
  };
17352
- var normalizePathToUnix = (path25) => toUnix(sp2.normalize(toUnix(path25)));
17353
- var normalizeIgnored = (cwd = "") => (path25) => {
17354
- if (typeof path25 === "string") {
17355
- return normalizePathToUnix(sp2.isAbsolute(path25) ? path25 : sp2.join(cwd, path25));
18441
+ var normalizePathToUnix = (path26) => toUnix(sp2.normalize(toUnix(path26)));
18442
+ var normalizeIgnored = (cwd = "") => (path26) => {
18443
+ if (typeof path26 === "string") {
18444
+ return normalizePathToUnix(sp2.isAbsolute(path26) ? path26 : sp2.join(cwd, path26));
17356
18445
  } else {
17357
- return path25;
18446
+ return path26;
17358
18447
  }
17359
18448
  };
17360
- var getAbsolutePath = (path25, cwd) => {
17361
- if (sp2.isAbsolute(path25)) {
17362
- return path25;
18449
+ var getAbsolutePath = (path26, cwd) => {
18450
+ if (sp2.isAbsolute(path26)) {
18451
+ return path26;
17363
18452
  }
17364
- return sp2.join(cwd, path25);
18453
+ return sp2.join(cwd, path26);
17365
18454
  };
17366
18455
  var EMPTY_SET = Object.freeze(/* @__PURE__ */ new Set());
17367
18456
  var DirEntry = class {
@@ -17426,10 +18515,10 @@ var WatchHelper = class {
17426
18515
  dirParts;
17427
18516
  followSymlinks;
17428
18517
  statMethod;
17429
- constructor(path25, follow, fsw) {
18518
+ constructor(path26, follow, fsw) {
17430
18519
  this.fsw = fsw;
17431
- const watchPath = path25;
17432
- this.path = path25 = path25.replace(REPLACER_RE, "");
18520
+ const watchPath = path26;
18521
+ this.path = path26 = path26.replace(REPLACER_RE, "");
17433
18522
  this.watchPath = watchPath;
17434
18523
  this.fullWatchPath = sp2.resolve(watchPath);
17435
18524
  this.dirParts = [];
@@ -17569,20 +18658,20 @@ var FSWatcher = class extends import_node_events.EventEmitter {
17569
18658
  this._closePromise = void 0;
17570
18659
  let paths = unifyPaths(paths_);
17571
18660
  if (cwd) {
17572
- paths = paths.map((path25) => {
17573
- const absPath = getAbsolutePath(path25, cwd);
18661
+ paths = paths.map((path26) => {
18662
+ const absPath = getAbsolutePath(path26, cwd);
17574
18663
  return absPath;
17575
18664
  });
17576
18665
  }
17577
- paths.forEach((path25) => {
17578
- this._removeIgnoredPath(path25);
18666
+ paths.forEach((path26) => {
18667
+ this._removeIgnoredPath(path26);
17579
18668
  });
17580
18669
  this._userIgnored = void 0;
17581
18670
  if (!this._readyCount)
17582
18671
  this._readyCount = 0;
17583
18672
  this._readyCount += paths.length;
17584
- Promise.all(paths.map(async (path25) => {
17585
- const res = await this._nodeFsHandler._addToNodeFs(path25, !_internal, void 0, 0, _origAdd);
18673
+ Promise.all(paths.map(async (path26) => {
18674
+ const res = await this._nodeFsHandler._addToNodeFs(path26, !_internal, void 0, 0, _origAdd);
17586
18675
  if (res)
17587
18676
  this._emitReady();
17588
18677
  return res;
@@ -17604,17 +18693,17 @@ var FSWatcher = class extends import_node_events.EventEmitter {
17604
18693
  return this;
17605
18694
  const paths = unifyPaths(paths_);
17606
18695
  const { cwd } = this.options;
17607
- paths.forEach((path25) => {
17608
- if (!sp2.isAbsolute(path25) && !this._closers.has(path25)) {
18696
+ paths.forEach((path26) => {
18697
+ if (!sp2.isAbsolute(path26) && !this._closers.has(path26)) {
17609
18698
  if (cwd)
17610
- path25 = sp2.join(cwd, path25);
17611
- path25 = sp2.resolve(path25);
18699
+ path26 = sp2.join(cwd, path26);
18700
+ path26 = sp2.resolve(path26);
17612
18701
  }
17613
- this._closePath(path25);
17614
- this._addIgnoredPath(path25);
17615
- if (this._watched.has(path25)) {
18702
+ this._closePath(path26);
18703
+ this._addIgnoredPath(path26);
18704
+ if (this._watched.has(path26)) {
17616
18705
  this._addIgnoredPath({
17617
- path: path25,
18706
+ path: path26,
17618
18707
  recursive: true
17619
18708
  });
17620
18709
  }
@@ -17678,38 +18767,38 @@ var FSWatcher = class extends import_node_events.EventEmitter {
17678
18767
  * @param stats arguments to be passed with event
17679
18768
  * @returns the error if defined, otherwise the value of the FSWatcher instance's `closed` flag
17680
18769
  */
17681
- async _emit(event, path25, stats) {
18770
+ async _emit(event, path26, stats) {
17682
18771
  if (this.closed)
17683
18772
  return;
17684
18773
  const opts = this.options;
17685
18774
  if (isWindows)
17686
- path25 = sp2.normalize(path25);
18775
+ path26 = sp2.normalize(path26);
17687
18776
  if (opts.cwd)
17688
- path25 = sp2.relative(opts.cwd, path25);
17689
- const args = [path25];
18777
+ path26 = sp2.relative(opts.cwd, path26);
18778
+ const args = [path26];
17690
18779
  if (stats != null)
17691
18780
  args.push(stats);
17692
18781
  const awf = opts.awaitWriteFinish;
17693
18782
  let pw;
17694
- if (awf && (pw = this._pendingWrites.get(path25))) {
18783
+ if (awf && (pw = this._pendingWrites.get(path26))) {
17695
18784
  pw.lastChange = /* @__PURE__ */ new Date();
17696
18785
  return this;
17697
18786
  }
17698
18787
  if (opts.atomic) {
17699
18788
  if (event === EVENTS.UNLINK) {
17700
- this._pendingUnlinks.set(path25, [event, ...args]);
18789
+ this._pendingUnlinks.set(path26, [event, ...args]);
17701
18790
  setTimeout(() => {
17702
- this._pendingUnlinks.forEach((entry, path26) => {
18791
+ this._pendingUnlinks.forEach((entry, path27) => {
17703
18792
  this.emit(...entry);
17704
18793
  this.emit(EVENTS.ALL, ...entry);
17705
- this._pendingUnlinks.delete(path26);
18794
+ this._pendingUnlinks.delete(path27);
17706
18795
  });
17707
18796
  }, typeof opts.atomic === "number" ? opts.atomic : 100);
17708
18797
  return this;
17709
18798
  }
17710
- if (event === EVENTS.ADD && this._pendingUnlinks.has(path25)) {
18799
+ if (event === EVENTS.ADD && this._pendingUnlinks.has(path26)) {
17711
18800
  event = EVENTS.CHANGE;
17712
- this._pendingUnlinks.delete(path25);
18801
+ this._pendingUnlinks.delete(path26);
17713
18802
  }
17714
18803
  }
17715
18804
  if (awf && (event === EVENTS.ADD || event === EVENTS.CHANGE) && this._readyEmitted) {
@@ -17727,16 +18816,16 @@ var FSWatcher = class extends import_node_events.EventEmitter {
17727
18816
  this.emitWithAll(event, args);
17728
18817
  }
17729
18818
  };
17730
- this._awaitWriteFinish(path25, awf.stabilityThreshold, event, awfEmit);
18819
+ this._awaitWriteFinish(path26, awf.stabilityThreshold, event, awfEmit);
17731
18820
  return this;
17732
18821
  }
17733
18822
  if (event === EVENTS.CHANGE) {
17734
- const isThrottled = !this._throttle(EVENTS.CHANGE, path25, 50);
18823
+ const isThrottled = !this._throttle(EVENTS.CHANGE, path26, 50);
17735
18824
  if (isThrottled)
17736
18825
  return this;
17737
18826
  }
17738
18827
  if (opts.alwaysStat && stats === void 0 && (event === EVENTS.ADD || event === EVENTS.ADD_DIR || event === EVENTS.CHANGE)) {
17739
- const fullPath = opts.cwd ? sp2.join(opts.cwd, path25) : path25;
18828
+ const fullPath = opts.cwd ? sp2.join(opts.cwd, path26) : path26;
17740
18829
  let stats2;
17741
18830
  try {
17742
18831
  stats2 = await (0, import_promises3.stat)(fullPath);
@@ -17767,23 +18856,23 @@ var FSWatcher = class extends import_node_events.EventEmitter {
17767
18856
  * @param timeout duration of time to suppress duplicate actions
17768
18857
  * @returns tracking object or false if action should be suppressed
17769
18858
  */
17770
- _throttle(actionType, path25, timeout) {
18859
+ _throttle(actionType, path26, timeout) {
17771
18860
  if (!this._throttled.has(actionType)) {
17772
18861
  this._throttled.set(actionType, /* @__PURE__ */ new Map());
17773
18862
  }
17774
18863
  const action = this._throttled.get(actionType);
17775
18864
  if (!action)
17776
18865
  throw new Error("invalid throttle");
17777
- const actionPath = action.get(path25);
18866
+ const actionPath = action.get(path26);
17778
18867
  if (actionPath) {
17779
18868
  actionPath.count++;
17780
18869
  return false;
17781
18870
  }
17782
18871
  let timeoutObject;
17783
18872
  const clear = () => {
17784
- const item = action.get(path25);
18873
+ const item = action.get(path26);
17785
18874
  const count = item ? item.count : 0;
17786
- action.delete(path25);
18875
+ action.delete(path26);
17787
18876
  clearTimeout(timeoutObject);
17788
18877
  if (item)
17789
18878
  clearTimeout(item.timeoutObject);
@@ -17791,7 +18880,7 @@ var FSWatcher = class extends import_node_events.EventEmitter {
17791
18880
  };
17792
18881
  timeoutObject = setTimeout(clear, timeout);
17793
18882
  const thr = { timeoutObject, clear, count: 0 };
17794
- action.set(path25, thr);
18883
+ action.set(path26, thr);
17795
18884
  return thr;
17796
18885
  }
17797
18886
  _incrReadyCount() {
@@ -17805,44 +18894,44 @@ var FSWatcher = class extends import_node_events.EventEmitter {
17805
18894
  * @param event
17806
18895
  * @param awfEmit Callback to be called when ready for event to be emitted.
17807
18896
  */
17808
- _awaitWriteFinish(path25, threshold, event, awfEmit) {
18897
+ _awaitWriteFinish(path26, threshold, event, awfEmit) {
17809
18898
  const awf = this.options.awaitWriteFinish;
17810
18899
  if (typeof awf !== "object")
17811
18900
  return;
17812
18901
  const pollInterval = awf.pollInterval;
17813
18902
  let timeoutHandler;
17814
- let fullPath = path25;
17815
- if (this.options.cwd && !sp2.isAbsolute(path25)) {
17816
- fullPath = sp2.join(this.options.cwd, path25);
18903
+ let fullPath = path26;
18904
+ if (this.options.cwd && !sp2.isAbsolute(path26)) {
18905
+ fullPath = sp2.join(this.options.cwd, path26);
17817
18906
  }
17818
18907
  const now2 = /* @__PURE__ */ new Date();
17819
18908
  const writes = this._pendingWrites;
17820
18909
  function awaitWriteFinishFn(prevStat) {
17821
- (0, import_node_fs2.stat)(fullPath, (err, curStat) => {
17822
- if (err || !writes.has(path25)) {
18910
+ (0, import_node_fs3.stat)(fullPath, (err, curStat) => {
18911
+ if (err || !writes.has(path26)) {
17823
18912
  if (err && err.code !== "ENOENT")
17824
18913
  awfEmit(err);
17825
18914
  return;
17826
18915
  }
17827
18916
  const now3 = Number(/* @__PURE__ */ new Date());
17828
18917
  if (prevStat && curStat.size !== prevStat.size) {
17829
- writes.get(path25).lastChange = now3;
18918
+ writes.get(path26).lastChange = now3;
17830
18919
  }
17831
- const pw = writes.get(path25);
18920
+ const pw = writes.get(path26);
17832
18921
  const df = now3 - pw.lastChange;
17833
18922
  if (df >= threshold) {
17834
- writes.delete(path25);
18923
+ writes.delete(path26);
17835
18924
  awfEmit(void 0, curStat);
17836
18925
  } else {
17837
18926
  timeoutHandler = setTimeout(awaitWriteFinishFn, pollInterval, curStat);
17838
18927
  }
17839
18928
  });
17840
18929
  }
17841
- if (!writes.has(path25)) {
17842
- writes.set(path25, {
18930
+ if (!writes.has(path26)) {
18931
+ writes.set(path26, {
17843
18932
  lastChange: now2,
17844
18933
  cancelWait: () => {
17845
- writes.delete(path25);
18934
+ writes.delete(path26);
17846
18935
  clearTimeout(timeoutHandler);
17847
18936
  return event;
17848
18937
  }
@@ -17853,8 +18942,8 @@ var FSWatcher = class extends import_node_events.EventEmitter {
17853
18942
  /**
17854
18943
  * Determines whether user has asked to ignore this path.
17855
18944
  */
17856
- _isIgnored(path25, stats) {
17857
- if (this.options.atomic && DOT_RE.test(path25))
18945
+ _isIgnored(path26, stats) {
18946
+ if (this.options.atomic && DOT_RE.test(path26))
17858
18947
  return true;
17859
18948
  if (!this._userIgnored) {
17860
18949
  const { cwd } = this.options;
@@ -17864,17 +18953,17 @@ var FSWatcher = class extends import_node_events.EventEmitter {
17864
18953
  const list = [...ignoredPaths.map(normalizeIgnored(cwd)), ...ignored];
17865
18954
  this._userIgnored = anymatch(list, void 0);
17866
18955
  }
17867
- return this._userIgnored(path25, stats);
18956
+ return this._userIgnored(path26, stats);
17868
18957
  }
17869
- _isntIgnored(path25, stat5) {
17870
- return !this._isIgnored(path25, stat5);
18958
+ _isntIgnored(path26, stat5) {
18959
+ return !this._isIgnored(path26, stat5);
17871
18960
  }
17872
18961
  /**
17873
18962
  * Provides a set of common helpers and properties relating to symlink handling.
17874
18963
  * @param path file or directory pattern being watched
17875
18964
  */
17876
- _getWatchHelpers(path25) {
17877
- return new WatchHelper(path25, this.options.followSymlinks, this);
18965
+ _getWatchHelpers(path26) {
18966
+ return new WatchHelper(path26, this.options.followSymlinks, this);
17878
18967
  }
17879
18968
  // Directory helpers
17880
18969
  // -----------------
@@ -17906,63 +18995,63 @@ var FSWatcher = class extends import_node_events.EventEmitter {
17906
18995
  * @param item base path of item/directory
17907
18996
  */
17908
18997
  _remove(directory, item, isDirectory) {
17909
- const path25 = sp2.join(directory, item);
17910
- const fullPath = sp2.resolve(path25);
17911
- isDirectory = isDirectory != null ? isDirectory : this._watched.has(path25) || this._watched.has(fullPath);
17912
- if (!this._throttle("remove", path25, 100))
18998
+ const path26 = sp2.join(directory, item);
18999
+ const fullPath = sp2.resolve(path26);
19000
+ isDirectory = isDirectory != null ? isDirectory : this._watched.has(path26) || this._watched.has(fullPath);
19001
+ if (!this._throttle("remove", path26, 100))
17913
19002
  return;
17914
19003
  if (!isDirectory && this._watched.size === 1) {
17915
19004
  this.add(directory, item, true);
17916
19005
  }
17917
- const wp = this._getWatchedDir(path25);
19006
+ const wp = this._getWatchedDir(path26);
17918
19007
  const nestedDirectoryChildren = wp.getChildren();
17919
- nestedDirectoryChildren.forEach((nested) => this._remove(path25, nested));
19008
+ nestedDirectoryChildren.forEach((nested) => this._remove(path26, nested));
17920
19009
  const parent = this._getWatchedDir(directory);
17921
19010
  const wasTracked = parent.has(item);
17922
19011
  parent.remove(item);
17923
19012
  if (this._symlinkPaths.has(fullPath)) {
17924
19013
  this._symlinkPaths.delete(fullPath);
17925
19014
  }
17926
- let relPath = path25;
19015
+ let relPath = path26;
17927
19016
  if (this.options.cwd)
17928
- relPath = sp2.relative(this.options.cwd, path25);
19017
+ relPath = sp2.relative(this.options.cwd, path26);
17929
19018
  if (this.options.awaitWriteFinish && this._pendingWrites.has(relPath)) {
17930
19019
  const event = this._pendingWrites.get(relPath).cancelWait();
17931
19020
  if (event === EVENTS.ADD)
17932
19021
  return;
17933
19022
  }
17934
- this._watched.delete(path25);
19023
+ this._watched.delete(path26);
17935
19024
  this._watched.delete(fullPath);
17936
19025
  const eventName = isDirectory ? EVENTS.UNLINK_DIR : EVENTS.UNLINK;
17937
- if (wasTracked && !this._isIgnored(path25))
17938
- this._emit(eventName, path25);
17939
- this._closePath(path25);
19026
+ if (wasTracked && !this._isIgnored(path26))
19027
+ this._emit(eventName, path26);
19028
+ this._closePath(path26);
17940
19029
  }
17941
19030
  /**
17942
19031
  * Closes all watchers for a path
17943
19032
  */
17944
- _closePath(path25) {
17945
- this._closeFile(path25);
17946
- const dir = sp2.dirname(path25);
17947
- this._getWatchedDir(dir).remove(sp2.basename(path25));
19033
+ _closePath(path26) {
19034
+ this._closeFile(path26);
19035
+ const dir = sp2.dirname(path26);
19036
+ this._getWatchedDir(dir).remove(sp2.basename(path26));
17948
19037
  }
17949
19038
  /**
17950
19039
  * Closes only file-specific watchers
17951
19040
  */
17952
- _closeFile(path25) {
17953
- const closers = this._closers.get(path25);
19041
+ _closeFile(path26) {
19042
+ const closers = this._closers.get(path26);
17954
19043
  if (!closers)
17955
19044
  return;
17956
19045
  closers.forEach((closer) => closer());
17957
- this._closers.delete(path25);
19046
+ this._closers.delete(path26);
17958
19047
  }
17959
- _addPathCloser(path25, closer) {
19048
+ _addPathCloser(path26, closer) {
17960
19049
  if (!closer)
17961
19050
  return;
17962
- let list = this._closers.get(path25);
19051
+ let list = this._closers.get(path26);
17963
19052
  if (!list) {
17964
19053
  list = [];
17965
- this._closers.set(path25, list);
19054
+ this._closers.set(path26, list);
17966
19055
  }
17967
19056
  list.push(closer);
17968
19057
  }
@@ -17992,11 +19081,11 @@ function watch(paths, options = {}) {
17992
19081
  var chokidar_default = { watch, FSWatcher };
17993
19082
 
17994
19083
  // src/watcher/file-watcher.ts
17995
- var path23 = __toESM(require("path"), 1);
19084
+ var path24 = __toESM(require("path"), 1);
17996
19085
 
17997
19086
  // src/watcher/native-recursive-watcher.ts
17998
- var import_node_fs3 = require("fs");
17999
- var path21 = __toESM(require("path"), 1);
19087
+ var import_node_fs4 = require("fs");
19088
+ var path22 = __toESM(require("path"), 1);
18000
19089
  var NativeRecursiveWatcher = class {
18001
19090
  constructor(root, onChange, options = {}) {
18002
19091
  this.root = root;
@@ -18044,26 +19133,26 @@ var NativeRecursiveWatcher = class {
18044
19133
  toAbsolutePath(filename) {
18045
19134
  if (filename == null) return null;
18046
19135
  const normalizedFilename = typeof filename === "string" ? filename : filename.toString();
18047
- const absolutePath = path21.resolve(this.root, normalizedFilename);
18048
- const relativePath = path21.relative(this.root, absolutePath);
18049
- const outsideRoot = relativePath === ".." || relativePath.startsWith(`..${path21.sep}`) || path21.isAbsolute(relativePath);
19136
+ const absolutePath = path22.resolve(this.root, normalizedFilename);
19137
+ const relativePath = path22.relative(this.root, absolutePath);
19138
+ const outsideRoot = relativePath === ".." || relativePath.startsWith(`..${path22.sep}`) || path22.isAbsolute(relativePath);
18050
19139
  return outsideRoot ? null : absolutePath;
18051
19140
  }
18052
- defaultWatchFactory = (root, listener, options) => (0, import_node_fs3.watch)(root, options, listener);
19141
+ defaultWatchFactory = (root, listener, options) => (0, import_node_fs4.watch)(root, options, listener);
18053
19142
  };
18054
19143
 
18055
19144
  // src/watcher/snapshot.ts
18056
19145
  var fsPromises4 = __toESM(require("fs/promises"), 1);
18057
- var path22 = __toESM(require("path"), 1);
19146
+ var path23 = __toESM(require("path"), 1);
18058
19147
  async function buildFileSnapshotScan(projectRoot3, config, configPaths = []) {
18059
- const normalizedProjectRoot = path22.resolve(projectRoot3);
19148
+ const normalizedProjectRoot = path23.resolve(projectRoot3);
18060
19149
  const ignoreFilter = createIgnoreFilter(normalizedProjectRoot);
18061
19150
  const includePatterns = [...config.include, ...config.additionalInclude ?? []];
18062
19151
  const maxDepth = config.indexing?.maxDepth ?? -1;
18063
19152
  const snapshot = /* @__PURE__ */ new Map();
18064
19153
  const unreadablePrefixes = /* @__PURE__ */ new Set();
18065
19154
  const includeFile = async (filePath) => {
18066
- const normalizedPath2 = path22.resolve(filePath);
19155
+ const normalizedPath2 = path23.resolve(filePath);
18067
19156
  if (!shouldIncludeFile(normalizedPath2, normalizedProjectRoot, includePatterns, config.exclude, ignoreFilter)) return;
18068
19157
  const stat5 = await readStatIfFile(normalizedPath2, unreadablePrefixes);
18069
19158
  if (stat5) snapshot.set(normalizedPath2, { size: stat5.size, mtimeMs: stat5.mtimeMs });
@@ -18075,16 +19164,16 @@ async function buildFileSnapshotScan(projectRoot3, config, configPaths = []) {
18075
19164
  } catch (error) {
18076
19165
  if (isMissingFsError(error)) return;
18077
19166
  if (isPermissionFsError(error)) {
18078
- unreadablePrefixes.add(path22.resolve(directoryPath));
19167
+ unreadablePrefixes.add(path23.resolve(directoryPath));
18079
19168
  return;
18080
19169
  }
18081
19170
  throw error;
18082
19171
  }
18083
19172
  for (const entry of entries) {
18084
- const fullPath = path22.join(directoryPath, entry.name);
18085
- const relativePath = path22.relative(normalizedProjectRoot, fullPath);
19173
+ const fullPath = path23.join(directoryPath, entry.name);
19174
+ const relativePath = path23.relative(normalizedProjectRoot, fullPath);
18086
19175
  if (entry.isDirectory()) {
18087
- if (hasFilteredPathSegment(relativePath, path22.sep) || isRestrictedDirectory(relativePath, path22.sep)) continue;
19176
+ if (hasFilteredPathSegment(relativePath, path23.sep) || isRestrictedDirectory(relativePath, path23.sep)) continue;
18088
19177
  if (ignoreFilter.ignores(relativePath)) continue;
18089
19178
  if (maxDepth === -1 || depth < maxDepth) await walk(fullPath, depth + 1);
18090
19179
  } else if (entry.isFile()) {
@@ -18097,19 +19186,19 @@ async function buildFileSnapshotScan(projectRoot3, config, configPaths = []) {
18097
19186
  return { entries: snapshot, unreadablePrefixes };
18098
19187
  }
18099
19188
  async function buildFileSnapshotForPathScan(projectRoot3, config, configPaths, targetPath) {
18100
- const normalizedProjectRoot = path22.resolve(projectRoot3);
18101
- const normalizedTargetPath = path22.resolve(targetPath);
19189
+ const normalizedProjectRoot = path23.resolve(projectRoot3);
19190
+ const normalizedTargetPath = path23.resolve(targetPath);
18102
19191
  if (!isWithinPath(normalizedProjectRoot, normalizedTargetPath)) {
18103
19192
  return { entries: /* @__PURE__ */ new Map(), unreadablePrefixes: /* @__PURE__ */ new Set() };
18104
19193
  }
18105
19194
  const ignoreFilter = createIgnoreFilter(normalizedProjectRoot);
18106
19195
  const includePatterns = [...config.include, ...config.additionalInclude ?? []];
18107
19196
  const maxDepth = config.indexing?.maxDepth ?? -1;
18108
- const explicitConfigPaths = new Set(configPaths.map((configPath) => path22.resolve(configPath)));
19197
+ const explicitConfigPaths = new Set(configPaths.map((configPath) => path23.resolve(configPath)));
18109
19198
  const snapshot = /* @__PURE__ */ new Map();
18110
19199
  const unreadablePrefixes = /* @__PURE__ */ new Set();
18111
19200
  const includeFile = async (filePath) => {
18112
- const normalizedPath2 = path22.resolve(filePath);
19201
+ const normalizedPath2 = path23.resolve(filePath);
18113
19202
  if (!explicitConfigPaths.has(normalizedPath2) && !shouldIncludeFile(
18114
19203
  normalizedPath2,
18115
19204
  normalizedProjectRoot,
@@ -18127,16 +19216,16 @@ async function buildFileSnapshotForPathScan(projectRoot3, config, configPaths, t
18127
19216
  } catch (error) {
18128
19217
  if (isMissingFsError(error)) return;
18129
19218
  if (isPermissionFsError(error)) {
18130
- unreadablePrefixes.add(path22.resolve(directoryPath));
19219
+ unreadablePrefixes.add(path23.resolve(directoryPath));
18131
19220
  return;
18132
19221
  }
18133
19222
  throw error;
18134
19223
  }
18135
19224
  for (const entry of entries) {
18136
- const fullPath = path22.join(directoryPath, entry.name);
18137
- const relativePath = path22.relative(normalizedProjectRoot, fullPath);
19225
+ const fullPath = path23.join(directoryPath, entry.name);
19226
+ const relativePath = path23.relative(normalizedProjectRoot, fullPath);
18138
19227
  if (entry.isDirectory()) {
18139
- if (hasFilteredPathSegment(relativePath, path22.sep) || isRestrictedDirectory(relativePath, path22.sep)) continue;
19228
+ if (hasFilteredPathSegment(relativePath, path23.sep) || isRestrictedDirectory(relativePath, path23.sep)) continue;
18140
19229
  if (ignoreFilter.ignores(relativePath)) continue;
18141
19230
  if (maxDepth === -1 || depth < maxDepth) await walk(fullPath, depth + 1);
18142
19231
  } else if (entry.isFile()) {
@@ -18160,7 +19249,7 @@ function completeFileSnapshot(previous, scan) {
18160
19249
  return completed;
18161
19250
  }
18162
19251
  async function includeExplicitConfigPaths(snapshot, unreadablePrefixes, configPaths) {
18163
- for (const configPath of [...new Set(configPaths.map((value) => path22.resolve(value)))]) {
19252
+ for (const configPath of [...new Set(configPaths.map((value) => path23.resolve(value)))]) {
18164
19253
  if (snapshot.has(configPath)) continue;
18165
19254
  const stat5 = await readStatIfFile(configPath, unreadablePrefixes);
18166
19255
  if (stat5) snapshot.set(configPath, { size: stat5.size, mtimeMs: stat5.mtimeMs });
@@ -18170,12 +19259,12 @@ async function includeExplicitConfigPathsInPath(snapshot, unreadablePrefixes, co
18170
19259
  await includeExplicitConfigPaths(
18171
19260
  snapshot,
18172
19261
  unreadablePrefixes,
18173
- configPaths.filter((configPath) => isWithinPath(targetPath, path22.resolve(configPath)))
19262
+ configPaths.filter((configPath) => isWithinPath(targetPath, path23.resolve(configPath)))
18174
19263
  );
18175
19264
  }
18176
19265
  function isWithinPath(parentPath, childPath) {
18177
- const relativePath = path22.relative(parentPath, childPath);
18178
- return relativePath === "" || !relativePath.startsWith(`..${path22.sep}`) && relativePath !== ".." && !path22.isAbsolute(relativePath);
19266
+ const relativePath = path23.relative(parentPath, childPath);
19267
+ return relativePath === "" || !relativePath.startsWith(`..${path23.sep}`) && relativePath !== ".." && !path23.isAbsolute(relativePath);
18179
19268
  }
18180
19269
  async function readStatIfFile(filePath, unreadablePrefixes) {
18181
19270
  try {
@@ -18184,7 +19273,7 @@ async function readStatIfFile(filePath, unreadablePrefixes) {
18184
19273
  } catch (error) {
18185
19274
  if (isMissingFsError(error)) return null;
18186
19275
  if (isPermissionFsError(error)) {
18187
- unreadablePrefixes.add(path22.resolve(filePath));
19276
+ unreadablePrefixes.add(path23.resolve(filePath));
18188
19277
  return null;
18189
19278
  }
18190
19279
  throw error;
@@ -18321,8 +19410,8 @@ var FileWatcher = class {
18321
19410
  this.createWatcher();
18322
19411
  }
18323
19412
  resetReady() {
18324
- this.readyPromise = new Promise((resolve17) => {
18325
- this.resolveReady = resolve17;
19413
+ this.readyPromise = new Promise((resolve18) => {
19414
+ this.resolveReady = resolve18;
18326
19415
  });
18327
19416
  this.startupReadySignals = 1;
18328
19417
  }
@@ -18353,7 +19442,7 @@ var FileWatcher = class {
18353
19442
  const resolvedWatchTargets = watchTargets ?? this.getFullChokidarWatchTargets();
18354
19443
  const watcherOptions = {
18355
19444
  ignored: (filePath) => {
18356
- const relativePath = path23.relative(this.projectRoot, filePath);
19445
+ const relativePath = path24.relative(this.projectRoot, filePath);
18357
19446
  if (!relativePath) return false;
18358
19447
  if (this.isProjectConfigPathOrAncestor(relativePath)) {
18359
19448
  return false;
@@ -18361,10 +19450,10 @@ var FileWatcher = class {
18361
19450
  if (this.isOutsideProjectPath(relativePath)) {
18362
19451
  return true;
18363
19452
  }
18364
- if (hasFilteredPathSegment(relativePath, path23.sep)) {
19453
+ if (hasFilteredPathSegment(relativePath, path24.sep)) {
18365
19454
  return true;
18366
19455
  }
18367
- if (isRestrictedDirectory(relativePath, path23.sep)) {
19456
+ if (isRestrictedDirectory(relativePath, path24.sep)) {
18368
19457
  return true;
18369
19458
  }
18370
19459
  if (ignoreFilter.ignores(relativePath)) {
@@ -18455,13 +19544,13 @@ var FileWatcher = class {
18455
19544
  getExternalConfigWatchTargets() {
18456
19545
  return [...new Set(
18457
19546
  this.projectConfigPaths.filter((projectConfigPath) => {
18458
- const relativeConfigPath = path23.relative(this.projectRoot, projectConfigPath);
19547
+ const relativeConfigPath = path24.relative(this.projectRoot, projectConfigPath);
18459
19548
  return this.isOutsideProjectPath(relativeConfigPath);
18460
19549
  }).map((projectConfigPath) => {
18461
19550
  if ((0, import_fs14.existsSync)(projectConfigPath)) {
18462
19551
  return projectConfigPath;
18463
19552
  }
18464
- return this.getNearestExistingDirectory(path23.dirname(projectConfigPath));
19553
+ return this.getNearestExistingDirectory(path24.dirname(projectConfigPath));
18465
19554
  })
18466
19555
  )];
18467
19556
  }
@@ -18523,7 +19612,7 @@ var FileWatcher = class {
18523
19612
  }
18524
19613
  scheduleNativeReconciliation(generation, filePath) {
18525
19614
  if (!this.isCurrentNativeSetup(generation)) return;
18526
- const requiresFullReconciliation = filePath === path23.join(this.projectRoot, ".gitignore");
19615
+ const requiresFullReconciliation = filePath === path24.join(this.projectRoot, ".gitignore");
18527
19616
  const invalidatedPath = requiresFullReconciliation ? null : filePath;
18528
19617
  this.nativeInvalidatedPaths.set(invalidatedPath, invalidatedPath !== null);
18529
19618
  if (this.nativeReconcileTimer) {
@@ -18618,23 +19707,23 @@ var FileWatcher = class {
18618
19707
  this.scheduleFlush();
18619
19708
  }
18620
19709
  isProjectConfigPath(filePath) {
18621
- const relativePath = path23.relative(this.projectRoot, filePath);
18622
- const normalizedRelativePath = path23.normalize(relativePath);
19710
+ const relativePath = path24.relative(this.projectRoot, filePath);
19711
+ const normalizedRelativePath = path24.normalize(relativePath);
18623
19712
  return this.getProjectConfigRelativePaths().some((configPath) => configPath === normalizedRelativePath);
18624
19713
  }
18625
19714
  isProjectConfigPathOrAncestor(relativePath) {
18626
- const normalizedRelativePath = path23.normalize(relativePath);
19715
+ const normalizedRelativePath = path24.normalize(relativePath);
18627
19716
  return this.getProjectConfigRelativePaths().some(
18628
- (configPath) => configPath === normalizedRelativePath || configPath.startsWith(`${normalizedRelativePath}${path23.sep}`)
19717
+ (configPath) => configPath === normalizedRelativePath || configPath.startsWith(`${normalizedRelativePath}${path24.sep}`)
18629
19718
  );
18630
19719
  }
18631
19720
  isOutsideProjectPath(relativePath) {
18632
- return relativePath === ".." || relativePath.startsWith(`..${path23.sep}`) || path23.isAbsolute(relativePath);
19721
+ return relativePath === ".." || relativePath.startsWith(`..${path24.sep}`) || path24.isAbsolute(relativePath);
18633
19722
  }
18634
19723
  getNearestExistingDirectory(directoryPath) {
18635
19724
  let candidate = directoryPath;
18636
19725
  while (!(0, import_fs14.existsSync)(candidate)) {
18637
- const parent = path23.dirname(candidate);
19726
+ const parent = path24.dirname(candidate);
18638
19727
  if (parent === candidate) break;
18639
19728
  candidate = parent;
18640
19729
  }
@@ -18642,7 +19731,7 @@ var FileWatcher = class {
18642
19731
  }
18643
19732
  getProjectConfigRelativePaths() {
18644
19733
  return this.projectConfigPaths.map(
18645
- (configPath) => path23.normalize(path23.relative(this.projectRoot, configPath))
19734
+ (configPath) => path24.normalize(path24.relative(this.projectRoot, configPath))
18646
19735
  );
18647
19736
  }
18648
19737
  getConfigPathStates() {
@@ -18700,7 +19789,7 @@ var FileWatcher = class {
18700
19789
  return;
18701
19790
  }
18702
19791
  const changes = Array.from(this.pendingChanges.entries()).map(
18703
- ([path25, type]) => ({ path: path25, type })
19792
+ ([path26, type]) => ({ path: path26, type })
18704
19793
  );
18705
19794
  this.pendingChanges.clear();
18706
19795
  try {
@@ -18746,7 +19835,7 @@ var FileWatcher = class {
18746
19835
  };
18747
19836
 
18748
19837
  // src/watcher/git-head-watcher.ts
18749
- var path24 = __toESM(require("path"), 1);
19838
+ var path25 = __toESM(require("path"), 1);
18750
19839
  var GitHeadWatcher = class {
18751
19840
  watcher = null;
18752
19841
  projectRoot;
@@ -18768,13 +19857,13 @@ var GitHeadWatcher = class {
18768
19857
  this.readyPromise = Promise.resolve();
18769
19858
  return;
18770
19859
  }
18771
- this.readyPromise = new Promise((resolve17) => {
18772
- this.resolveReady = resolve17;
19860
+ this.readyPromise = new Promise((resolve18) => {
19861
+ this.resolveReady = resolve18;
18773
19862
  });
18774
19863
  this.onBranchChange = handler;
18775
19864
  this.currentBranch = getCurrentBranch(this.projectRoot);
18776
19865
  const headPath = getHeadPath(this.projectRoot);
18777
- const refsPath = path24.join(this.projectRoot, ".git", "refs", "heads");
19866
+ const refsPath = path25.join(this.projectRoot, ".git", "refs", "heads");
18778
19867
  this.watcher = chokidar_default.watch([headPath, refsPath], {
18779
19868
  persistent: true,
18780
19869
  ignoreInitial: true,
@@ -18842,7 +19931,9 @@ var GitHeadWatcher = class {
18842
19931
  function createWatcherWithIndexer(getIndexer, projectRoot3, config, host, options = {}) {
18843
19932
  const fileWatcher = new FileWatcher(projectRoot3, config, host, options);
18844
19933
  const configPaths = getConfigPaths(projectRoot3, host, options);
18845
- configureAutoIndex(projectRoot3, host, parseConfig(config), getIndexer);
19934
+ configureAutoIndex(projectRoot3, host, parseConfig(config), getIndexer, {
19935
+ synchronizeBackgroundWorker: false
19936
+ });
18846
19937
  let stopped = false;
18847
19938
  const requestReindex = () => {
18848
19939
  if (stopped) return;
@@ -18862,7 +19953,9 @@ function createWatcherWithIndexer(getIndexer, projectRoot3, config, host, option
18862
19953
  const parsedConfig = options.configPath ? parseConfig(loadConfigFile(options.configPath)) : void 0;
18863
19954
  const refreshedConfig = refreshIndexerForDirectory(projectRoot3, host, parsedConfig);
18864
19955
  if (refreshedConfig) {
18865
- configureAutoIndex(projectRoot3, host, refreshedConfig, getIndexer);
19956
+ configureAutoIndex(projectRoot3, host, refreshedConfig, getIndexer, {
19957
+ synchronizeBackgroundWorker: false
19958
+ });
18866
19959
  }
18867
19960
  }
18868
19961
  requestReindex();
@@ -18910,7 +20003,6 @@ function getConfigPaths(projectRoot3, host, options) {
18910
20003
 
18911
20004
  // src/adapters/pi/extension.ts
18912
20005
  var HOST2 = "pi";
18913
- var activeWatchers = /* @__PURE__ */ new Map();
18914
20006
  var ChunkType = import_typebox2.Type.Union([
18915
20007
  import_typebox2.Type.Literal("function"),
18916
20008
  import_typebox2.Type.Literal("class"),
@@ -18929,24 +20021,30 @@ function projectRoot2(ctx) {
18929
20021
  function isValidProject(projectRoot3, requireProjectMarker) {
18930
20022
  return !isHomeDirectory(projectRoot3) && (!requireProjectMarker || hasProjectMarker(projectRoot3));
18931
20023
  }
18932
- function ensureWatcher(projectRoot3) {
18933
- if (activeWatchers.has(projectRoot3)) return;
20024
+ async function ensureWatcher(projectRoot3) {
18934
20025
  const config = parseConfig(loadMergedConfig(projectRoot3, HOST2));
18935
- if (!config.indexing.watchFiles || !isValidProject(projectRoot3, config.indexing.requireProjectMarker)) {
20026
+ if (!isValidProject(projectRoot3, config.indexing.requireProjectMarker)) {
20027
+ await stopBackgroundWorker(projectRoot3, HOST2).catch((error) => {
20028
+ console.error("[codebase-index] Failed to stop Pi background worker:", error);
20029
+ });
18936
20030
  return;
18937
20031
  }
18938
- activeWatchers.set(projectRoot3, createWatcherWithIndexer(
20032
+ getIndexerForProject(projectRoot3, HOST2);
20033
+ const watcherFactoryForConfig = (refreshedConfig) => refreshedConfig.indexing.watchFiles ? () => createWatcherWithIndexer(
18939
20034
  () => getIndexerForProject(projectRoot3, HOST2),
18940
20035
  projectRoot3,
18941
- config,
20036
+ refreshedConfig,
18942
20037
  HOST2
18943
- ));
18944
- }
18945
- async function stopWatcher(projectRoot3) {
18946
- const watcher = activeWatchers.get(projectRoot3);
18947
- if (!watcher) return;
18948
- activeWatchers.delete(projectRoot3);
18949
- await watcher.stop();
20038
+ ) : null;
20039
+ configureBackgroundWorker(projectRoot3, HOST2, config, {
20040
+ startAutoIndex: (source, allowDisabledAutoIndex) => {
20041
+ startAutoIndexForBackgroundWorker(projectRoot3, HOST2, source, allowDisabledAutoIndex);
20042
+ },
20043
+ stopAutoIndex: () => stopAutoIndexForBackgroundWorker(projectRoot3, HOST2),
20044
+ watcherFactory: watcherFactoryForConfig(config),
20045
+ watcherFactoryForConfig
20046
+ });
20047
+ await waitForBackgroundWorkerStart(projectRoot3, HOST2);
18950
20048
  }
18951
20049
  function codebaseIndexPiExtension(pi) {
18952
20050
  pi.registerTool({
@@ -19106,12 +20204,14 @@ function codebaseIndexPiExtension(pi) {
19106
20204
  parameters: import_typebox2.Type.Object({
19107
20205
  force: import_typebox2.Type.Optional(import_typebox2.Type.Boolean({ default: false })),
19108
20206
  estimateOnly: import_typebox2.Type.Optional(import_typebox2.Type.Boolean({ default: false })),
20207
+ dryRun: import_typebox2.Type.Optional(import_typebox2.Type.Boolean({ default: false })),
19109
20208
  verbose: import_typebox2.Type.Optional(import_typebox2.Type.Boolean({ default: false }))
19110
20209
  }),
19111
20210
  async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
19112
20211
  try {
19113
20212
  const result = await runIndexCodebase(projectRoot2(ctx), HOST2, params);
19114
20213
  if (result.kind === "estimate") return text2(formatCostEstimate(result.estimate), result.estimate);
20214
+ if (result.kind === "dryrun") return text2(formatDryRunEstimate(result.dryrun), result.dryrun);
19115
20215
  if (result.kind === "busy") return text2(result.text, { code: "INDEX_BUSY" });
19116
20216
  if (result.kind === "message") return text2(result.text);
19117
20217
  return text2(formatIndexStats(result.stats, params.verbose ?? false), result.stats);
@@ -19177,16 +20277,16 @@ function codebaseIndexPiExtension(pi) {
19177
20277
  });
19178
20278
  registerPiCallGraphTools(pi);
19179
20279
  pi.on("before_agent_start", async (event, ctx) => {
19180
- ensureWatcher(projectRoot2(ctx));
20280
+ await ensureWatcher(projectRoot2(ctx));
19181
20281
  return {
19182
20282
  systemPrompt: `${event.systemPrompt}
19183
20283
 
19184
- Check index_status first when index readiness is unknown. Use codebase_context only when repository orientation is needed (for layout, key symbols, or cross-file dependency intent), not mechanically for every task. When using codebase_context for orientation, request a compact first pass (for example: tokenBudget: 600, limit: 5) and inspect returned evidence before broad search/grep/bash/read-style reads. Avoid repeating broad reads when the compact evidence already answers the question. Use implementation_lookup for known symbols and call_graph/call_graph_path after endpoints are identified for dependency flow.`
20284
+ Check index_status first when index readiness is unknown. Use codebase_context only when repository orientation is needed (for layout, key symbols, or cross-file dependency intent), not mechanically for every task. When using codebase_context for orientation, request a compact first pass (for example: tokenBudget: 600, limit: 5) and inspect returned evidence before broad search/grep/bash/read-style reads. For change requests with a known or strongly suspected target symbol, optionally use codebase_edit_context as a compact, bounded pre-edit context for source plus direct callers and callees. Avoid repeating broad reads when the compact evidence already answers the question. Use implementation_lookup for known symbols and call_graph/call_graph_path after endpoints are identified for dependency flow.`
19185
20285
  };
19186
20286
  });
19187
20287
  pi.on("session_shutdown", async (_event, ctx) => {
19188
20288
  const root = projectRoot2(ctx);
19189
- await Promise.all([stopWatcher(root), stopAutoIndex(root, HOST2)]);
20289
+ await stopBackgroundWorker(root, HOST2);
19190
20290
  });
19191
20291
  pi.registerTool({
19192
20292
  name: TOOL_NAME.PR_IMPACT,