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.
@@ -328,7 +328,7 @@ var require_ignore = __commonJS({
328
328
  // path matching.
329
329
  // - check `string` either `MODE_IGNORE` or `MODE_CHECK_IGNORE`
330
330
  // @returns {TestResult} true if a file is ignored
331
- test(path25, checkUnignored, mode) {
331
+ test(path26, checkUnignored, mode) {
332
332
  let ignored = false;
333
333
  let unignored = false;
334
334
  let matchedRule;
@@ -337,7 +337,7 @@ var require_ignore = __commonJS({
337
337
  if (unignored === negative && ignored !== unignored || negative && !ignored && !unignored && !checkUnignored) {
338
338
  return;
339
339
  }
340
- const matched = rule[mode].test(path25);
340
+ const matched = rule[mode].test(path26);
341
341
  if (!matched) {
342
342
  return;
343
343
  }
@@ -358,17 +358,17 @@ var require_ignore = __commonJS({
358
358
  var throwError = (message, Ctor) => {
359
359
  throw new Ctor(message);
360
360
  };
361
- var checkPath = (path25, originalPath, doThrow) => {
362
- if (!isString(path25)) {
361
+ var checkPath = (path26, originalPath, doThrow) => {
362
+ if (!isString(path26)) {
363
363
  return doThrow(
364
364
  `path must be a string, but got \`${originalPath}\``,
365
365
  TypeError
366
366
  );
367
367
  }
368
- if (!path25) {
368
+ if (!path26) {
369
369
  return doThrow(`path must not be empty`, TypeError);
370
370
  }
371
- if (checkPath.isNotRelative(path25)) {
371
+ if (checkPath.isNotRelative(path26)) {
372
372
  const r = "`path.relative()`d";
373
373
  return doThrow(
374
374
  `path should be a ${r} string, but got "${originalPath}"`,
@@ -377,7 +377,7 @@ var require_ignore = __commonJS({
377
377
  }
378
378
  return true;
379
379
  };
380
- var isNotRelative = (path25) => REGEX_TEST_INVALID_PATH.test(path25);
380
+ var isNotRelative = (path26) => REGEX_TEST_INVALID_PATH.test(path26);
381
381
  checkPath.isNotRelative = isNotRelative;
382
382
  checkPath.convert = (p) => p;
383
383
  var Ignore2 = class {
@@ -407,19 +407,19 @@ var require_ignore = __commonJS({
407
407
  }
408
408
  // @returns {TestResult}
409
409
  _test(originalPath, cache, checkUnignored, slices) {
410
- const path25 = originalPath && checkPath.convert(originalPath);
410
+ const path26 = originalPath && checkPath.convert(originalPath);
411
411
  checkPath(
412
- path25,
412
+ path26,
413
413
  originalPath,
414
414
  this._strictPathCheck ? throwError : RETURN_FALSE
415
415
  );
416
- return this._t(path25, cache, checkUnignored, slices);
416
+ return this._t(path26, cache, checkUnignored, slices);
417
417
  }
418
- checkIgnore(path25) {
419
- if (!REGEX_TEST_TRAILING_SLASH.test(path25)) {
420
- return this.test(path25);
418
+ checkIgnore(path26) {
419
+ if (!REGEX_TEST_TRAILING_SLASH.test(path26)) {
420
+ return this.test(path26);
421
421
  }
422
- const slices = path25.split(SLASH2).filter(Boolean);
422
+ const slices = path26.split(SLASH2).filter(Boolean);
423
423
  slices.pop();
424
424
  if (slices.length) {
425
425
  const parent = this._t(
@@ -432,18 +432,18 @@ var require_ignore = __commonJS({
432
432
  return parent;
433
433
  }
434
434
  }
435
- return this._rules.test(path25, false, MODE_CHECK_IGNORE);
435
+ return this._rules.test(path26, false, MODE_CHECK_IGNORE);
436
436
  }
437
- _t(path25, cache, checkUnignored, slices) {
438
- if (path25 in cache) {
439
- return cache[path25];
437
+ _t(path26, cache, checkUnignored, slices) {
438
+ if (path26 in cache) {
439
+ return cache[path26];
440
440
  }
441
441
  if (!slices) {
442
- slices = path25.split(SLASH2).filter(Boolean);
442
+ slices = path26.split(SLASH2).filter(Boolean);
443
443
  }
444
444
  slices.pop();
445
445
  if (!slices.length) {
446
- return cache[path25] = this._rules.test(path25, checkUnignored, MODE_IGNORE);
446
+ return cache[path26] = this._rules.test(path26, checkUnignored, MODE_IGNORE);
447
447
  }
448
448
  const parent = this._t(
449
449
  slices.join(SLASH2) + SLASH2,
@@ -451,29 +451,29 @@ var require_ignore = __commonJS({
451
451
  checkUnignored,
452
452
  slices
453
453
  );
454
- return cache[path25] = parent.ignored ? parent : this._rules.test(path25, checkUnignored, MODE_IGNORE);
454
+ return cache[path26] = parent.ignored ? parent : this._rules.test(path26, checkUnignored, MODE_IGNORE);
455
455
  }
456
- ignores(path25) {
457
- return this._test(path25, this._ignoreCache, false).ignored;
456
+ ignores(path26) {
457
+ return this._test(path26, this._ignoreCache, false).ignored;
458
458
  }
459
459
  createFilter() {
460
- return (path25) => !this.ignores(path25);
460
+ return (path26) => !this.ignores(path26);
461
461
  }
462
462
  filter(paths) {
463
463
  return makeArray(paths).filter(this.createFilter());
464
464
  }
465
465
  // @returns {TestResult}
466
- test(path25) {
467
- return this._test(path25, this._testCache, true);
466
+ test(path26) {
467
+ return this._test(path26, this._testCache, true);
468
468
  }
469
469
  };
470
470
  var factory = (options) => new Ignore2(options);
471
- var isPathValid = (path25) => checkPath(path25 && checkPath.convert(path25), path25, RETURN_FALSE);
471
+ var isPathValid = (path26) => checkPath(path26 && checkPath.convert(path26), path26, RETURN_FALSE);
472
472
  var setupWindows = () => {
473
473
  const makePosix = (str) => /^\\\\\?\\/.test(str) || /["<>|\u0000-\u001F]+/u.test(str) ? str : str.replace(/\\/g, "/");
474
474
  checkPath.convert = makePosix;
475
475
  const REGEX_TEST_WINDOWS_PATH_ABSOLUTE = /^[a-z]:\//i;
476
- checkPath.isNotRelative = (path25) => REGEX_TEST_WINDOWS_PATH_ABSOLUTE.test(path25) || isNotRelative(path25);
476
+ checkPath.isNotRelative = (path26) => REGEX_TEST_WINDOWS_PATH_ABSOLUTE.test(path26) || isNotRelative(path26);
477
477
  };
478
478
  if (
479
479
  // Detect `process` so that it can run in browsers.
@@ -2043,6 +2043,26 @@ function formatCostEstimate(estimate) {
2043
2043
  \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
2044
2044
  `;
2045
2045
  }
2046
+ function formatDryRunEstimate(estimate) {
2047
+ return `Dry run: parsed the file set to measure the embedding workload. No embedding requests were made and the index was not changed.
2048
+
2049
+ Files to embed: ${estimate.filesCount.toLocaleString()}
2050
+ Chunks to embed: ${estimate.chunksCount.toLocaleString()}
2051
+ Tokens to embed: ${estimate.tokensToEmbed.toLocaleString()}
2052
+
2053
+ The "Tokens to embed" value uses the local estimateTokens(text) = ceil(len/4). It
2054
+ matches the live "Tokens used" counter only for providers that report usage on the
2055
+ same basis (ollama); for providers that report a server tokenizer count (OpenAI,
2056
+ Gemini, custom) it is only an estimate.
2057
+
2058
+ For a matching provider and a project-scoped force index, the force pass clears its
2059
+ own cached embeddings, so the live counter climbs to this number. A force index on a
2060
+ shared global index can reuse cached embeddings from other projects, and an
2061
+ incremental index counts cached chunks that are not re-embedded; in both cases this
2062
+ number is an upper bound on the live counter, so a progress percent against this
2063
+ total tops out below 100%.
2064
+ `;
2065
+ }
2046
2066
  function formatBytes(bytes) {
2047
2067
  if (bytes === 0) return "0 B";
2048
2068
  const k = 1024;
@@ -2221,8 +2241,8 @@ function formatCodeCommunities(result) {
2221
2241
  }
2222
2242
 
2223
2243
  // src/tools/operations.ts
2224
- import { existsSync as existsSync12, realpathSync as realpathSync5, statSync as statSync5 } from "fs";
2225
- import * as path20 from "path";
2244
+ import { existsSync as existsSync13, realpathSync as realpathSync6, statSync as statSync5 } from "fs";
2245
+ import * as path21 from "path";
2226
2246
 
2227
2247
  // src/tools/knowledge-base-paths.ts
2228
2248
  import * as path9 from "path";
@@ -2934,8 +2954,8 @@ function formatExactSearchHandoff(results) {
2934
2954
  }
2935
2955
  function formatContextEvidence(result, index) {
2936
2956
  const symbol = result.name ? ` ${JSON.stringify(compactEvidenceValue(result.name, 80))}` : "";
2937
- const path25 = compactEvidenceValue(result.filePath, 120);
2938
- return `[${index}] ${result.chunkType}${symbol} in ${path25}:${result.startLine}-${result.endLine} (score ${result.score.toFixed(2)})`;
2957
+ const path26 = compactEvidenceValue(result.filePath, 120);
2958
+ return `[${index}] ${result.chunkType}${symbol} in ${path26}:${result.startLine}-${result.endLine} (score ${result.score.toFixed(2)})`;
2939
2959
  }
2940
2960
  function formatContextPack(heading, selected, candidateCount, duplicateCount, limitOmittedCount, budgetOmittedCount, includeExactSearchHandoff) {
2941
2961
  const lines = selected.map((result, index) => formatContextEvidence(result, index + 1));
@@ -3577,9 +3597,9 @@ function formatEffectivenessMetrics(snapshot) {
3577
3597
  }
3578
3598
 
3579
3599
  // src/utils/auto-index.ts
3580
- import { existsSync as existsSync8, realpathSync as realpathSync3 } from "fs";
3581
- import * as os4 from "os";
3582
- import * as path12 from "path";
3600
+ import { existsSync as existsSync9, realpathSync as realpathSync4 } from "fs";
3601
+ import * as os5 from "os";
3602
+ import * as path13 from "path";
3583
3603
 
3584
3604
  // src/indexer/index-lock.ts
3585
3605
  import { randomUUID } from "crypto";
@@ -3836,7 +3856,7 @@ function removeDeadReclaimMarker(lockPath, expectedOwner) {
3836
3856
  return true;
3837
3857
  }
3838
3858
  function reclaimDeadOwner(indexPath, lockPath, expectedOwner) {
3839
- const reclaimPath = path10.join(lockPath, RECLAIM_DIRECTORY_NAME);
3859
+ const reclaimPath2 = path10.join(lockPath, RECLAIM_DIRECTORY_NAME);
3840
3860
  const reclaimOwner = {
3841
3861
  pid: process.pid,
3842
3862
  hostname: os3.hostname(),
@@ -3845,19 +3865,19 @@ function reclaimDeadOwner(indexPath, lockPath, expectedOwner) {
3845
3865
  expectedOwnerToken: expectedOwner.token
3846
3866
  };
3847
3867
  for (let attempt = 0; attempt < 2; attempt += 1) {
3848
- if (publishJsonDirectory(reclaimPath, reclaimOwner)) break;
3868
+ if (publishJsonDirectory(reclaimPath2, reclaimOwner)) break;
3849
3869
  if (attempt === 0 && removeDeadReclaimMarker(lockPath, expectedOwner)) continue;
3850
3870
  return false;
3851
3871
  }
3852
3872
  try {
3853
- const currentReclaimer = readReclaimOwner(reclaimPath);
3873
+ const currentReclaimer = readReclaimOwner(reclaimPath2);
3854
3874
  const currentOwner = readDirectoryOwner(lockPath);
3855
3875
  if (!currentReclaimer || !sameReclaimOwner(currentReclaimer, reclaimOwner) || !currentOwner || !sameOwner(currentOwner, expectedOwner) || getOwnerLiveness(currentOwner) !== "dead") {
3856
3876
  return false;
3857
3877
  }
3858
3878
  publishRecoveryMarker(indexPath, expectedOwner);
3859
3879
  const ownerBeforeQuarantine = readDirectoryOwner(lockPath);
3860
- const reclaimerBeforeQuarantine = readReclaimOwner(reclaimPath);
3880
+ const reclaimerBeforeQuarantine = readReclaimOwner(reclaimPath2);
3861
3881
  if (!ownerBeforeQuarantine || !sameOwner(ownerBeforeQuarantine, expectedOwner) || getOwnerLiveness(ownerBeforeQuarantine) !== "dead" || !reclaimerBeforeQuarantine || !sameReclaimOwner(reclaimerBeforeQuarantine, reclaimOwner)) {
3862
3882
  return false;
3863
3883
  }
@@ -4027,10 +4047,893 @@ function completeLeaseRecovery(lease) {
4027
4047
  }
4028
4048
  }
4029
4049
 
4050
+ // src/utils/background-worker.ts
4051
+ import { createHash, randomUUID as randomUUID2 } from "crypto";
4052
+ import {
4053
+ existsSync as existsSync7,
4054
+ lstatSync as lstatSync2,
4055
+ mkdirSync as mkdirSync2,
4056
+ readFileSync as readFileSync6,
4057
+ realpathSync as realpathSync3,
4058
+ renameSync as renameSync2,
4059
+ rmSync as rmSync2,
4060
+ writeFileSync as writeFileSync2
4061
+ } from "fs";
4062
+ import * as os4 from "os";
4063
+ import * as path11 from "path";
4064
+ var OWNER_FILE_NAME2 = "owner.json";
4065
+ var HEARTBEAT_FILE_PREFIX = "heartbeat.";
4066
+ var RECLAIM_DIRECTORY_NAME2 = "reclaim";
4067
+ var REFRESH_REQUEST_FILE_NAME = "refresh-request.json";
4068
+ var HEARTBEAT_INTERVAL_MS = 5e3;
4069
+ var STALE_LEASE_MS = 3e4;
4070
+ var RETRY_DELAY_MS = 5e3;
4071
+ 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;
4072
+ var BackgroundWorkerStopError = class extends Error {
4073
+ constructor(watcherError, autoIndexError) {
4074
+ super("Failed to stop background worker");
4075
+ this.watcherError = watcherError;
4076
+ this.autoIndexError = autoIndexError;
4077
+ this.name = "BackgroundWorkerStopError";
4078
+ }
4079
+ watcherError;
4080
+ autoIndexError;
4081
+ };
4082
+ var workers = /* @__PURE__ */ new Map();
4083
+ var workerKeysByProject = /* @__PURE__ */ new Map();
4084
+ var workerReplacementBarriers = /* @__PURE__ */ new Map();
4085
+ function getErrorCode2(error) {
4086
+ return typeof error === "object" && error !== null && "code" in error ? String(error.code) : void 0;
4087
+ }
4088
+ function canonicalizePath(targetPath) {
4089
+ const resolved = path11.resolve(targetPath);
4090
+ if (existsSync7(resolved)) {
4091
+ try {
4092
+ return realpathSync3.native(resolved);
4093
+ } catch {
4094
+ return resolved;
4095
+ }
4096
+ }
4097
+ const parent = path11.dirname(resolved);
4098
+ if (parent === resolved) return resolved;
4099
+ return path11.join(canonicalizePath(parent), path11.basename(resolved));
4100
+ }
4101
+ function projectLookupKey(projectRoot3, host) {
4102
+ return `${host}::${canonicalizePath(projectRoot3)}`;
4103
+ }
4104
+ function resolveIdentity(projectRoot3, config, host) {
4105
+ const canonicalProjectRoot = canonicalizePath(projectRoot3);
4106
+ const canonicalIndexPath = canonicalizePath(resolveProjectIndexPath(projectRoot3, config.scope, host));
4107
+ return {
4108
+ canonicalIndexPath,
4109
+ canonicalProjectRoot,
4110
+ key: `${canonicalIndexPath}::${canonicalProjectRoot}`
4111
+ };
4112
+ }
4113
+ function controllerKey(identity, host) {
4114
+ return `${identity.key}::${host}`;
4115
+ }
4116
+ function leaseDirectoryName(identity) {
4117
+ const hash = createHash("sha256").update(identity.key).digest("hex").slice(0, 32);
4118
+ return `background-worker.${hash}.lease`;
4119
+ }
4120
+ function leasePathFor(identity) {
4121
+ return path11.join(identity.canonicalIndexPath, leaseDirectoryName(identity));
4122
+ }
4123
+ function parseOwner2(value) {
4124
+ if (typeof value !== "object" || value === null) return null;
4125
+ const candidate = value;
4126
+ if (candidate.version !== 1) return null;
4127
+ if (!Number.isInteger(candidate.pid) || (candidate.pid ?? 0) <= 0) return null;
4128
+ if (typeof candidate.hostname !== "string" || candidate.hostname.length === 0) return null;
4129
+ if (typeof candidate.startedAt !== "string" || Number.isNaN(Date.parse(candidate.startedAt))) return null;
4130
+ if (typeof candidate.heartbeatAt !== "string" || Number.isNaN(Date.parse(candidate.heartbeatAt))) return null;
4131
+ if (typeof candidate.projectRoot !== "string" || candidate.projectRoot.length === 0) return null;
4132
+ if (typeof candidate.indexPath !== "string" || candidate.indexPath.length === 0) return null;
4133
+ if (typeof candidate.token !== "string" || !UUID_PATTERN2.test(candidate.token)) return null;
4134
+ return candidate;
4135
+ }
4136
+ function parseHeartbeat(value, expectedToken) {
4137
+ if (typeof value !== "object" || value === null) return null;
4138
+ const candidate = value;
4139
+ if (candidate.version !== 1 || candidate.token !== expectedToken) return null;
4140
+ if (typeof candidate.heartbeatAt !== "string" || Number.isNaN(Date.parse(candidate.heartbeatAt))) return null;
4141
+ return candidate;
4142
+ }
4143
+ function parseReclaimOwner2(value) {
4144
+ if (typeof value !== "object" || value === null) return null;
4145
+ const candidate = value;
4146
+ if (candidate.version !== 1) return null;
4147
+ if (!Number.isInteger(candidate.pid) || (candidate.pid ?? 0) <= 0) return null;
4148
+ if (typeof candidate.hostname !== "string" || candidate.hostname.length === 0) return null;
4149
+ if (typeof candidate.startedAt !== "string" || Number.isNaN(Date.parse(candidate.startedAt))) return null;
4150
+ if (typeof candidate.token !== "string" || !UUID_PATTERN2.test(candidate.token)) return null;
4151
+ if (candidate.expectedOwnerToken !== null && (typeof candidate.expectedOwnerToken !== "string" || !UUID_PATTERN2.test(candidate.expectedOwnerToken))) return null;
4152
+ return candidate;
4153
+ }
4154
+ function heartbeatPath(leasePath, token) {
4155
+ return path11.join(leasePath, `${HEARTBEAT_FILE_PREFIX}${token}.json`);
4156
+ }
4157
+ function reclaimPath(leasePath) {
4158
+ return path11.join(leasePath, RECLAIM_DIRECTORY_NAME2);
4159
+ }
4160
+ function refreshRequestPath(leasePath) {
4161
+ return path11.join(leasePath, REFRESH_REQUEST_FILE_NAME);
4162
+ }
4163
+ function readLeaseOwner(leasePath) {
4164
+ try {
4165
+ return parseOwner2(JSON.parse(readFileSync6(path11.join(leasePath, OWNER_FILE_NAME2), "utf-8")));
4166
+ } catch {
4167
+ return null;
4168
+ }
4169
+ }
4170
+ function readOwner(leasePath) {
4171
+ const owner = readLeaseOwner(leasePath);
4172
+ if (!owner) return null;
4173
+ try {
4174
+ const heartbeat = parseHeartbeat(
4175
+ JSON.parse(readFileSync6(heartbeatPath(leasePath, owner.token), "utf-8")),
4176
+ owner.token
4177
+ );
4178
+ return heartbeat ? { ...owner, heartbeatAt: heartbeat.heartbeatAt } : owner;
4179
+ } catch {
4180
+ return owner;
4181
+ }
4182
+ }
4183
+ function readReclaimOwner2(leasePath) {
4184
+ try {
4185
+ return parseReclaimOwner2(JSON.parse(readFileSync6(path11.join(reclaimPath(leasePath), OWNER_FILE_NAME2), "utf-8")));
4186
+ } catch {
4187
+ return null;
4188
+ }
4189
+ }
4190
+ function ownerLiveness(owner) {
4191
+ if (owner.hostname !== os4.hostname()) return "unknown";
4192
+ try {
4193
+ process.kill(owner.pid, 0);
4194
+ return "alive";
4195
+ } catch (error) {
4196
+ const code = getErrorCode2(error);
4197
+ if (code === "ESRCH") return "dead";
4198
+ if (code === "EPERM") return "alive";
4199
+ return "unknown";
4200
+ }
4201
+ }
4202
+ function isHeartbeatExpired(owner) {
4203
+ return Date.now() - Date.parse(owner.heartbeatAt) >= STALE_LEASE_MS;
4204
+ }
4205
+ function sameOwner2(left, right) {
4206
+ return left.pid === right.pid && left.hostname === right.hostname && left.token === right.token;
4207
+ }
4208
+ function writeHeartbeat(leasePath, owner) {
4209
+ const targetPath = heartbeatPath(leasePath, owner.token);
4210
+ const temporaryPath = `${targetPath}.tmp.${process.pid}.${owner.token}.${randomUUID2()}`;
4211
+ const heartbeat = {
4212
+ version: 1,
4213
+ token: owner.token,
4214
+ heartbeatAt: owner.heartbeatAt
4215
+ };
4216
+ try {
4217
+ writeFileSync2(temporaryPath, JSON.stringify(heartbeat), {
4218
+ encoding: "utf-8",
4219
+ flag: "wx",
4220
+ mode: 384
4221
+ });
4222
+ renameSync2(temporaryPath, targetPath);
4223
+ const currentOwner = readLeaseOwner(leasePath);
4224
+ return currentOwner !== null && sameOwner2(currentOwner, owner);
4225
+ } finally {
4226
+ if (existsSync7(temporaryPath)) rmSync2(temporaryPath, { force: true });
4227
+ }
4228
+ }
4229
+ function requestRefreshFromLeader(leasePath, allowDisabledAutoIndex) {
4230
+ const requestPath = refreshRequestPath(leasePath);
4231
+ const temporaryPath = `${requestPath}.tmp.${process.pid}.${randomUUID2()}`;
4232
+ try {
4233
+ const request = {
4234
+ allowDisabledAutoIndex,
4235
+ requestedAt: (/* @__PURE__ */ new Date()).toISOString(),
4236
+ version: 1
4237
+ };
4238
+ writeFileSync2(temporaryPath, JSON.stringify(request), {
4239
+ encoding: "utf-8",
4240
+ flag: "wx",
4241
+ mode: 384
4242
+ });
4243
+ renameSync2(temporaryPath, requestPath);
4244
+ } catch (error) {
4245
+ if (getErrorCode2(error) !== "ENOENT") {
4246
+ console.error("[codebase-index] Failed to request background index refresh from the project worker:", error);
4247
+ }
4248
+ } finally {
4249
+ if (existsSync7(temporaryPath)) rmSync2(temporaryPath, { force: true });
4250
+ }
4251
+ }
4252
+ function consumeRefreshRequest(leasePath) {
4253
+ const requestPath = refreshRequestPath(leasePath);
4254
+ const claimedPath = `${requestPath}.handling.${process.pid}.${randomUUID2()}`;
4255
+ try {
4256
+ renameSync2(requestPath, claimedPath);
4257
+ } catch (error) {
4258
+ if (getErrorCode2(error) === "ENOENT") return null;
4259
+ throw error;
4260
+ }
4261
+ try {
4262
+ const value = JSON.parse(readFileSync6(claimedPath, "utf-8"));
4263
+ return {
4264
+ allowDisabledAutoIndex: value.version === 1 && value.allowDisabledAutoIndex === true,
4265
+ requestedAt: typeof value.requestedAt === "string" ? value.requestedAt : (/* @__PURE__ */ new Date()).toISOString(),
4266
+ version: 1
4267
+ };
4268
+ } catch {
4269
+ return { allowDisabledAutoIndex: false, requestedAt: (/* @__PURE__ */ new Date()).toISOString(), version: 1 };
4270
+ } finally {
4271
+ rmSync2(claimedPath, { force: true });
4272
+ }
4273
+ }
4274
+ function publishLease(leasePath, owner) {
4275
+ const candidatePath = `${leasePath}.candidate.${process.pid}.${owner.token}`;
4276
+ try {
4277
+ mkdirSync2(candidatePath, { mode: 448 });
4278
+ } catch (error) {
4279
+ if (getErrorCode2(error) === "ENOENT") return false;
4280
+ throw error;
4281
+ }
4282
+ try {
4283
+ writeFileSync2(path11.join(candidatePath, OWNER_FILE_NAME2), JSON.stringify(owner), {
4284
+ encoding: "utf-8",
4285
+ flag: "wx",
4286
+ mode: 384
4287
+ });
4288
+ if (existsSync7(leasePath)) return false;
4289
+ try {
4290
+ renameSync2(candidatePath, leasePath);
4291
+ return true;
4292
+ } catch (error) {
4293
+ if (existsSync7(leasePath) || getErrorCode2(error) === "ENOENT") return false;
4294
+ throw error;
4295
+ }
4296
+ } finally {
4297
+ if (existsSync7(candidatePath)) rmSync2(candidatePath, { recursive: true, force: true });
4298
+ }
4299
+ }
4300
+ function sameReclaimOwner2(left, right) {
4301
+ return left.pid === right.pid && left.hostname === right.hostname && left.token === right.token && left.expectedOwnerToken === right.expectedOwnerToken;
4302
+ }
4303
+ function reclaimerLiveness(owner) {
4304
+ return ownerLiveness(owner);
4305
+ }
4306
+ function isReclaimMarkerExpired(leasePath, owner) {
4307
+ const startedAt = owner ? Date.parse(owner.startedAt) : (() => {
4308
+ try {
4309
+ return lstatSync2(reclaimPath(leasePath)).mtimeMs;
4310
+ } catch {
4311
+ return Date.now();
4312
+ }
4313
+ })();
4314
+ return Date.now() - startedAt >= STALE_LEASE_MS;
4315
+ }
4316
+ function hasActiveReclaimMarker(leasePath, owner) {
4317
+ const marker = readReclaimOwner2(leasePath);
4318
+ return marker !== null && marker.expectedOwnerToken === owner.token && (marker.hostname !== os4.hostname() || ownerLiveness(owner) !== "alive");
4319
+ }
4320
+ function publishReclaimMarker(leasePath, expectedOwner) {
4321
+ const markerPath = reclaimPath(leasePath);
4322
+ const owner = {
4323
+ version: 1,
4324
+ pid: process.pid,
4325
+ hostname: os4.hostname(),
4326
+ startedAt: (/* @__PURE__ */ new Date()).toISOString(),
4327
+ token: randomUUID2(),
4328
+ expectedOwnerToken: expectedOwner?.token ?? null
4329
+ };
4330
+ try {
4331
+ mkdirSync2(markerPath, { mode: 448 });
4332
+ } catch (error) {
4333
+ if (getErrorCode2(error) === "EEXIST" || getErrorCode2(error) === "ENOENT") return null;
4334
+ throw error;
4335
+ }
4336
+ try {
4337
+ writeFileSync2(path11.join(markerPath, OWNER_FILE_NAME2), JSON.stringify(owner), {
4338
+ encoding: "utf-8",
4339
+ flag: "wx",
4340
+ mode: 384
4341
+ });
4342
+ return owner;
4343
+ } catch (error) {
4344
+ rmSync2(markerPath, { recursive: true, force: true });
4345
+ throw error;
4346
+ }
4347
+ }
4348
+ function removeExpiredReclaimMarker(leasePath, expectedOwner) {
4349
+ const marker = readReclaimOwner2(leasePath);
4350
+ const markerPath = reclaimPath(leasePath);
4351
+ if (!existsSync7(markerPath)) return false;
4352
+ if (marker && marker.expectedOwnerToken !== (expectedOwner?.token ?? null)) return false;
4353
+ if (marker && (reclaimerLiveness(marker) === "alive" || !isReclaimMarkerExpired(leasePath, marker))) return false;
4354
+ if (!marker && !isReclaimMarkerExpired(leasePath, null)) return false;
4355
+ const staleMarkerPath = `${markerPath}.stale.${marker?.pid ?? process.pid}.${marker?.token ?? randomUUID2()}.${randomUUID2()}`;
4356
+ try {
4357
+ renameSync2(markerPath, staleMarkerPath);
4358
+ } catch (error) {
4359
+ if (getErrorCode2(error) === "ENOENT") return false;
4360
+ throw error;
4361
+ }
4362
+ try {
4363
+ let claimedMarker = null;
4364
+ try {
4365
+ claimedMarker = parseReclaimOwner2(
4366
+ JSON.parse(readFileSync6(path11.join(staleMarkerPath, OWNER_FILE_NAME2), "utf-8"))
4367
+ );
4368
+ } catch {
4369
+ claimedMarker = null;
4370
+ }
4371
+ const markerMatches = marker ? claimedMarker !== null && sameReclaimOwner2(claimedMarker, marker) : claimedMarker === null;
4372
+ if (!markerMatches || !canReclaimLease(leasePath, expectedOwner)) {
4373
+ if (!existsSync7(markerPath) && existsSync7(staleMarkerPath)) renameSync2(staleMarkerPath, markerPath);
4374
+ return false;
4375
+ }
4376
+ rmSync2(staleMarkerPath, { recursive: true, force: true });
4377
+ return true;
4378
+ } catch (error) {
4379
+ if (getErrorCode2(error) === "ENOENT") return false;
4380
+ throw error;
4381
+ }
4382
+ }
4383
+ function canReclaimLease(leasePath, expectedOwner) {
4384
+ if (!existsSync7(leasePath)) return false;
4385
+ if (!expectedOwner) return false;
4386
+ const currentOwner = readOwner(leasePath);
4387
+ if (!currentOwner || !sameOwner2(currentOwner, expectedOwner)) return false;
4388
+ if (currentOwner.hostname === os4.hostname()) {
4389
+ return ownerLiveness(currentOwner) === "dead";
4390
+ }
4391
+ return isHeartbeatExpired(currentOwner);
4392
+ }
4393
+ function reclaimLease(leasePath, expectedOwner) {
4394
+ let marker = null;
4395
+ for (let attempt = 0; attempt < 2; attempt += 1) {
4396
+ marker = publishReclaimMarker(leasePath, expectedOwner);
4397
+ if (marker) break;
4398
+ if (attempt === 0 && removeExpiredReclaimMarker(leasePath, expectedOwner)) continue;
4399
+ return false;
4400
+ }
4401
+ if (!marker) return false;
4402
+ const markerPath = reclaimPath(leasePath);
4403
+ try {
4404
+ const currentMarker = readReclaimOwner2(leasePath);
4405
+ if (!currentMarker || !sameReclaimOwner2(currentMarker, marker) || !canReclaimLease(leasePath, expectedOwner)) {
4406
+ return false;
4407
+ }
4408
+ const stalePath = `${leasePath}.stale.${process.pid}.${marker.token}`;
4409
+ renameSync2(leasePath, stalePath);
4410
+ const quarantinedOwner = readOwner(stalePath);
4411
+ const quarantinedMarker = readReclaimOwner2(stalePath);
4412
+ if (!quarantinedMarker || !sameReclaimOwner2(quarantinedMarker, marker) || expectedOwner !== null && (!quarantinedOwner || !sameOwner2(quarantinedOwner, expectedOwner))) {
4413
+ if (!existsSync7(leasePath) && existsSync7(stalePath)) renameSync2(stalePath, leasePath);
4414
+ return false;
4415
+ }
4416
+ rmSync2(stalePath, { recursive: true, force: true });
4417
+ return true;
4418
+ } catch (error) {
4419
+ if (getErrorCode2(error) === "ENOENT") return false;
4420
+ throw error;
4421
+ } finally {
4422
+ const currentMarker = readReclaimOwner2(leasePath);
4423
+ if (currentMarker && sameReclaimOwner2(currentMarker, marker)) {
4424
+ rmSync2(markerPath, { recursive: true, force: true });
4425
+ }
4426
+ }
4427
+ }
4428
+ function acquireLease(identity) {
4429
+ mkdirSync2(identity.canonicalIndexPath, { recursive: true, mode: 448 });
4430
+ const canonicalIndexPath = realpathSync3.native(identity.canonicalIndexPath);
4431
+ const leasePath = path11.join(canonicalIndexPath, leaseDirectoryName({ ...identity, canonicalIndexPath }));
4432
+ for (let attempt = 0; attempt < 4; attempt += 1) {
4433
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
4434
+ const owner = {
4435
+ version: 1,
4436
+ pid: process.pid,
4437
+ hostname: os4.hostname(),
4438
+ startedAt: timestamp,
4439
+ heartbeatAt: timestamp,
4440
+ projectRoot: identity.canonicalProjectRoot,
4441
+ indexPath: canonicalIndexPath,
4442
+ token: randomUUID2()
4443
+ };
4444
+ if (publishLease(leasePath, owner)) {
4445
+ return { leasePath, owner };
4446
+ }
4447
+ const existingOwner = readOwner(leasePath);
4448
+ if (existingOwner) {
4449
+ if (canReclaimLease(leasePath, existingOwner) && reclaimLease(leasePath, existingOwner)) continue;
4450
+ return null;
4451
+ }
4452
+ return null;
4453
+ }
4454
+ return null;
4455
+ }
4456
+ function releaseLease(lease) {
4457
+ const currentOwner = readOwner(lease.leasePath);
4458
+ if (!currentOwner || !sameOwner2(currentOwner, lease.owner)) return false;
4459
+ const releasePath = `${lease.leasePath}.release.${lease.owner.pid}.${lease.owner.token}`;
4460
+ try {
4461
+ renameSync2(lease.leasePath, releasePath);
4462
+ } catch (error) {
4463
+ if (getErrorCode2(error) === "ENOENT") return false;
4464
+ throw error;
4465
+ }
4466
+ const claimedOwner = readOwner(releasePath);
4467
+ if (!claimedOwner || !sameOwner2(claimedOwner, lease.owner)) {
4468
+ if (!existsSync7(lease.leasePath) && existsSync7(releasePath)) {
4469
+ renameSync2(releasePath, lease.leasePath);
4470
+ }
4471
+ return false;
4472
+ }
4473
+ rmSync2(releasePath, { recursive: true, force: true });
4474
+ return true;
4475
+ }
4476
+ var BackgroundWorkerController = class {
4477
+ constructor(projectRoot3, host, config, hooks, identity) {
4478
+ this.projectRoot = projectRoot3;
4479
+ this.host = host;
4480
+ this.config = config;
4481
+ this.hooks = hooks;
4482
+ this.identity = identity;
4483
+ }
4484
+ projectRoot;
4485
+ host;
4486
+ config;
4487
+ hooks;
4488
+ identity;
4489
+ lease = null;
4490
+ watcher = null;
4491
+ leaderReady = Promise.resolve();
4492
+ heartbeatTimer = null;
4493
+ retryTimer = null;
4494
+ teardownRetryTimer = null;
4495
+ transition = Promise.resolve();
4496
+ stopPromise = null;
4497
+ stopped = false;
4498
+ stopping = false;
4499
+ losingLeadership = false;
4500
+ restartAfterStop = false;
4501
+ leaderWorkStopped = false;
4502
+ startingLeaderWork = false;
4503
+ stopAutoIndexOnTeardown = true;
4504
+ autoIndexStarted = false;
4505
+ reportedError = null;
4506
+ update(config, hooks, options) {
4507
+ const autoIndexWasEnabled = this.config.indexing.autoIndex;
4508
+ const shouldReplaceWatcher = this.watcher !== null && hooks.watcherFactory !== void 0 && (hooks.watcherFactory === null || hooks.replaceWatcher === true);
4509
+ this.config = config;
4510
+ this.hooks = {
4511
+ ...this.hooks,
4512
+ ...hooks,
4513
+ watcherFactory: hooks.watcherFactory === void 0 ? this.hooks.watcherFactory : hooks.watcherFactory,
4514
+ watcherFactoryForConfig: hooks.watcherFactoryForConfig === void 0 ? this.hooks.watcherFactoryForConfig : hooks.watcherFactoryForConfig
4515
+ };
4516
+ if (autoIndexWasEnabled && !config.indexing.autoIndex || options.restartAutoIndex === true && config.indexing.autoIndex && !this.startingLeaderWork) {
4517
+ this.autoIndexStarted = false;
4518
+ }
4519
+ if (!this.canRun()) {
4520
+ void this.stop().catch((error) => {
4521
+ console.error("[codebase-index] Failed to stop background worker after disabling automatic work:", error);
4522
+ });
4523
+ return;
4524
+ }
4525
+ if (shouldReplaceWatcher) {
4526
+ void this.enqueue(async () => {
4527
+ const watcher = this.watcher;
4528
+ if (watcher) {
4529
+ await watcher.stop();
4530
+ if (this.watcher === watcher) this.watcher = null;
4531
+ }
4532
+ if (this.lease && !this.stopped) this.startLeaderWork();
4533
+ }).catch((error) => {
4534
+ console.error("[codebase-index] Failed to replace background file watcher:", error);
4535
+ });
4536
+ }
4537
+ this.start();
4538
+ }
4539
+ startAfter(activation) {
4540
+ this.transition = activation.catch(() => void 0);
4541
+ this.start();
4542
+ }
4543
+ start() {
4544
+ if (!this.canRun() || this.losingLeadership) return;
4545
+ if (this.stopping) {
4546
+ this.restartAfterStop = true;
4547
+ return;
4548
+ }
4549
+ this.stopped = false;
4550
+ void this.enqueue(async () => {
4551
+ if (this.stopped || this.stopping || this.losingLeadership || !this.canRun()) return;
4552
+ if (!this.lease) {
4553
+ try {
4554
+ this.lease = acquireLease(this.identity);
4555
+ this.reportedError = null;
4556
+ } catch (error) {
4557
+ this.reportAcquireError(error);
4558
+ this.scheduleRetry();
4559
+ return;
4560
+ }
4561
+ }
4562
+ if (!this.lease) {
4563
+ this.scheduleRetry();
4564
+ return;
4565
+ }
4566
+ this.startHeartbeat();
4567
+ this.startLeaderWork();
4568
+ });
4569
+ }
4570
+ waitForStart() {
4571
+ return this.transition.catch(() => void 0).then(() => this.leaderReady);
4572
+ }
4573
+ requestRefresh(allowDisabledAutoIndex = false) {
4574
+ this.start();
4575
+ if (!this.isLeader()) {
4576
+ requestRefreshFromLeader(leasePathFor(this.identity), allowDisabledAutoIndex);
4577
+ return;
4578
+ }
4579
+ void this.enqueue(async () => {
4580
+ if (this.stopped || !this.lease) return;
4581
+ this.hooks.startAutoIndex("retrieval", allowDisabledAutoIndex);
4582
+ });
4583
+ }
4584
+ isLeader() {
4585
+ return this.lease !== null && !this.stopping && !this.losingLeadership;
4586
+ }
4587
+ isStopping() {
4588
+ return this.stopping;
4589
+ }
4590
+ getHooksForConfig(config) {
4591
+ const watcherFactoryForConfig = this.hooks.watcherFactoryForConfig;
4592
+ if (!watcherFactoryForConfig) return this.hooks;
4593
+ return {
4594
+ ...this.hooks,
4595
+ watcherFactory: watcherFactoryForConfig(config),
4596
+ replaceWatcher: true
4597
+ };
4598
+ }
4599
+ attachWatcher(watcherFactory, watcherFactoryForConfig) {
4600
+ if (this.hooks.watcherFactory !== void 0) return;
4601
+ this.hooks = {
4602
+ ...this.hooks,
4603
+ watcherFactory,
4604
+ watcherFactoryForConfig: watcherFactoryForConfig ?? this.hooks.watcherFactoryForConfig
4605
+ };
4606
+ this.start();
4607
+ }
4608
+ async stop(stopAutoIndex = true) {
4609
+ if (this.stopPromise) return this.stopPromise;
4610
+ this.stopped = true;
4611
+ this.stopping = true;
4612
+ this.stopAutoIndexOnTeardown &&= stopAutoIndex;
4613
+ this.clearRetryTimer();
4614
+ const attempt = this.enqueue(async () => {
4615
+ try {
4616
+ const lease = this.lease;
4617
+ if (this.leaderWorkStopped) {
4618
+ if (lease) {
4619
+ this.releaseStoppedLease(lease);
4620
+ } else {
4621
+ this.finishStoppedLease();
4622
+ }
4623
+ return;
4624
+ }
4625
+ const hadLeaderWork = lease !== null || this.watcher !== null || this.autoIndexStarted;
4626
+ const stopped = await this.stopLeaderWork(hadLeaderWork && this.stopAutoIndexOnTeardown);
4627
+ if (!lease) {
4628
+ this.finishStoppedLease();
4629
+ return;
4630
+ }
4631
+ if (!stopped.completed) {
4632
+ this.releaseLeaseWhenAutoIndexStops(lease, stopped.completion);
4633
+ return;
4634
+ }
4635
+ this.leaderWorkStopped = true;
4636
+ this.releaseStoppedLease(lease);
4637
+ } catch (error) {
4638
+ this.scheduleTeardownRetry();
4639
+ throw error;
4640
+ }
4641
+ });
4642
+ const completion = attempt.finally(() => {
4643
+ if (this.stopPromise === completion) this.stopPromise = null;
4644
+ });
4645
+ this.stopPromise = completion;
4646
+ return completion;
4647
+ }
4648
+ canRun() {
4649
+ return this.config.indexing.autoIndex || this.hooks.watcherFactory != null;
4650
+ }
4651
+ enqueue(operation) {
4652
+ const next = this.transition.catch(() => void 0).then(operation);
4653
+ this.transition = next;
4654
+ return next;
4655
+ }
4656
+ startLeaderWork() {
4657
+ if (this.stopped || this.stopping || this.losingLeadership) return;
4658
+ this.startingLeaderWork = true;
4659
+ try {
4660
+ if (this.config.indexing.autoIndex && !this.autoIndexStarted) {
4661
+ this.autoIndexStarted = true;
4662
+ this.hooks.startAutoIndex("startup");
4663
+ }
4664
+ if (!this.watcher && this.hooks.watcherFactory) {
4665
+ try {
4666
+ const watcher = this.hooks.watcherFactory();
4667
+ this.watcher = watcher;
4668
+ this.leaderReady = watcher.whenReady?.().catch((error) => {
4669
+ console.error("[codebase-index] Failed while waiting for background file watcher startup:", error);
4670
+ }) ?? Promise.resolve();
4671
+ } catch (error) {
4672
+ console.error("[codebase-index] Failed to start background file watcher:", error);
4673
+ this.leaderReady = Promise.resolve();
4674
+ }
4675
+ }
4676
+ } finally {
4677
+ this.startingLeaderWork = false;
4678
+ }
4679
+ }
4680
+ async stopLeaderWork(stopAutoIndex) {
4681
+ const watcher = this.watcher;
4682
+ let watcherError;
4683
+ if (watcher) {
4684
+ try {
4685
+ await watcher.stop();
4686
+ if (this.watcher === watcher) this.watcher = null;
4687
+ } catch (error) {
4688
+ watcherError = error;
4689
+ }
4690
+ }
4691
+ let autoIndexError;
4692
+ let autoIndexStop = {
4693
+ completed: true,
4694
+ completion: Promise.resolve()
4695
+ };
4696
+ if (stopAutoIndex) {
4697
+ try {
4698
+ autoIndexStop = await this.hooks.stopAutoIndex();
4699
+ this.autoIndexStarted = false;
4700
+ } catch (error) {
4701
+ autoIndexError = error;
4702
+ }
4703
+ }
4704
+ if (watcherError !== void 0 || autoIndexError !== void 0) {
4705
+ throw new BackgroundWorkerStopError(watcherError, autoIndexError);
4706
+ }
4707
+ return autoIndexStop;
4708
+ }
4709
+ releaseLeaseWhenAutoIndexStops(lease, completion) {
4710
+ void completion.then(
4711
+ () => {
4712
+ void this.enqueue(async () => {
4713
+ if (this.lease !== lease || !this.stopping) return;
4714
+ this.leaderWorkStopped = true;
4715
+ this.releaseStoppedLease(lease);
4716
+ }).catch((error) => {
4717
+ console.error("[codebase-index] Failed to release background worker lease after automatic indexing stopped:", error);
4718
+ this.scheduleTeardownRetry();
4719
+ });
4720
+ },
4721
+ (error) => {
4722
+ console.error("[codebase-index] Failed while waiting for automatic indexing to stop:", error);
4723
+ this.scheduleTeardownRetry();
4724
+ }
4725
+ );
4726
+ }
4727
+ releaseStoppedLease(lease) {
4728
+ if (this.lease !== lease) {
4729
+ this.finishStoppedLease();
4730
+ return;
4731
+ }
4732
+ releaseLease(lease);
4733
+ this.lease = null;
4734
+ this.finishStoppedLease();
4735
+ }
4736
+ finishStoppedLease() {
4737
+ this.leaderWorkStopped = false;
4738
+ this.stopAutoIndexOnTeardown = true;
4739
+ this.stopping = false;
4740
+ this.clearTimers();
4741
+ this.restartAfterTeardown();
4742
+ if (!this.stopped || this.stopping) return;
4743
+ const projectKey = projectLookupKey(this.projectRoot, this.host);
4744
+ const key = controllerKey(this.identity, this.host);
4745
+ if (workers.get(key) === this) workers.delete(key);
4746
+ if (workerKeysByProject.get(projectKey) === key) workerKeysByProject.delete(projectKey);
4747
+ }
4748
+ startHeartbeat() {
4749
+ if (this.heartbeatTimer) return;
4750
+ const heartbeat = () => {
4751
+ void this.heartbeat();
4752
+ };
4753
+ this.heartbeatTimer = setInterval(heartbeat, HEARTBEAT_INTERVAL_MS);
4754
+ this.heartbeatTimer.unref?.();
4755
+ }
4756
+ async heartbeat() {
4757
+ const lease = this.lease;
4758
+ if (!lease || this.losingLeadership || this.stopped && !this.stopping) return;
4759
+ if (hasActiveReclaimMarker(lease.leasePath, lease.owner)) {
4760
+ await this.loseLeadership();
4761
+ return;
4762
+ }
4763
+ const currentOwner = readOwner(lease.leasePath);
4764
+ if (!currentOwner || !sameOwner2(currentOwner, lease.owner)) {
4765
+ await this.loseLeadership();
4766
+ return;
4767
+ }
4768
+ try {
4769
+ const nextOwner = { ...lease.owner, heartbeatAt: (/* @__PURE__ */ new Date()).toISOString() };
4770
+ if (!writeHeartbeat(lease.leasePath, nextOwner)) {
4771
+ await this.loseLeadership();
4772
+ return;
4773
+ }
4774
+ lease.owner = nextOwner;
4775
+ const refreshRequest = !this.stopping ? consumeRefreshRequest(lease.leasePath) : null;
4776
+ if (refreshRequest) {
4777
+ this.hooks.startAutoIndex("retrieval", refreshRequest.allowDisabledAutoIndex);
4778
+ }
4779
+ } catch (error) {
4780
+ const ownerAfterError = readOwner(lease.leasePath);
4781
+ if (hasActiveReclaimMarker(lease.leasePath, lease.owner) || !ownerAfterError || !sameOwner2(ownerAfterError, lease.owner)) {
4782
+ await this.loseLeadership();
4783
+ return;
4784
+ }
4785
+ console.error("[codebase-index] Failed to renew background worker lease:", error);
4786
+ }
4787
+ }
4788
+ async loseLeadership() {
4789
+ if (this.losingLeadership) return;
4790
+ this.losingLeadership = true;
4791
+ this.clearHeartbeat();
4792
+ await this.enqueue(async () => this.stopAfterLeadershipLoss());
4793
+ }
4794
+ async stopAfterLeadershipLoss() {
4795
+ const lease = this.lease;
4796
+ if (!lease) {
4797
+ this.losingLeadership = false;
4798
+ return;
4799
+ }
4800
+ try {
4801
+ const stopped = await this.stopLeaderWork(true);
4802
+ this.lease = null;
4803
+ this.losingLeadership = false;
4804
+ if (stopped.completed) {
4805
+ this.scheduleRetry();
4806
+ } else {
4807
+ void stopped.completion.then(() => this.scheduleRetry());
4808
+ }
4809
+ } catch (error) {
4810
+ console.error("[codebase-index] Failed to stop background work after losing its lease:", error);
4811
+ this.scheduleLostLeadershipTeardownRetry();
4812
+ }
4813
+ }
4814
+ scheduleRetry() {
4815
+ if (this.stopped || !this.canRun() || this.retryTimer) return;
4816
+ this.retryTimer = setTimeout(() => {
4817
+ this.retryTimer = null;
4818
+ this.start();
4819
+ }, RETRY_DELAY_MS);
4820
+ this.retryTimer.unref?.();
4821
+ }
4822
+ scheduleTeardownRetry() {
4823
+ if (!this.stopping || this.teardownRetryTimer) return;
4824
+ this.teardownRetryTimer = setTimeout(() => {
4825
+ this.teardownRetryTimer = null;
4826
+ void this.stop(this.stopAutoIndexOnTeardown).catch((error) => {
4827
+ console.error("[codebase-index] Failed to retry background worker teardown:", error);
4828
+ });
4829
+ }, RETRY_DELAY_MS);
4830
+ this.teardownRetryTimer.unref?.();
4831
+ }
4832
+ restartAfterTeardown() {
4833
+ if (!this.restartAfterStop || !this.canRun() || this.losingLeadership) return;
4834
+ this.restartAfterStop = false;
4835
+ this.stopped = false;
4836
+ this.start();
4837
+ }
4838
+ scheduleLostLeadershipTeardownRetry() {
4839
+ if (this.stopped || !this.losingLeadership || this.retryTimer) return;
4840
+ this.retryTimer = setTimeout(() => {
4841
+ this.retryTimer = null;
4842
+ void this.enqueue(async () => this.stopAfterLeadershipLoss());
4843
+ }, RETRY_DELAY_MS);
4844
+ this.retryTimer.unref?.();
4845
+ }
4846
+ clearHeartbeat() {
4847
+ if (!this.heartbeatTimer) return;
4848
+ clearInterval(this.heartbeatTimer);
4849
+ this.heartbeatTimer = null;
4850
+ }
4851
+ clearTimers() {
4852
+ this.clearHeartbeat();
4853
+ this.clearRetryTimer();
4854
+ if (this.teardownRetryTimer) {
4855
+ clearTimeout(this.teardownRetryTimer);
4856
+ this.teardownRetryTimer = null;
4857
+ }
4858
+ }
4859
+ clearRetryTimer() {
4860
+ if (!this.retryTimer) return;
4861
+ clearTimeout(this.retryTimer);
4862
+ this.retryTimer = null;
4863
+ }
4864
+ reportAcquireError(error) {
4865
+ const message = error instanceof Error ? error.message : String(error);
4866
+ if (this.reportedError === message) return;
4867
+ this.reportedError = message;
4868
+ console.error("[codebase-index] Failed to acquire background worker lease:", error);
4869
+ }
4870
+ };
4871
+ function configureBackgroundWorker(projectRoot3, host, config, hooks, options = {}) {
4872
+ const projectKey = projectLookupKey(projectRoot3, host);
4873
+ const identity = resolveIdentity(projectRoot3, config, host);
4874
+ const key = controllerKey(identity, host);
4875
+ const previousKey = workerKeysByProject.get(projectKey);
4876
+ if (previousKey && previousKey !== key) {
4877
+ const previous = workers.get(previousKey);
4878
+ const previousBarrier = workerReplacementBarriers.get(projectKey) ?? Promise.resolve();
4879
+ const stopPrevious = previous?.stop(options.stopPreviousAutoIndex ?? true) ?? Promise.resolve();
4880
+ const activation = Promise.all([previousBarrier, stopPrevious]).then(() => void 0);
4881
+ workerReplacementBarriers.set(projectKey, activation);
4882
+ workers.delete(previousKey);
4883
+ const worker2 = new BackgroundWorkerController(projectRoot3, host, config, hooks, identity);
4884
+ worker2.startAfter(activation);
4885
+ workers.set(key, worker2);
4886
+ workerKeysByProject.set(projectKey, key);
4887
+ return;
4888
+ }
4889
+ let worker = workers.get(key);
4890
+ if (!worker) {
4891
+ worker = new BackgroundWorkerController(projectRoot3, host, config, hooks, identity);
4892
+ workers.set(key, worker);
4893
+ } else {
4894
+ worker.update(config, hooks, options);
4895
+ }
4896
+ workerKeysByProject.set(projectKey, key);
4897
+ worker.start();
4898
+ }
4899
+ function updateBackgroundWorkerConfig(projectRoot3, host, config) {
4900
+ const projectKey = projectLookupKey(projectRoot3, host);
4901
+ const key = workerKeysByProject.get(projectKey);
4902
+ const worker = key ? workers.get(key) : void 0;
4903
+ if (!worker) return;
4904
+ configureBackgroundWorker(projectRoot3, host, config, worker.getHooksForConfig(config), {
4905
+ stopPreviousAutoIndex: false,
4906
+ restartAutoIndex: true
4907
+ });
4908
+ }
4909
+ function waitForBackgroundWorkerStart(projectRoot3, host) {
4910
+ const key = workerKeysByProject.get(projectLookupKey(projectRoot3, host));
4911
+ return workers.get(key ?? "")?.waitForStart() ?? Promise.resolve();
4912
+ }
4913
+ function requestBackgroundWorkerRefresh(projectRoot3, host, allowDisabledAutoIndex = false) {
4914
+ const key = workerKeysByProject.get(projectLookupKey(projectRoot3, host));
4915
+ workers.get(key ?? "")?.requestRefresh(allowDisabledAutoIndex);
4916
+ }
4917
+ function isBackgroundWorkerManaged(projectRoot3, host) {
4918
+ const key = workerKeysByProject.get(projectLookupKey(projectRoot3, host));
4919
+ return key !== void 0 && workers.has(key);
4920
+ }
4921
+ function isBackgroundWorkerLeader(projectRoot3, host) {
4922
+ const key = workerKeysByProject.get(projectLookupKey(projectRoot3, host));
4923
+ return key !== void 0 && workers.get(key)?.isLeader() === true;
4924
+ }
4925
+ async function stopBackgroundWorker(projectRoot3, host) {
4926
+ const projectKey = projectLookupKey(projectRoot3, host);
4927
+ const key = workerKeysByProject.get(projectKey);
4928
+ const worker = key ? workers.get(key) : void 0;
4929
+ if (!worker) return;
4930
+ await worker.stop();
4931
+ }
4932
+
4030
4933
  // src/utils/files.ts
4031
4934
  var import_ignore = __toESM(require_ignore(), 1);
4032
- import { existsSync as existsSync7, readFileSync as readFileSync6, promises as fsPromises } from "fs";
4033
- import * as path11 from "path";
4935
+ import { existsSync as existsSync8, readFileSync as readFileSync7, promises as fsPromises } from "fs";
4936
+ import * as path12 from "path";
4034
4937
  var PROJECT_MARKERS = [
4035
4938
  ".git",
4036
4939
  "package.json",
@@ -4048,7 +4951,7 @@ var PROJECT_MARKERS = [
4048
4951
  ];
4049
4952
  function hasProjectMarker(projectRoot3) {
4050
4953
  for (const marker of PROJECT_MARKERS) {
4051
- if (existsSync7(path11.join(projectRoot3, marker))) {
4954
+ if (existsSync8(path12.join(projectRoot3, marker))) {
4052
4955
  return true;
4053
4956
  }
4054
4957
  }
@@ -4075,33 +4978,53 @@ function createIgnoreFilter(projectRoot3) {
4075
4978
  "**/*build*/**"
4076
4979
  ];
4077
4980
  ig.add(defaultIgnores);
4078
- const gitignorePath = path11.join(projectRoot3, ".gitignore");
4079
- if (existsSync7(gitignorePath)) {
4080
- const gitignoreContent = readFileSync6(gitignorePath, "utf-8");
4981
+ const gitignorePath = path12.join(projectRoot3, ".gitignore");
4982
+ if (existsSync8(gitignorePath)) {
4983
+ const gitignoreContent = readFileSync7(gitignorePath, "utf-8");
4081
4984
  ig.add(gitignoreContent);
4082
4985
  }
4083
4986
  return ig;
4084
4987
  }
4085
- function shouldIncludeFile(filePath, projectRoot3, includePatterns, excludePatterns, ignoreFilter) {
4086
- const relativePath = path11.relative(projectRoot3, filePath);
4087
- if (hasFilteredPathSegment(relativePath, path11.sep)) {
4088
- return false;
4089
- }
4090
- if (ignoreFilter.ignores(relativePath)) {
4091
- return false;
4988
+ function toPosixRelativePath(relativePath) {
4989
+ return relativePath.split(path12.sep).join("/");
4990
+ }
4991
+ function matchesAnyGlob(filePath, patterns) {
4992
+ const normalized = toPosixRelativePath(filePath);
4993
+ return patterns.some((pattern) => matchGlob(normalized, pattern));
4994
+ }
4995
+ function isExcludedByPatterns(relativePath, excludePatterns) {
4996
+ return matchesAnyGlob(relativePath, excludePatterns);
4997
+ }
4998
+ function isExcludedDirectory(relativePath, excludePatterns) {
4999
+ const normalized = toPosixRelativePath(relativePath);
5000
+ if (matchesAnyGlob(normalized, excludePatterns)) {
5001
+ return true;
4092
5002
  }
4093
5003
  for (const pattern of excludePatterns) {
4094
- if (matchGlob(relativePath, pattern)) {
4095
- return false;
5004
+ const posixPattern = toPosixRelativePath(pattern).replace(/\/+$/, "");
5005
+ if (!posixPattern.endsWith("/**")) {
5006
+ continue;
4096
5007
  }
4097
- }
4098
- for (const pattern of includePatterns) {
4099
- if (matchGlob(relativePath, pattern)) {
5008
+ const directoryPattern = posixPattern.slice(0, -3);
5009
+ if (directoryPattern && matchesAnyGlob(normalized, [directoryPattern])) {
4100
5010
  return true;
4101
5011
  }
4102
5012
  }
4103
5013
  return false;
4104
5014
  }
5015
+ function shouldIncludeFile(filePath, projectRoot3, includePatterns, excludePatterns, ignoreFilter) {
5016
+ const relativePath = toPosixRelativePath(path12.relative(projectRoot3, filePath));
5017
+ if (hasFilteredPathSegment(relativePath, "/")) {
5018
+ return false;
5019
+ }
5020
+ if (ignoreFilter.ignores(relativePath)) {
5021
+ return false;
5022
+ }
5023
+ if (isExcludedByPatterns(relativePath, excludePatterns)) {
5024
+ return false;
5025
+ }
5026
+ return matchesAnyGlob(relativePath, includePatterns);
5027
+ }
4105
5028
  function matchGlob(filePath, pattern) {
4106
5029
  if (pattern.startsWith("**/")) {
4107
5030
  const withoutPrefix = pattern.slice(3);
@@ -4122,8 +5045,8 @@ async function* walkDirectory(dir, projectRoot3, includePatterns, excludePattern
4122
5045
  const filesInDir = [];
4123
5046
  const subdirs = [];
4124
5047
  for (const entry of entries) {
4125
- const fullPath = path11.join(dir, entry.name);
4126
- const relativePath = path11.relative(projectRoot3, fullPath);
5048
+ const fullPath = path12.join(dir, entry.name);
5049
+ const relativePath = toPosixRelativePath(path12.relative(projectRoot3, fullPath));
4127
5050
  if (isHiddenPathSegment(entry.name)) {
4128
5051
  if (entry.isDirectory()) {
4129
5052
  skipped.push({ path: relativePath, reason: "excluded" });
@@ -4141,6 +5064,10 @@ async function* walkDirectory(dir, projectRoot3, includePatterns, excludePattern
4141
5064
  continue;
4142
5065
  }
4143
5066
  if (entry.isDirectory()) {
5067
+ if (isExcludedDirectory(relativePath, excludePatterns)) {
5068
+ skipped.push({ path: relativePath, reason: "excluded" });
5069
+ continue;
5070
+ }
4144
5071
  subdirs.push({ fullPath, relativePath });
4145
5072
  } else if (entry.isFile()) {
4146
5073
  const stat5 = await fsPromises.stat(fullPath);
@@ -4148,20 +5075,11 @@ async function* walkDirectory(dir, projectRoot3, includePatterns, excludePattern
4148
5075
  skipped.push({ path: relativePath, reason: "too_large" });
4149
5076
  continue;
4150
5077
  }
4151
- for (const pattern of excludePatterns) {
4152
- if (matchGlob(relativePath, pattern)) {
4153
- skipped.push({ path: relativePath, reason: "excluded" });
4154
- continue;
4155
- }
4156
- }
4157
- let matched = false;
4158
- for (const pattern of includePatterns) {
4159
- if (matchGlob(relativePath, pattern)) {
4160
- matched = true;
4161
- break;
4162
- }
5078
+ if (isExcludedByPatterns(relativePath, excludePatterns)) {
5079
+ skipped.push({ path: relativePath, reason: "excluded" });
5080
+ continue;
4163
5081
  }
4164
- if (matched) {
5082
+ if (matchesAnyGlob(relativePath, includePatterns)) {
4165
5083
  filesInDir.push({ path: fullPath, size: stat5.size });
4166
5084
  }
4167
5085
  }
@@ -4172,7 +5090,7 @@ async function* walkDirectory(dir, projectRoot3, includePatterns, excludePattern
4172
5090
  yield f;
4173
5091
  }
4174
5092
  for (let i = options.maxFilesPerDirectory; i < filesInDir.length; i++) {
4175
- skipped.push({ path: path11.relative(projectRoot3, filesInDir[i].path), reason: "excluded" });
5093
+ skipped.push({ path: toPosixRelativePath(path12.relative(projectRoot3, filesInDir[i].path)), reason: "excluded" });
4176
5094
  }
4177
5095
  const canRecurse = options.maxDepth === -1 || currentDepth < options.maxDepth;
4178
5096
  if (canRecurse) {
@@ -4212,8 +5130,8 @@ async function collectFiles(projectRoot3, includePatterns, excludePatterns, maxF
4212
5130
  if (additionalRoots && additionalRoots.length > 0) {
4213
5131
  const normalizedRoots = /* @__PURE__ */ new Set();
4214
5132
  for (const kbRoot of additionalRoots) {
4215
- const resolved = path11.normalize(
4216
- path11.isAbsolute(kbRoot) ? kbRoot : path11.resolve(projectRoot3, kbRoot)
5133
+ const resolved = path12.normalize(
5134
+ path12.isAbsolute(kbRoot) ? kbRoot : path12.resolve(projectRoot3, kbRoot)
4217
5135
  );
4218
5136
  normalizedRoots.add(resolved);
4219
5137
  }
@@ -4254,7 +5172,7 @@ function getErrorMessage(error) {
4254
5172
  return error instanceof Error ? error.message : String(error);
4255
5173
  }
4256
5174
  function runCommand(file, args, options) {
4257
- return new Promise((resolve17, reject) => {
5175
+ return new Promise((resolve18, reject) => {
4258
5176
  childProcess.execFile(
4259
5177
  file,
4260
5178
  args,
@@ -4264,7 +5182,7 @@ function runCommand(file, args, options) {
4264
5182
  reject(error);
4265
5183
  return;
4266
5184
  }
4267
- resolve17(stdout);
5185
+ resolve18(stdout);
4268
5186
  }
4269
5187
  );
4270
5188
  });
@@ -4356,29 +5274,29 @@ var AutoIndexCancelledError = class extends Error {
4356
5274
  function now() {
4357
5275
  return (/* @__PURE__ */ new Date()).toISOString();
4358
5276
  }
4359
- function canonicalizePath(targetPath) {
4360
- const resolved = path12.resolve(targetPath);
4361
- if (existsSync8(resolved)) {
5277
+ function canonicalizePath2(targetPath) {
5278
+ const resolved = path13.resolve(targetPath);
5279
+ if (existsSync9(resolved)) {
4362
5280
  try {
4363
- return realpathSync3.native(resolved);
5281
+ return realpathSync4.native(resolved);
4364
5282
  } catch {
4365
5283
  return resolved;
4366
5284
  }
4367
5285
  }
4368
- const parent = path12.dirname(resolved);
5286
+ const parent = path13.dirname(resolved);
4369
5287
  if (parent === resolved) return resolved;
4370
- return path12.join(canonicalizePath(parent), path12.basename(resolved));
5288
+ return path13.join(canonicalizePath2(parent), path13.basename(resolved));
4371
5289
  }
4372
5290
  function isHomeDirectory(projectRoot3) {
4373
- return canonicalizePath(projectRoot3) === canonicalizePath(os4.homedir());
5291
+ return canonicalizePath2(projectRoot3) === canonicalizePath2(os5.homedir());
4374
5292
  }
4375
- function projectLookupKey(projectRoot3, host) {
4376
- return `${host}::${canonicalizePath(projectRoot3)}`;
5293
+ function projectLookupKey2(projectRoot3, host) {
5294
+ return `${host}::${canonicalizePath2(projectRoot3)}`;
4377
5295
  }
4378
5296
  function coordinatorKey(projectRoot3, config, host) {
4379
- const canonicalProjectRoot = canonicalizePath(projectRoot3);
5297
+ const canonicalProjectRoot = canonicalizePath2(projectRoot3);
4380
5298
  const indexPath = resolveProjectIndexPath(projectRoot3, config.scope, host);
4381
- return `${canonicalizePath(indexPath)}::${canonicalProjectRoot}`;
5299
+ return `${canonicalizePath2(indexPath)}::${canonicalProjectRoot}`;
4382
5300
  }
4383
5301
  function getProjectSafety(projectRoot3, config) {
4384
5302
  if (isHomeDirectory(projectRoot3)) {
@@ -4409,10 +5327,10 @@ function safeFailureMessage(error) {
4409
5327
  }
4410
5328
  function cancellableDelay(delayMs, signal) {
4411
5329
  if (signal.aborted) return Promise.reject(new AutoIndexCancelledError());
4412
- return new Promise((resolve17, reject) => {
5330
+ return new Promise((resolve18, reject) => {
4413
5331
  const timer = setTimeout(() => {
4414
5332
  signal.removeEventListener("abort", onAbort);
4415
- resolve17();
5333
+ resolve18();
4416
5334
  }, delayMs);
4417
5335
  timer.unref?.();
4418
5336
  const onAbort = () => {
@@ -4424,18 +5342,44 @@ function cancellableDelay(delayMs, signal) {
4424
5342
  }
4425
5343
  function withTimeout(promise, timeoutMs) {
4426
5344
  if (timeoutMs <= 0) return Promise.resolve(void 0);
4427
- return new Promise((resolve17) => {
4428
- const timer = setTimeout(() => resolve17(void 0), timeoutMs);
5345
+ return new Promise((resolve18) => {
5346
+ const timer = setTimeout(() => resolve18(void 0), timeoutMs);
4429
5347
  timer.unref?.();
4430
5348
  void promise.then((value) => {
4431
5349
  clearTimeout(timer);
4432
- resolve17(value);
5350
+ resolve18(value);
4433
5351
  }, () => {
4434
5352
  clearTimeout(timer);
4435
- resolve17(void 0);
5353
+ resolve18(void 0);
4436
5354
  });
4437
5355
  });
4438
5356
  }
5357
+ function settlesWithin(promise, timeoutMs) {
5358
+ if (timeoutMs <= 0) return Promise.resolve(false);
5359
+ return new Promise((resolve18) => {
5360
+ let settled = false;
5361
+ const timer = setTimeout(() => {
5362
+ if (settled) return;
5363
+ settled = true;
5364
+ resolve18(false);
5365
+ }, timeoutMs);
5366
+ timer.unref?.();
5367
+ void promise.then(
5368
+ () => {
5369
+ if (settled) return;
5370
+ settled = true;
5371
+ clearTimeout(timer);
5372
+ resolve18(true);
5373
+ },
5374
+ () => {
5375
+ if (settled) return;
5376
+ settled = true;
5377
+ clearTimeout(timer);
5378
+ resolve18(true);
5379
+ }
5380
+ );
5381
+ });
5382
+ }
4439
5383
  function requestPriority(request) {
4440
5384
  if (request.force) return 4;
4441
5385
  if (request.source === "manual") return 3;
@@ -4446,6 +5390,7 @@ function mergeRequests(current, next) {
4446
5390
  if (!current) return next;
4447
5391
  const preferred = requestPriority(next) > requestPriority(current) ? next : current;
4448
5392
  return {
5393
+ allowDisabledAutoIndex: current.allowDisabledAutoIndex || next.allowDisabledAutoIndex,
4449
5394
  checkFreshness: current.checkFreshness && next.checkFreshness,
4450
5395
  force: current.force || next.force,
4451
5396
  onProgress: next.onProgress ?? current.onProgress,
@@ -4507,11 +5452,11 @@ var AutoIndexCoordinator = class {
4507
5452
  progress: this.status.progress ? { ...this.status.progress } : void 0
4508
5453
  };
4509
5454
  }
4510
- start(source) {
5455
+ start(source, allowDisabledAutoIndex = false) {
4511
5456
  this.refreshSafety();
4512
- if (!this.registration.config.indexing.autoIndex || !this.registration.safeToRun) return null;
5457
+ if (!this.registration.config.indexing.autoIndex && !allowDisabledAutoIndex || !this.registration.safeToRun) return null;
4513
5458
  if (this.status.state === "failed") return this.inFlight;
4514
- return this.request({ checkFreshness: true, force: false, source });
5459
+ return this.request({ allowDisabledAutoIndex, checkFreshness: true, force: false, source });
4515
5460
  }
4516
5461
  request(request) {
4517
5462
  if (this.stopped) {
@@ -4586,13 +5531,15 @@ var AutoIndexCoordinator = class {
4586
5531
  retryAttempt: void 0
4587
5532
  });
4588
5533
  const inFlight = this.inFlight;
4589
- if (inFlight) {
4590
- if (waitForCompletion) {
4591
- await inFlight;
4592
- } else {
4593
- await withTimeout(inFlight, SHUTDOWN_WAIT_MS);
4594
- }
5534
+ const completion = inFlight ? inFlight.then(() => void 0, () => void 0) : Promise.resolve();
5535
+ if (!inFlight) {
5536
+ return { completed: true, completion };
4595
5537
  }
5538
+ if (waitForCompletion) {
5539
+ await completion;
5540
+ return { completed: true, completion };
5541
+ }
5542
+ return { completed: await settlesWithin(completion, SHUTDOWN_WAIT_MS), completion };
4596
5543
  }
4597
5544
  startRequest(request) {
4598
5545
  if (this.stopped || !this.canRun(request)) {
@@ -4781,7 +5728,7 @@ var AutoIndexCoordinator = class {
4781
5728
  if (request.source === "manual" || request.source === "watcher") {
4782
5729
  return true;
4783
5730
  }
4784
- return this.registration.safeToRun && this.registration.config.indexing.autoIndex;
5731
+ return this.registration.safeToRun && (this.registration.config.indexing.autoIndex || request.allowDisabledAutoIndex === true);
4785
5732
  }
4786
5733
  shouldDeferForBattery(request) {
4787
5734
  return this.registration.backgroundIndexingPolicy !== null && (request.source === "startup" || request.source === "watcher");
@@ -4814,17 +5761,17 @@ var AutoIndexCoordinator = class {
4814
5761
  }
4815
5762
  }
4816
5763
  waitForBatteryRetry(delayMs) {
4817
- return new Promise((resolve17) => {
5764
+ return new Promise((resolve18) => {
4818
5765
  const timer = setTimeout(() => {
4819
5766
  if (this.batteryRetryTimer === timer) {
4820
5767
  this.batteryRetryTimer = null;
4821
5768
  this.resolveBatteryRetry = null;
4822
5769
  }
4823
- resolve17();
5770
+ resolve18();
4824
5771
  }, delayMs);
4825
5772
  timer.unref?.();
4826
5773
  this.batteryRetryTimer = timer;
4827
- this.resolveBatteryRetry = resolve17;
5774
+ this.resolveBatteryRetry = resolve18;
4828
5775
  });
4829
5776
  }
4830
5777
  cancelBatteryRetry() {
@@ -4832,9 +5779,9 @@ var AutoIndexCoordinator = class {
4832
5779
  clearTimeout(this.batteryRetryTimer);
4833
5780
  this.batteryRetryTimer = null;
4834
5781
  }
4835
- const resolve17 = this.resolveBatteryRetry;
5782
+ const resolve18 = this.resolveBatteryRetry;
4836
5783
  this.resolveBatteryRetry = null;
4837
- resolve17?.();
5784
+ resolve18?.();
4838
5785
  }
4839
5786
  finishBatteryCheck(batteryCheck) {
4840
5787
  if (this.batteryCheck !== batteryCheck) return;
@@ -4847,12 +5794,25 @@ var AutoIndexCoordinator = class {
4847
5794
  }
4848
5795
  };
4849
5796
  function getCoordinator(projectRoot3, host) {
4850
- const key = coordinatorKeysByProject.get(projectLookupKey(projectRoot3, host));
5797
+ const key = coordinatorKeysByProject.get(projectLookupKey2(projectRoot3, host));
4851
5798
  return key ? coordinators.get(key) ?? null : null;
4852
5799
  }
4853
- function configureAutoIndex(projectRoot3, host, config, getIndexer) {
4854
- const projectKey = projectLookupKey(projectRoot3, host);
5800
+ function synchronizeBackgroundWorker(projectRoot3, host, config, safeToRun) {
5801
+ if (safeToRun) {
5802
+ updateBackgroundWorkerConfig(projectRoot3, host, config);
5803
+ return;
5804
+ }
5805
+ void stopBackgroundWorker(projectRoot3, host).catch((error) => {
5806
+ console.error("[codebase-index] Failed to stop background worker after project safety changed:", error);
5807
+ });
5808
+ }
5809
+ function configureAutoIndex(projectRoot3, host, config, getIndexer, options = {}) {
5810
+ const projectKey = projectLookupKey2(projectRoot3, host);
4855
5811
  const safety = getProjectSafety(projectRoot3, config);
5812
+ const synchronizeWorker = options.synchronizeBackgroundWorker ?? true;
5813
+ if (options.preserveManagedWorker === true && isBackgroundWorkerManaged(projectRoot3, host)) {
5814
+ return;
5815
+ }
4856
5816
  const registration = {
4857
5817
  backgroundIndexingPolicy: createBackgroundIndexingPolicy(
4858
5818
  config.indexing.pauseBackgroundIndexingOnBattery
@@ -4870,6 +5830,9 @@ function configureAutoIndex(projectRoot3, host, config, getIndexer) {
4870
5830
  const stopPrevious = previousCoordinator?.stop(true) ?? Promise.resolve();
4871
5831
  const activation = Promise.all([previousBarrier, stopPrevious]).then(() => void 0);
4872
5832
  coordinatorReplacementBarriers.set(projectKey, activation);
5833
+ if (synchronizeWorker) {
5834
+ synchronizeBackgroundWorker(projectRoot3, host, config, safety.safeToRun);
5835
+ }
4873
5836
  coordinators.delete(previousKey);
4874
5837
  const coordinator2 = new AutoIndexCoordinator(registration);
4875
5838
  coordinator2.activateAfter(activation);
@@ -4885,8 +5848,17 @@ function configureAutoIndex(projectRoot3, host, config, getIndexer) {
4885
5848
  coordinator.update(registration);
4886
5849
  }
4887
5850
  coordinatorKeysByProject.set(projectKey, key);
5851
+ if (synchronizeWorker) {
5852
+ synchronizeBackgroundWorker(projectRoot3, host, config, safety.safeToRun);
5853
+ }
5854
+ }
5855
+ function startAutoIndexForBackgroundWorker(projectRoot3, host, source = "startup", allowDisabledAutoIndex = false) {
5856
+ return getCoordinator(projectRoot3, host)?.start(source, allowDisabledAutoIndex) ?? null;
4888
5857
  }
4889
5858
  function requestBackgroundIndex(projectRoot3, host) {
5859
+ if (isBackgroundWorkerManaged(projectRoot3, host) && !isBackgroundWorkerLeader(projectRoot3, host)) {
5860
+ return null;
5861
+ }
4890
5862
  return getCoordinator(projectRoot3, host)?.request({
4891
5863
  checkFreshness: false,
4892
5864
  force: false,
@@ -4926,15 +5898,23 @@ async function waitForAutoIndexForRetrieval(projectRoot3, host) {
4926
5898
  };
4927
5899
  }
4928
5900
  try {
4929
- if (await hasReadableCurrentIndex(coordinator)) return { ready: true };
5901
+ const readiness = await getSearchReadiness(coordinator);
5902
+ if (readiness.searchable) {
5903
+ return { ready: true };
5904
+ }
5905
+ if (readiness.blocked) return unavailableSnapshotResult(readiness.reason);
4930
5906
  } catch {
4931
5907
  }
4932
- const job = coordinator.start("retrieval") ?? coordinator.currentJob();
5908
+ const job = startRetrievalRefresh(projectRoot3, host, coordinator);
4933
5909
  if (job) {
4934
5910
  await withTimeout(job, coordinator.getWaitMs());
5911
+ } else if (isBackgroundWorkerManaged(projectRoot3, host)) {
5912
+ await waitForPublishedSnapshot(coordinator, coordinator.getWaitMs());
4935
5913
  }
4936
5914
  try {
4937
- if (await hasReadableCurrentIndex(coordinator)) return { ready: true };
5915
+ const readiness = await getSearchReadiness(coordinator);
5916
+ if (readiness.searchable) return { ready: true };
5917
+ if (readiness.blocked) return unavailableSnapshotResult(readiness.reason);
4938
5918
  } catch {
4939
5919
  }
4940
5920
  const status = coordinator.snapshot();
@@ -4955,21 +5935,52 @@ async function waitForAutoIndexForRetrieval(projectRoot3, host) {
4955
5935
  text: `Automatic indexing is ${status.state}. Retry shortly or call index_status for progress. You can also run index_codebase explicitly.`
4956
5936
  };
4957
5937
  }
4958
- async function stopAutoIndex(projectRoot3, host) {
4959
- await getCoordinator(projectRoot3, host)?.stop();
5938
+ async function stopAutoIndexForBackgroundWorker(projectRoot3, host, waitForCompletion = false) {
5939
+ const coordinator = getCoordinator(projectRoot3, host);
5940
+ if (!coordinator) {
5941
+ return { completed: true, completion: Promise.resolve() };
5942
+ }
5943
+ return coordinator.stop(waitForCompletion);
4960
5944
  }
4961
- async function hasReadableCurrentIndex(coordinator) {
5945
+ async function getSearchReadiness(coordinator) {
4962
5946
  const indexer = coordinator.getIndexer();
4963
5947
  if (indexer.getIndexFreshness) {
4964
5948
  const freshness = await indexer.getIndexFreshness();
4965
- return freshness.readable && freshness.current;
5949
+ const searchable = freshness.readable && freshness.current && freshness.reason === "current";
5950
+ return {
5951
+ blocked: freshness.reason === "unreadable" || freshness.reason === "incompatible" || freshness.reason === "failed-batches" || freshness.reason === "migration-required",
5952
+ reason: freshness.reason,
5953
+ searchable
5954
+ };
5955
+ }
5956
+ const indexed = (await indexer.getStatus()).indexed;
5957
+ return { blocked: false, searchable: indexed };
5958
+ }
5959
+ function unavailableSnapshotResult(reason) {
5960
+ 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.";
5961
+ return {
5962
+ ready: false,
5963
+ text: `${detail} Run index_codebase before retrying retrieval.`
5964
+ };
5965
+ }
5966
+ function startRetrievalRefresh(projectRoot3, host, coordinator) {
5967
+ if (isBackgroundWorkerManaged(projectRoot3, host)) {
5968
+ requestBackgroundWorkerRefresh(projectRoot3, host, true);
5969
+ return isBackgroundWorkerLeader(projectRoot3, host) ? coordinator.currentJob() : null;
5970
+ }
5971
+ return coordinator.start("retrieval") ?? coordinator.currentJob();
5972
+ }
5973
+ async function waitForPublishedSnapshot(coordinator, waitMs) {
5974
+ const deadline = Date.now() + waitMs;
5975
+ while (Date.now() < deadline) {
5976
+ if ((await getSearchReadiness(coordinator)).searchable) return;
5977
+ await new Promise((resolve18) => setTimeout(resolve18, Math.min(250, deadline - Date.now())));
4966
5978
  }
4967
- return (await indexer.getStatus()).indexed;
4968
5979
  }
4969
5980
 
4970
5981
  // src/tools/config-state.ts
4971
- import { existsSync as existsSync9, mkdirSync as mkdirSync2, writeFileSync as writeFileSync2 } from "fs";
4972
- import * as path13 from "path";
5982
+ import { existsSync as existsSync10, mkdirSync as mkdirSync3, writeFileSync as writeFileSync3 } from "fs";
5983
+ import * as path14 from "path";
4973
5984
  function normalizeKnowledgeBasePaths(config, projectRoot3) {
4974
5985
  const normalized = { ...config };
4975
5986
  if (Array.isArray(normalized.knowledgeBases)) {
@@ -4996,10 +6007,10 @@ function loadEditableConfig(projectRoot3, host) {
4996
6007
  }
4997
6008
  function saveConfig(projectRoot3, config, host) {
4998
6009
  const configPath = getConfigPath(projectRoot3, host);
4999
- const configDir = path13.dirname(configPath);
5000
- const configBaseDir = path13.dirname(configDir);
5001
- if (!existsSync9(configDir)) {
5002
- mkdirSync2(configDir, { recursive: true });
6010
+ const configDir = path14.dirname(configPath);
6011
+ const configBaseDir = path14.dirname(configDir);
6012
+ if (!existsSync10(configDir)) {
6013
+ mkdirSync3(configDir, { recursive: true });
5003
6014
  }
5004
6015
  const serializableConfig = { ...config };
5005
6016
  if (Array.isArray(serializableConfig.knowledgeBases)) {
@@ -5007,12 +6018,12 @@ function saveConfig(projectRoot3, config, host) {
5007
6018
  (kb) => serializeConfigPathValue(kb, configBaseDir)
5008
6019
  );
5009
6020
  }
5010
- writeFileSync2(configPath, JSON.stringify(serializableConfig, null, 2) + "\n", "utf-8");
6021
+ writeFileSync3(configPath, JSON.stringify(serializableConfig, null, 2) + "\n", "utf-8");
5011
6022
  }
5012
6023
 
5013
6024
  // src/indexer/index.ts
5014
- import { existsSync as existsSync11, readFileSync as readFileSync8, statSync as statSync4, writeFileSync as writeFileSync3, renameSync as renameSync3, unlinkSync as unlinkSync2, mkdirSync as mkdirSync4, promises as fsPromises3 } from "fs";
5015
- import * as path19 from "path";
6025
+ import { existsSync as existsSync12, readFileSync as readFileSync9, statSync as statSync4, writeFileSync as writeFileSync4, renameSync as renameSync4, unlinkSync as unlinkSync2, mkdirSync as mkdirSync5, promises as fsPromises3 } from "fs";
6026
+ import * as path20 from "path";
5016
6027
  import { performance as performance2 } from "perf_hooks";
5017
6028
  import { execFile as execFile5 } from "child_process";
5018
6029
  import { promisify as promisify4 } from "util";
@@ -5039,7 +6050,7 @@ function pTimeout(promise, options) {
5039
6050
  } = options;
5040
6051
  let timer;
5041
6052
  let abortHandler;
5042
- const wrappedPromise = new Promise((resolve17, reject) => {
6053
+ const wrappedPromise = new Promise((resolve18, reject) => {
5043
6054
  if (typeof milliseconds !== "number" || Math.sign(milliseconds) !== 1) {
5044
6055
  throw new TypeError(`Expected \`milliseconds\` to be a positive number, got \`${milliseconds}\``);
5045
6056
  }
@@ -5053,7 +6064,7 @@ function pTimeout(promise, options) {
5053
6064
  };
5054
6065
  signal.addEventListener("abort", abortHandler, { once: true });
5055
6066
  }
5056
- promise.then(resolve17, reject);
6067
+ promise.then(resolve18, reject);
5057
6068
  if (milliseconds === Number.POSITIVE_INFINITY) {
5058
6069
  return;
5059
6070
  }
@@ -5061,7 +6072,7 @@ function pTimeout(promise, options) {
5061
6072
  timer = customTimers.setTimeout.call(void 0, () => {
5062
6073
  if (fallback) {
5063
6074
  try {
5064
- resolve17(fallback());
6075
+ resolve18(fallback());
5065
6076
  } catch (error) {
5066
6077
  reject(error);
5067
6078
  }
@@ -5071,7 +6082,7 @@ function pTimeout(promise, options) {
5071
6082
  promise.cancel();
5072
6083
  }
5073
6084
  if (message === false) {
5074
- resolve17();
6085
+ resolve18();
5075
6086
  } else if (message instanceof Error) {
5076
6087
  reject(message);
5077
6088
  } else {
@@ -5473,7 +6484,7 @@ var PQueue = class extends import_index.default {
5473
6484
  // Assign unique ID if not provided
5474
6485
  id: options.id ?? (this.#idAssigner++).toString()
5475
6486
  };
5476
- return new Promise((resolve17, reject) => {
6487
+ return new Promise((resolve18, reject) => {
5477
6488
  const taskSymbol = /* @__PURE__ */ Symbol(`task-${options.id}`);
5478
6489
  let cleanupQueueAbortHandler = () => void 0;
5479
6490
  const run = async () => {
@@ -5513,7 +6524,7 @@ var PQueue = class extends import_index.default {
5513
6524
  })]);
5514
6525
  }
5515
6526
  const result = await operation;
5516
- resolve17(result);
6527
+ resolve18(result);
5517
6528
  this.emit("completed", result);
5518
6529
  } catch (error) {
5519
6530
  reject(error);
@@ -5701,13 +6712,13 @@ var PQueue = class extends import_index.default {
5701
6712
  });
5702
6713
  }
5703
6714
  async #onEvent(event, filter) {
5704
- return new Promise((resolve17) => {
6715
+ return new Promise((resolve18) => {
5705
6716
  const listener = () => {
5706
6717
  if (filter && !filter()) {
5707
6718
  return;
5708
6719
  }
5709
6720
  this.off(event, listener);
5710
- resolve17();
6721
+ resolve18();
5711
6722
  };
5712
6723
  this.on(event, listener);
5713
6724
  });
@@ -5993,7 +7004,7 @@ async function onAttemptFailure({ error, attemptNumber, retriesConsumed, startTi
5993
7004
  const finalDelay = Math.min(delayTime, remainingTime);
5994
7005
  options.signal?.throwIfAborted();
5995
7006
  if (finalDelay > 0) {
5996
- await new Promise((resolve17, reject) => {
7007
+ await new Promise((resolve18, reject) => {
5997
7008
  const onAbort = () => {
5998
7009
  clearTimeout(timeoutToken);
5999
7010
  options.signal?.removeEventListener("abort", onAbort);
@@ -6001,7 +7012,7 @@ async function onAttemptFailure({ error, attemptNumber, retriesConsumed, startTi
6001
7012
  };
6002
7013
  const timeoutToken = setTimeout(() => {
6003
7014
  options.signal?.removeEventListener("abort", onAbort);
6004
- resolve17();
7015
+ resolve18();
6005
7016
  }, finalDelay);
6006
7017
  if (options.unref) {
6007
7018
  timeoutToken.unref?.();
@@ -6119,17 +7130,17 @@ function validateExternalUrl(urlString) {
6119
7130
  if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
6120
7131
  return { valid: false, reason: `Blocked protocol: ${parsed.protocol}` };
6121
7132
  }
6122
- const hostname2 = parsed.hostname.toLowerCase();
6123
- if (BLOCKED_HOSTNAMES.has(hostname2)) {
6124
- return { valid: false, reason: `Blocked: cloud metadata service (${hostname2})` };
7133
+ const hostname3 = parsed.hostname.toLowerCase();
7134
+ if (BLOCKED_HOSTNAMES.has(hostname3)) {
7135
+ return { valid: false, reason: `Blocked: cloud metadata service (${hostname3})` };
6125
7136
  }
6126
7137
  for (const pattern of BLOCKED_METADATA_IPS) {
6127
- if (pattern.test(hostname2)) {
6128
- return { valid: false, reason: `Blocked: cloud metadata IP (${hostname2})` };
7138
+ if (pattern.test(hostname3)) {
7139
+ return { valid: false, reason: `Blocked: cloud metadata IP (${hostname3})` };
6129
7140
  }
6130
7141
  }
6131
- if (/^169\.254\./.test(hostname2)) {
6132
- return { valid: false, reason: `Blocked: link-local address (${hostname2})` };
7142
+ if (/^169\.254\./.test(hostname3)) {
7143
+ return { valid: false, reason: `Blocked: link-local address (${hostname3})` };
6133
7144
  }
6134
7145
  return { valid: true };
6135
7146
  }
@@ -7141,8 +8152,8 @@ function extractParamNames(params) {
7141
8152
  }
7142
8153
 
7143
8154
  // src/native/binding.ts
7144
- import * as os5 from "os";
7145
- import * as path14 from "path";
8155
+ import * as os6 from "os";
8156
+ import * as path15 from "path";
7146
8157
  import * as module from "module";
7147
8158
  import { fileURLToPath } from "url";
7148
8159
 
@@ -7178,7 +8189,7 @@ var MCP_BINARY_CURRENT_NAME = CURRENT_PRODUCT.mcpBinary;
7178
8189
  var STABLE_NATIVE_BINARY_NAME = IDENTITY_CATALOG.native.binaryName;
7179
8190
 
7180
8191
  // src/native/binding.ts
7181
- function getNativeBindingFilename(platform2 = os5.platform(), arch2 = os5.arch()) {
8192
+ function getNativeBindingFilename(platform2 = os6.platform(), arch2 = os6.arch()) {
7182
8193
  if (platform2 === "darwin" && arch2 === "arm64") {
7183
8194
  return `${STABLE_NATIVE_BINARY_NAME}.darwin-arm64.node`;
7184
8195
  }
@@ -7196,25 +8207,25 @@ function getNativeBindingFilename(platform2 = os5.platform(), arch2 = os5.arch()
7196
8207
  }
7197
8208
  throw new Error(`Unsupported platform: ${platform2}-${arch2}`);
7198
8209
  }
7199
- function resolveNativeBindingPath(packageRoot, platform2 = os5.platform(), arch2 = os5.arch()) {
7200
- return path14.join(packageRoot, "native", getNativeBindingFilename(platform2, arch2));
8210
+ function resolveNativeBindingPath(packageRoot, platform2 = os6.platform(), arch2 = os6.arch()) {
8211
+ return path15.join(packageRoot, "native", getNativeBindingFilename(platform2, arch2));
7201
8212
  }
7202
8213
  function getNativeBinding() {
7203
8214
  let currentDir;
7204
8215
  let requireTarget;
7205
8216
  if (typeof import.meta !== "undefined" && import.meta.url) {
7206
- currentDir = path14.dirname(fileURLToPath(import.meta.url));
8217
+ currentDir = path15.dirname(fileURLToPath(import.meta.url));
7207
8218
  requireTarget = import.meta.url;
7208
8219
  } else if (typeof __dirname !== "undefined") {
7209
8220
  currentDir = __dirname;
7210
8221
  requireTarget = __filename;
7211
8222
  } else {
7212
8223
  currentDir = process.cwd();
7213
- requireTarget = path14.join(currentDir, "index.js");
8224
+ requireTarget = path15.join(currentDir, "index.js");
7214
8225
  }
7215
8226
  const normalizedDir = currentDir.replace(/\\/g, "/");
7216
- const isDevMode = normalizedDir.includes("/src/native") || currentDir.includes(path14.join("src", "native"));
7217
- const packageRoot = isDevMode ? path14.resolve(currentDir, "../..") : path14.resolve(currentDir, "..");
8227
+ const isDevMode = normalizedDir.includes("/src/native") || currentDir.includes(path15.join("src", "native"));
8228
+ const packageRoot = isDevMode ? path15.resolve(currentDir, "../..") : path15.resolve(currentDir, "..");
7218
8229
  const nativePath = resolveNativeBindingPath(packageRoot);
7219
8230
  const require2 = module.createRequire(requireTarget);
7220
8231
  return require2(nativePath);
@@ -7798,8 +8809,8 @@ var Database = class _Database {
7798
8809
 
7799
8810
  // src/git/branch-materialization.ts
7800
8811
  import { promises as fsPromises2 } from "fs";
7801
- import * as os6 from "os";
7802
- import * as path15 from "path";
8812
+ import * as os7 from "os";
8813
+ import * as path16 from "path";
7803
8814
 
7804
8815
  // src/git/branch-resolution.ts
7805
8816
  import { execFile as execFile2 } from "child_process";
@@ -8118,13 +9129,13 @@ async function isWorktreeRegistered(projectRoot3, worktreePath) {
8118
9129
  return false;
8119
9130
  }
8120
9131
  function isPathWithinRoot(filePath, rootPath) {
8121
- const relative13 = path15.relative(path15.resolve(rootPath), path15.resolve(filePath));
8122
- return relative13 === "" || !relative13.startsWith(`..${path15.sep}`) && relative13 !== ".." && !path15.isAbsolute(relative13);
9132
+ const relative13 = path16.relative(path16.resolve(rootPath), path16.resolve(filePath));
9133
+ return relative13 === "" || !relative13.startsWith(`..${path16.sep}`) && relative13 !== ".." && !path16.isAbsolute(relative13);
8123
9134
  }
8124
9135
  async function pruneExactMissingWorktreeRegistration(projectRoot3, worktreePath) {
8125
9136
  if (await pathExists(worktreePath)) return false;
8126
9137
  const commonDir = await runGit(projectRoot3, ["rev-parse", "--path-format=absolute", "--git-common-dir"]);
8127
- const registrationsRoot = path15.join(commonDir, "worktrees");
9138
+ const registrationsRoot = path16.join(commonDir, "worktrees");
8128
9139
  let entries;
8129
9140
  try {
8130
9141
  entries = await fsPromises2.readdir(registrationsRoot, { withFileTypes: true });
@@ -8135,16 +9146,16 @@ async function pruneExactMissingWorktreeRegistration(projectRoot3, worktreePath)
8135
9146
  const target = canonicalizePathForComparison(worktreePath);
8136
9147
  for (const entry of entries) {
8137
9148
  if (!entry.isDirectory()) continue;
8138
- const registrationPath = path15.join(registrationsRoot, entry.name);
9149
+ const registrationPath = path16.join(registrationsRoot, entry.name);
8139
9150
  if (!isPathWithinRoot(registrationPath, registrationsRoot)) continue;
8140
9151
  let gitdirPath;
8141
9152
  try {
8142
- gitdirPath = (await fsPromises2.readFile(path15.join(registrationPath, "gitdir"), "utf8")).trim();
9153
+ gitdirPath = (await fsPromises2.readFile(path16.join(registrationPath, "gitdir"), "utf8")).trim();
8143
9154
  } catch {
8144
9155
  continue;
8145
9156
  }
8146
- const resolvedGitdirPath = path15.isAbsolute(gitdirPath) ? gitdirPath : path15.resolve(registrationPath, gitdirPath);
8147
- if (canonicalizePathForComparison(path15.dirname(resolvedGitdirPath)) !== target) continue;
9157
+ const resolvedGitdirPath = path16.isAbsolute(gitdirPath) ? gitdirPath : path16.resolve(registrationPath, gitdirPath);
9158
+ if (canonicalizePathForComparison(path16.dirname(resolvedGitdirPath)) !== target) continue;
8148
9159
  await fsPromises2.rm(registrationPath, { recursive: true, force: true });
8149
9160
  return true;
8150
9161
  }
@@ -8162,7 +9173,7 @@ async function removeWorktree(projectRoot3, worktreePath) {
8162
9173
  registered = await isWorktreeRegistered(projectRoot3, worktreePath);
8163
9174
  } catch (error) {
8164
9175
  errors.push(asError(error));
8165
- throw new AggregateError(errors, `Could not verify temporary worktree deregistration; preserved ${path15.dirname(worktreePath)}`);
9176
+ throw new AggregateError(errors, `Could not verify temporary worktree deregistration; preserved ${path16.dirname(worktreePath)}`);
8166
9177
  }
8167
9178
  if (registered) {
8168
9179
  try {
@@ -8178,7 +9189,7 @@ async function removeWorktree(projectRoot3, worktreePath) {
8178
9189
  registered = await isWorktreeRegistered(projectRoot3, worktreePath);
8179
9190
  } catch (error) {
8180
9191
  errors.push(asError(error));
8181
- throw new AggregateError(errors, `Could not verify temporary worktree deregistration; preserved ${path15.dirname(worktreePath)}`);
9192
+ throw new AggregateError(errors, `Could not verify temporary worktree deregistration; preserved ${path16.dirname(worktreePath)}`);
8182
9193
  }
8183
9194
  }
8184
9195
  if (registered && !await pathExists(worktreePath)) {
@@ -8191,13 +9202,13 @@ async function removeWorktree(projectRoot3, worktreePath) {
8191
9202
  }
8192
9203
  if (registered) {
8193
9204
  errors.push(new Error(`Temporary worktree remains registered: ${worktreePath}`));
8194
- throw new AggregateError(errors, `Failed to deregister temporary worktree; preserved ${path15.dirname(worktreePath)}`);
9205
+ throw new AggregateError(errors, `Failed to deregister temporary worktree; preserved ${path16.dirname(worktreePath)}`);
8195
9206
  }
8196
9207
  try {
8197
- await fsPromises2.rm(path15.dirname(worktreePath), { recursive: true, force: true });
9208
+ await fsPromises2.rm(path16.dirname(worktreePath), { recursive: true, force: true });
8198
9209
  } catch (error) {
8199
9210
  errors.push(asError(error));
8200
- throw new AggregateError(errors, `Deregistered the temporary worktree but could not remove ${path15.dirname(worktreePath)}`);
9211
+ throw new AggregateError(errors, `Deregistered the temporary worktree but could not remove ${path16.dirname(worktreePath)}`);
8201
9212
  }
8202
9213
  }
8203
9214
  async function cleanupTemporaryWorktree(projectRoot3, worktreePath, temporaryRoot) {
@@ -8233,9 +9244,9 @@ async function withMaterializedBranch(request, callback) {
8233
9244
  `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.`
8234
9245
  );
8235
9246
  }
8236
- const temporaryRoot = await fsPromises2.mkdtemp(path15.join(os6.tmpdir(), "codebase-index-branch-"));
8237
- const worktreePath = path15.join(temporaryRoot, "worktree");
8238
- const hooksPath = path15.join(temporaryRoot, "hooks");
9247
+ const temporaryRoot = await fsPromises2.mkdtemp(path16.join(os7.tmpdir(), "codebase-index-branch-"));
9248
+ const worktreePath = path16.join(temporaryRoot, "worktree");
9249
+ const hooksPath = path16.join(temporaryRoot, "hooks");
8239
9250
  await fsPromises2.mkdir(hooksPath);
8240
9251
  const info = {
8241
9252
  branch: request.branch,
@@ -8286,8 +9297,8 @@ async function withMaterializedBranch(request, callback) {
8286
9297
 
8287
9298
  // src/tools/changed-files.ts
8288
9299
  import { execFile as execFile3 } from "child_process";
8289
- import { realpathSync as realpathSync4 } from "fs";
8290
- import * as path16 from "path";
9300
+ import { realpathSync as realpathSync5 } from "fs";
9301
+ import * as path17 from "path";
8291
9302
  import { promisify as promisify2 } from "util";
8292
9303
  var execFileAsync2 = promisify2(execFile3);
8293
9304
  var GH_PR_VIEW_FIELDS = [
@@ -8429,9 +9440,9 @@ function getHeadRepositoryIdentity(data, host) {
8429
9440
  return `${host}/${owner.toLowerCase()}/${repository.replace(/\.git$/i, "").toLowerCase()}`;
8430
9441
  }
8431
9442
  function getLocalRepositoryIdentity(projectRoot3) {
8432
- let canonicalRoot = path16.resolve(projectRoot3);
9443
+ let canonicalRoot = path17.resolve(projectRoot3);
8433
9444
  try {
8434
- canonicalRoot = realpathSync4.native(canonicalRoot);
9445
+ canonicalRoot = realpathSync5.native(canonicalRoot);
8435
9446
  } catch {
8436
9447
  }
8437
9448
  return `local:${canonicalRoot}`;
@@ -8490,17 +9501,17 @@ async function getMergeBase(projectRoot3, baseCommit, headCommit) {
8490
9501
  return commit;
8491
9502
  }
8492
9503
  function normalizeFiles(rawFiles, projectRoot3) {
8493
- const root = path16.resolve(projectRoot3);
9504
+ const root = path17.resolve(projectRoot3);
8494
9505
  const seen = /* @__PURE__ */ new Set();
8495
9506
  const result = [];
8496
9507
  for (const raw of rawFiles) {
8497
9508
  if (raw.length === 0) continue;
8498
- const absolute = path16.resolve(root, raw);
8499
- const relative13 = path16.relative(root, absolute);
8500
- if (path16.isAbsolute(raw) || relative13 === ".." || relative13.startsWith(`..${path16.sep}`) || path16.isAbsolute(relative13)) {
9509
+ const absolute = path17.resolve(root, raw);
9510
+ const relative13 = path17.relative(root, absolute);
9511
+ if (path17.isAbsolute(raw) || relative13 === ".." || relative13.startsWith(`..${path17.sep}`) || path17.isAbsolute(relative13)) {
8501
9512
  throw new Error(`Changed file escapes the project root: ${JSON.stringify(raw)}`);
8502
9513
  }
8503
- const cleaned = relative13.startsWith(`.${path16.sep}`) ? relative13.slice(2) : relative13;
9514
+ const cleaned = relative13.startsWith(`.${path17.sep}`) ? relative13.slice(2) : relative13;
8504
9515
  if (!seen.has(cleaned)) {
8505
9516
  seen.add(cleaned);
8506
9517
  result.push(cleaned);
@@ -8511,7 +9522,7 @@ function normalizeFiles(rawFiles, projectRoot3) {
8511
9522
 
8512
9523
  // src/indexer/git-blame.ts
8513
9524
  import { execFile as execFile4 } from "child_process";
8514
- import * as path17 from "path";
9525
+ import * as path18 from "path";
8515
9526
  import { promisify as promisify3 } from "util";
8516
9527
  var execFileAsync3 = promisify3(execFile4);
8517
9528
  function parseGitBlamePorcelain(output) {
@@ -8549,7 +9560,7 @@ function parseGitBlamePorcelain(output) {
8549
9560
  return Array.from(commits.values()).filter((commit) => commit.lines > 0).sort((a, b) => b.lines - a.lines || b.committedAt - a.committedAt)[0];
8550
9561
  }
8551
9562
  async function getChunkGitBlame(projectRoot3, filePath, startLine, endLine) {
8552
- const relativePath = path17.relative(projectRoot3, filePath);
9563
+ const relativePath = path18.relative(projectRoot3, filePath);
8553
9564
  try {
8554
9565
  const { stdout } = await execFileAsync3(
8555
9566
  "git",
@@ -8881,6 +9892,9 @@ var CALL_GRAPH_SYMBOL_CHUNK_TYPES = /* @__PURE__ */ new Set([
8881
9892
  "enum_declaration",
8882
9893
  "function_definition",
8883
9894
  "class_definition",
9895
+ // Ruby module/class symbols that are declaration-bearing and navigable.
9896
+ "class",
9897
+ "module",
8884
9898
  "class_specifier",
8885
9899
  "struct_specifier",
8886
9900
  "namespace_definition",
@@ -9125,8 +10139,8 @@ function pathSegmentsForAffinityMatch(filePath) {
9125
10139
  if (segments.length === 0) {
9126
10140
  return [];
9127
10141
  }
9128
- const basename7 = segments[segments.length - 1] ?? "";
9129
- const basenameWithoutExt = basename7.replace(/\.[^/.]+$/u, "");
10142
+ const basename8 = segments[segments.length - 1] ?? "";
10143
+ const basenameWithoutExt = basename8.replace(/\.[^/.]+$/u, "");
9130
10144
  const normalizedSegments = segments.map((segment) => segment.toLowerCase());
9131
10145
  return Array.from(/* @__PURE__ */ new Set([
9132
10146
  ...normalizedSegments,
@@ -9435,8 +10449,8 @@ function hasAllEmbeddingParts(parts, expectedPartCount) {
9435
10449
 
9436
10450
  // src/indexer/failed-state-persistence.ts
9437
10451
  import * as fs2 from "fs";
9438
- import { createHash, randomBytes as randomBytes3 } from "crypto";
9439
- import * as path18 from "path";
10452
+ import { createHash as createHash2, randomBytes as randomBytes3 } from "crypto";
10453
+ import * as path19 from "path";
9440
10454
  import { StringDecoder } from "string_decoder";
9441
10455
  var CURRENT_FAILED_BATCH_VERSION = 1;
9442
10456
  var DEFAULT_MALFORMED_LINE_ACTION = "skip";
@@ -9454,7 +10468,7 @@ function* readFailedBatchRecords(filePath, options = {}) {
9454
10468
  function createFailedBatchWriter(targetPath) {
9455
10469
  const temporaryPath = createTemporaryPath(targetPath);
9456
10470
  let finalized = false;
9457
- fs2.mkdirSync(path18.dirname(targetPath), { recursive: true });
10471
+ fs2.mkdirSync(path19.dirname(targetPath), { recursive: true });
9458
10472
  fs2.closeSync(fs2.openSync(temporaryPath, "w"));
9459
10473
  const write = (record) => {
9460
10474
  if (finalized) {
@@ -9473,7 +10487,7 @@ function createFailedBatchWriter(targetPath) {
9473
10487
  if (lines.length === 0) {
9474
10488
  return;
9475
10489
  }
9476
- fs2.mkdirSync(path18.dirname(targetPath), { recursive: true });
10490
+ fs2.mkdirSync(path19.dirname(targetPath), { recursive: true });
9477
10491
  fs2.appendFileSync(temporaryPath, `${lines.join("\n")}
9478
10492
  `, "utf-8");
9479
10493
  };
@@ -9481,7 +10495,7 @@ function createFailedBatchWriter(targetPath) {
9481
10495
  if (finalized) {
9482
10496
  return;
9483
10497
  }
9484
- fs2.mkdirSync(path18.dirname(targetPath), { recursive: true });
10498
+ fs2.mkdirSync(path19.dirname(targetPath), { recursive: true });
9485
10499
  fs2.renameSync(temporaryPath, targetPath);
9486
10500
  finalized = true;
9487
10501
  };
@@ -9627,10 +10641,10 @@ function stripLeadingBomAndWhitespace(value) {
9627
10641
  return result;
9628
10642
  }
9629
10643
  function createTemporaryPath(targetPath) {
9630
- const randomId = createHash("sha1").update(`${Date.now()}:${randomBytes3(8).toString("hex")}`).digest("hex");
9631
- const targetDir = path18.dirname(targetPath);
9632
- const baseName = path18.basename(targetPath);
9633
- return path18.join(targetDir, `.${baseName}.${randomId}.tmp`);
10644
+ const randomId = createHash2("sha1").update(`${Date.now()}:${randomBytes3(8).toString("hex")}`).digest("hex");
10645
+ const targetDir = path19.dirname(targetPath);
10646
+ const baseName = path19.basename(targetPath);
10647
+ return path19.join(targetDir, `.${baseName}.${randomId}.tmp`);
9634
10648
  }
9635
10649
  function handleMalformedLine(filePath, lineNumber, line, error, options) {
9636
10650
  const action = options.malformedLineAction ?? DEFAULT_MALFORMED_LINE_ACTION;
@@ -9876,9 +10890,9 @@ var SWIFT_PARSER_VERSION = "1";
9876
10890
  var METAL_PARSER_VERSION = "1";
9877
10891
  var SYMBOL_EXTRACTOR_VERSION = "1";
9878
10892
  function isPathWithinRoot2(filePath, rootPath) {
9879
- const normalizedFilePath = path19.resolve(filePath);
9880
- const normalizedRoot = path19.resolve(rootPath);
9881
- return normalizedFilePath === normalizedRoot || normalizedFilePath.startsWith(`${normalizedRoot}${path19.sep}`);
10893
+ const normalizedFilePath = path20.resolve(filePath);
10894
+ const normalizedRoot = path20.resolve(rootPath);
10895
+ return normalizedFilePath === normalizedRoot || normalizedFilePath.startsWith(`${normalizedRoot}${path20.sep}`);
9882
10896
  }
9883
10897
  function promoteIdentifierMatches(query, combined, semanticCandidates, keywordCandidates, database, branchChunkIds, prioritizeSourcePaths = classifyQueryIntentRaw(query) === "source") {
9884
10898
  if (combined.length === 0) {
@@ -10209,10 +11223,10 @@ function matchesHardSearchFilters(candidate, options, projectRoot3) {
10209
11223
  }
10210
11224
  if (options?.directory) {
10211
11225
  const candidatePath = canonicalizePathForComparison(
10212
- path19.resolve(projectRoot3, candidate.metadata.filePath.replace(/\\/g, path19.sep))
11226
+ path20.resolve(projectRoot3, candidate.metadata.filePath.replace(/\\/g, path20.sep))
10213
11227
  );
10214
11228
  const directoryPath = canonicalizePathForComparison(
10215
- path19.resolve(projectRoot3, options.directory.trim().replace(/\\/g, path19.sep))
11229
+ path20.resolve(projectRoot3, options.directory.trim().replace(/\\/g, path20.sep))
10216
11230
  );
10217
11231
  if (!isPathWithinRoot2(candidatePath, directoryPath)) return false;
10218
11232
  }
@@ -10325,26 +11339,37 @@ var Indexer = class _Indexer {
10325
11339
  return this.indexPathOverride ?? resolveProjectIndexPath(this.projectRoot, this.config.scope, this.host);
10326
11340
  }
10327
11341
  toCanonicalFilePath(filePath) {
10328
- if (!path19.isAbsolute(filePath)) {
11342
+ if (!path20.isAbsolute(filePath)) {
10329
11343
  return this.resolveStoredFilePath(filePath, this.projectRoot);
10330
11344
  }
10331
- if (path19.resolve(this.materializedProjectRoot) === path19.resolve(this.projectRoot) || !isPathWithinRoot2(filePath, this.materializedProjectRoot)) {
11345
+ if (path20.resolve(this.materializedProjectRoot) === path20.resolve(this.projectRoot) || !isPathWithinRoot2(filePath, this.materializedProjectRoot)) {
10332
11346
  return filePath;
10333
11347
  }
10334
- return path19.resolve(this.projectRoot, path19.relative(this.materializedProjectRoot, filePath));
11348
+ return path20.resolve(this.projectRoot, path20.relative(this.materializedProjectRoot, filePath));
10335
11349
  }
10336
11350
  toStoredFilePath(filePath) {
10337
11351
  const canonicalFilePath = this.toCanonicalFilePath(filePath);
10338
11352
  if (this.config.scope !== "project" || !isPathWithinRoot2(canonicalFilePath, this.projectRoot)) {
10339
11353
  return canonicalFilePath;
10340
11354
  }
10341
- return path19.relative(this.projectRoot, canonicalFilePath).split(path19.sep).join("/");
11355
+ return path20.relative(this.projectRoot, canonicalFilePath).split(path20.sep).join("/");
11356
+ }
11357
+ isStoredPathExcluded(storedPath) {
11358
+ let matchPath = storedPath.split(path20.sep).join("/");
11359
+ if (path20.isAbsolute(storedPath)) {
11360
+ const relativePath = path20.relative(this.projectRoot, storedPath).split(path20.sep).join("/");
11361
+ if (relativePath.startsWith("..") || path20.isAbsolute(relativePath)) {
11362
+ return false;
11363
+ }
11364
+ matchPath = relativePath;
11365
+ }
11366
+ return isExcludedByPatterns(matchPath, this.config.exclude);
10342
11367
  }
10343
11368
  resolveStoredFilePath(filePath, rootPath = this.projectRoot) {
10344
- if (path19.isAbsolute(filePath)) {
11369
+ if (path20.isAbsolute(filePath)) {
10345
11370
  return filePath;
10346
11371
  }
10347
- const resolvedPath = path19.resolve(rootPath, ...filePath.split("/"));
11372
+ const resolvedPath = path20.resolve(rootPath, ...filePath.split("/"));
10348
11373
  if (!isPathWithinRoot2(resolvedPath, rootPath)) {
10349
11374
  throw new Error(`Stored project path escapes project root: ${JSON.stringify(filePath)}`);
10350
11375
  }
@@ -10368,7 +11393,7 @@ var Indexer = class _Indexer {
10368
11393
  }
10369
11394
  toMaterializedFilePath(filePath) {
10370
11395
  const storedFilePath = this.toStoredFilePath(filePath);
10371
- if (path19.isAbsolute(storedFilePath)) {
11396
+ if (path20.isAbsolute(storedFilePath)) {
10372
11397
  return storedFilePath;
10373
11398
  }
10374
11399
  return this.resolveStoredFilePath(storedFilePath, this.materializedProjectRoot);
@@ -10385,10 +11410,10 @@ var Indexer = class _Indexer {
10385
11410
  }
10386
11411
  getRuntimeArtifactPath(fileName) {
10387
11412
  const namespace = this.getRuntimeArtifactNamespace();
10388
- if (!namespace) return path19.join(this.indexPath, fileName);
10389
- const extension = path19.extname(fileName);
11413
+ if (!namespace) return path20.join(this.indexPath, fileName);
11414
+ const extension = path20.extname(fileName);
10390
11415
  const baseName = fileName.slice(0, fileName.length - extension.length);
10391
- return path19.join(this.indexPath, `${baseName}.${namespace}${extension}`);
11416
+ return path20.join(this.indexPath, `${baseName}.${namespace}${extension}`);
10392
11417
  }
10393
11418
  refreshRuntimeArtifactPaths() {
10394
11419
  this.fileHashCachePath = this.getRuntimeArtifactPath("file-hashes.json");
@@ -10401,14 +11426,14 @@ var Indexer = class _Indexer {
10401
11426
  getMaterializedKnowledgeBases() {
10402
11427
  const canonicalProjectRoot = this.getCanonicalPath(this.projectRoot);
10403
11428
  return this.config.knowledgeBases.map((knowledgeBase) => {
10404
- const configuredPath = path19.isAbsolute(knowledgeBase) ? knowledgeBase : path19.resolve(this.projectRoot, knowledgeBase);
11429
+ const configuredPath = path20.isAbsolute(knowledgeBase) ? knowledgeBase : path20.resolve(this.projectRoot, knowledgeBase);
10405
11430
  const canonicalPath = this.getCanonicalPath(configuredPath);
10406
11431
  if (!isPathWithinRoot2(canonicalPath, canonicalProjectRoot)) {
10407
11432
  return canonicalPath;
10408
11433
  }
10409
- return path19.resolve(
11434
+ return path20.resolve(
10410
11435
  this.materializedProjectRoot,
10411
- path19.relative(canonicalProjectRoot, canonicalPath)
11436
+ path20.relative(canonicalProjectRoot, canonicalPath)
10412
11437
  );
10413
11438
  });
10414
11439
  }
@@ -10416,7 +11441,7 @@ var Indexer = class _Indexer {
10416
11441
  try {
10417
11442
  return canonicalizePathForComparison(targetPath);
10418
11443
  } catch {
10419
- return path19.resolve(targetPath);
11444
+ return path20.resolve(targetPath);
10420
11445
  }
10421
11446
  }
10422
11447
  getProjectIdentityHash(projectRoot3) {
@@ -10497,7 +11522,7 @@ var Indexer = class _Indexer {
10497
11522
  } catch (error) {
10498
11523
  releaseError = error;
10499
11524
  this.writerArtifactFingerprint = null;
10500
- if (!existsSync11(lease.lockPath) && this.activeIndexLease?.owner.token === lease.owner.token) {
11525
+ if (!existsSync12(lease.lockPath) && this.activeIndexLease?.owner.token === lease.owner.token) {
10501
11526
  this.activeIndexLease = null;
10502
11527
  }
10503
11528
  }
@@ -10515,12 +11540,12 @@ var Indexer = class _Indexer {
10515
11540
  return this.activeIndexLease;
10516
11541
  }
10517
11542
  loadFileHashCache() {
10518
- if (!existsSync11(this.fileHashCachePath)) {
11543
+ if (!existsSync12(this.fileHashCachePath)) {
10519
11544
  this.fileHashCache = /* @__PURE__ */ new Map();
10520
11545
  return;
10521
11546
  }
10522
11547
  try {
10523
- const data = readFileSync8(this.fileHashCachePath, "utf-8");
11548
+ const data = readFileSync9(this.fileHashCachePath, "utf-8");
10524
11549
  const parsed = JSON.parse(data);
10525
11550
  this.fileHashCache = new Map(Object.entries(parsed));
10526
11551
  } catch (error) {
@@ -10542,24 +11567,24 @@ var Indexer = class _Indexer {
10542
11567
  atomicWriteSync(targetPath, data) {
10543
11568
  const lease = this.requireActiveLease();
10544
11569
  const tempPath = createLeaseTemporaryPath(targetPath, lease.owner, "tmp");
10545
- mkdirSync4(path19.dirname(targetPath), { recursive: true });
11570
+ mkdirSync5(path20.dirname(targetPath), { recursive: true });
10546
11571
  try {
10547
- writeFileSync3(tempPath, data);
10548
- renameSync3(tempPath, targetPath);
11572
+ writeFileSync4(tempPath, data);
11573
+ renameSync4(tempPath, targetPath);
10549
11574
  } finally {
10550
11575
  removeLeaseTemporaryPath(tempPath);
10551
11576
  }
10552
11577
  }
10553
11578
  saveInvertedIndex(invertedIndex) {
10554
11579
  this.atomicWriteSync(
10555
- path19.join(this.indexPath, "inverted-index.json"),
11580
+ path20.join(this.indexPath, "inverted-index.json"),
10556
11581
  invertedIndex.serialize()
10557
11582
  );
10558
11583
  }
10559
11584
  getScopedRoots(projectRoot3 = this.projectRoot) {
10560
11585
  const roots = /* @__PURE__ */ new Set([this.getCanonicalPath(projectRoot3)]);
10561
11586
  for (const kbRoot of this.config.knowledgeBases) {
10562
- roots.add(this.getCanonicalPath(path19.resolve(projectRoot3, kbRoot)));
11587
+ roots.add(this.getCanonicalPath(path20.resolve(projectRoot3, kbRoot)));
10563
11588
  }
10564
11589
  return Array.from(roots);
10565
11590
  }
@@ -10976,7 +12001,7 @@ var Indexer = class _Indexer {
10976
12001
  return configuredProviderInfo !== null && recovery.embeddingProvider === configuredProviderInfo.provider && recovery.embeddingModel === configuredProviderInfo.modelInfo.model && recovery.embeddingDimensions === configuredProviderInfo.modelInfo.dimensions && recovery.embeddingStrategyVersion === EMBEDDING_STRATEGY_VERSION;
10977
12002
  }
10978
12003
  hasUnknownLegacyForceIndexClear(owner) {
10979
- return owner.operation === "force-index" && owner.clearRecovery === void 0 && owner.recoveryProtocolVersion !== 1 && existsSync11(path19.join(this.indexPath, "force-index-phase"));
12004
+ return owner.operation === "force-index" && owner.clearRecovery === void 0 && owner.recoveryProtocolVersion !== 1 && existsSync12(path20.join(this.indexPath, "force-index-phase"));
10980
12005
  }
10981
12006
  async recoverFromInterruptedIndexingUnlocked(owners) {
10982
12007
  for (const owner of owners) {
@@ -11162,7 +12187,7 @@ var Indexer = class _Indexer {
11162
12187
  }
11163
12188
  }
11164
12189
  clearFailedBatchState() {
11165
- if (existsSync11(this.failedBatchesPath)) {
12190
+ if (existsSync12(this.failedBatchesPath)) {
11166
12191
  try {
11167
12192
  unlinkSync2(this.failedBatchesPath);
11168
12193
  } catch {
@@ -11364,7 +12389,7 @@ var Indexer = class _Indexer {
11364
12389
  await options.queue.onSizeLessThan(Math.max(1, options.providerRateLimits.concurrency));
11365
12390
  const task = options.queue.add(async () => {
11366
12391
  if (options.rateLimitState.backoffMs > 0) {
11367
- await new Promise((resolve17) => setTimeout(resolve17, options.rateLimitState.backoffMs));
12392
+ await new Promise((resolve18) => setTimeout(resolve18, options.rateLimitState.backoffMs));
11368
12393
  }
11369
12394
  try {
11370
12395
  const embeddingResult = await pRetry(
@@ -11783,12 +12808,12 @@ var Indexer = class _Indexer {
11783
12808
  }
11784
12809
  }
11785
12810
  captureReaderArtifactFingerprint() {
11786
- const storePath = path19.join(this.indexPath, "vectors");
12811
+ const storePath = path20.join(this.indexPath, "vectors");
11787
12812
  return {
11788
12813
  vectors: `${this.getReaderFileFingerprint(storePath)}|${this.getReaderFileFingerprint(`${storePath}.meta.json`)}`,
11789
- keyword: this.getReaderFileFingerprint(path19.join(this.indexPath, "inverted-index.json")),
11790
- database: this.getReaderFileFingerprint(path19.join(this.indexPath, "codebase.db")),
11791
- databaseIdentity: this.getReaderFileFingerprint(path19.join(this.indexPath, "codebase.db"), true)
12814
+ keyword: this.getReaderFileFingerprint(path20.join(this.indexPath, "inverted-index.json")),
12815
+ database: this.getReaderFileFingerprint(path20.join(this.indexPath, "codebase.db")),
12816
+ databaseIdentity: this.getReaderFileFingerprint(path20.join(this.indexPath, "codebase.db"), true)
11792
12817
  };
11793
12818
  }
11794
12819
  refreshReaderArtifacts() {
@@ -11813,13 +12838,13 @@ var Indexer = class _Indexer {
11813
12838
  issues.set(component, this.createReadIssue(component, message));
11814
12839
  this.readerArtifactRetryAfter.set(component, Date.now() + READER_ARTIFACT_RETRY_INTERVAL_MS);
11815
12840
  };
11816
- const storePath = path19.join(this.indexPath, "vectors");
12841
+ const storePath = path20.join(this.indexPath, "vectors");
11817
12842
  const vectorMetadataPath = `${storePath}.meta.json`;
11818
- const invertedIndexPath = path19.join(this.indexPath, "inverted-index.json");
11819
- const dbPath = path19.join(this.indexPath, "codebase.db");
12843
+ const invertedIndexPath = path20.join(this.indexPath, "inverted-index.json");
12844
+ const dbPath = path20.join(this.indexPath, "codebase.db");
11820
12845
  if (vectorsChanged || retryDue("vectors")) {
11821
- const vectorStoreExists = existsSync11(storePath);
11822
- const vectorMetadataExists = existsSync11(vectorMetadataPath);
12846
+ const vectorStoreExists = existsSync12(storePath);
12847
+ const vectorMetadataExists = existsSync12(vectorMetadataPath);
11823
12848
  if (vectorStoreExists && vectorMetadataExists) {
11824
12849
  try {
11825
12850
  const store = new VectorStore(storePath, this.configuredProviderInfo.modelInfo.dimensions);
@@ -11834,8 +12859,8 @@ var Indexer = class _Indexer {
11834
12859
  setIssue("vectors", this.getVectorReadIssueMessage());
11835
12860
  }
11836
12861
  }
11837
- if (keywordChanged || retryDue("keyword") || !existsSync11(invertedIndexPath) && (this.store?.count() ?? 0) > 0) {
11838
- if (existsSync11(invertedIndexPath)) {
12862
+ if (keywordChanged || retryDue("keyword") || !existsSync12(invertedIndexPath) && (this.store?.count() ?? 0) > 0) {
12863
+ if (existsSync12(invertedIndexPath)) {
11839
12864
  try {
11840
12865
  const invertedIndex = new InvertedIndex(invertedIndexPath);
11841
12866
  invertedIndex.load();
@@ -11850,7 +12875,7 @@ var Indexer = class _Indexer {
11850
12875
  }
11851
12876
  }
11852
12877
  if (databaseReplaced || databaseChanged && issues.has("database") || retryDue("database")) {
11853
- if (existsSync11(dbPath)) {
12878
+ if (existsSync12(dbPath)) {
11854
12879
  try {
11855
12880
  const database = Database.openReadOnly(dbPath);
11856
12881
  if (this.database) {
@@ -11932,11 +12957,11 @@ var Indexer = class _Indexer {
11932
12957
  });
11933
12958
  this.provider = createEmbeddingProvider(this.configuredProviderInfo);
11934
12959
  const dimensions = this.configuredProviderInfo.modelInfo.dimensions;
11935
- const storePath = path19.join(this.indexPath, "vectors");
12960
+ const storePath = path20.join(this.indexPath, "vectors");
11936
12961
  const vectorMetadataPath = `${storePath}.meta.json`;
11937
- const invertedIndexPath = path19.join(this.indexPath, "inverted-index.json");
11938
- const dbPath = path19.join(this.indexPath, "codebase.db");
11939
- let dbIsNew = !existsSync11(dbPath);
12962
+ const invertedIndexPath = path20.join(this.indexPath, "inverted-index.json");
12963
+ const dbPath = path20.join(this.indexPath, "codebase.db");
12964
+ let dbIsNew = !existsSync12(dbPath);
11940
12965
  const readerArtifactFingerprint = mode === "reader" ? this.captureReaderArtifactFingerprint() : null;
11941
12966
  if (mode === "writer") {
11942
12967
  await fsPromises3.mkdir(this.indexPath, { recursive: true });
@@ -11968,14 +12993,14 @@ var Indexer = class _Indexer {
11968
12993
  }
11969
12994
  }
11970
12995
  this.store = new VectorStore(storePath, dimensions);
11971
- if (existsSync11(storePath) || existsSync11(vectorMetadataPath)) {
12996
+ if (existsSync12(storePath) || existsSync12(vectorMetadataPath)) {
11972
12997
  this.store.load();
11973
12998
  }
11974
12999
  this.invertedIndex = new InvertedIndex(invertedIndexPath);
11975
13000
  try {
11976
13001
  this.invertedIndex.load();
11977
13002
  } catch {
11978
- if (existsSync11(invertedIndexPath)) {
13003
+ if (existsSync12(invertedIndexPath)) {
11979
13004
  await fsPromises3.unlink(invertedIndexPath);
11980
13005
  }
11981
13006
  this.invertedIndex = new InvertedIndex(invertedIndexPath);
@@ -11993,8 +13018,8 @@ var Indexer = class _Indexer {
11993
13018
  }
11994
13019
  } else {
11995
13020
  this.store = new VectorStore(storePath, dimensions);
11996
- const vectorStoreExists = existsSync11(storePath);
11997
- const vectorMetadataExists = existsSync11(vectorMetadataPath);
13021
+ const vectorStoreExists = existsSync12(storePath);
13022
+ const vectorMetadataExists = existsSync12(vectorMetadataPath);
11998
13023
  const vectorReadFailureMessage = this.getVectorReadIssueMessage();
11999
13024
  if (vectorStoreExists !== vectorMetadataExists) {
12000
13025
  this.recordReadIssue("vectors", vectorReadFailureMessage);
@@ -12007,7 +13032,7 @@ var Indexer = class _Indexer {
12007
13032
  }
12008
13033
  }
12009
13034
  this.invertedIndex = new InvertedIndex(invertedIndexPath);
12010
- if (existsSync11(invertedIndexPath)) {
13035
+ if (existsSync12(invertedIndexPath)) {
12011
13036
  try {
12012
13037
  this.invertedIndex.load();
12013
13038
  } catch (error) {
@@ -12021,7 +13046,7 @@ var Indexer = class _Indexer {
12021
13046
  } else if (this.store.count() > 0) {
12022
13047
  this.recordReadIssue("keyword", this.getKeywordReadIssueMessage());
12023
13048
  }
12024
- if (existsSync11(dbPath)) {
13049
+ if (existsSync12(dbPath)) {
12025
13050
  try {
12026
13051
  this.database = Database.openReadOnly(dbPath);
12027
13052
  } catch (error) {
@@ -12113,7 +13138,7 @@ var Indexer = class _Indexer {
12113
13138
  if (await this.tryResetCorruptedIndex("running automatic orphan garbage collection", error)) {
12114
13139
  return {
12115
13140
  resetCorruptedIndex: true,
12116
- warning: this.getCorruptedIndexWarning(path19.join(this.indexPath, "codebase.db"))
13141
+ warning: this.getCorruptedIndexWarning(path20.join(this.indexPath, "codebase.db"))
12117
13142
  };
12118
13143
  }
12119
13144
  throw error;
@@ -12128,7 +13153,7 @@ var Indexer = class _Indexer {
12128
13153
  return;
12129
13154
  }
12130
13155
  const retainedEntries = store.getAllMetadata().filter(({ key }) => !excludedSet.has(key));
12131
- const storeBasePath = path19.join(this.indexPath, "vectors");
13156
+ const storeBasePath = path20.join(this.indexPath, "vectors");
12132
13157
  const storeIndexPath = storeBasePath;
12133
13158
  const storeMetadataPath = `${storeBasePath}.meta.json`;
12134
13159
  const lease = this.requireActiveLease();
@@ -12138,19 +13163,19 @@ var Indexer = class _Indexer {
12138
13163
  let backedUpMetadata = false;
12139
13164
  let rebuiltCount = 0;
12140
13165
  let skippedCount = 0;
12141
- if (existsSync11(backupIndexPath)) {
13166
+ if (existsSync12(backupIndexPath)) {
12142
13167
  unlinkSync2(backupIndexPath);
12143
13168
  }
12144
- if (existsSync11(backupMetadataPath)) {
13169
+ if (existsSync12(backupMetadataPath)) {
12145
13170
  unlinkSync2(backupMetadataPath);
12146
13171
  }
12147
13172
  try {
12148
- if (existsSync11(storeIndexPath)) {
12149
- renameSync3(storeIndexPath, backupIndexPath);
13173
+ if (existsSync12(storeIndexPath)) {
13174
+ renameSync4(storeIndexPath, backupIndexPath);
12150
13175
  backedUpIndex = true;
12151
13176
  }
12152
- if (existsSync11(storeMetadataPath)) {
12153
- renameSync3(storeMetadataPath, backupMetadataPath);
13177
+ if (existsSync12(storeMetadataPath)) {
13178
+ renameSync4(storeMetadataPath, backupMetadataPath);
12154
13179
  backedUpMetadata = true;
12155
13180
  }
12156
13181
  store.clear();
@@ -12170,10 +13195,10 @@ var Indexer = class _Indexer {
12170
13195
  rebuiltCount += 1;
12171
13196
  }
12172
13197
  store.save();
12173
- if (backedUpIndex && existsSync11(backupIndexPath)) {
13198
+ if (backedUpIndex && existsSync12(backupIndexPath)) {
12174
13199
  unlinkSync2(backupIndexPath);
12175
13200
  }
12176
- if (backedUpMetadata && existsSync11(backupMetadataPath)) {
13201
+ if (backedUpMetadata && existsSync12(backupMetadataPath)) {
12177
13202
  unlinkSync2(backupMetadataPath);
12178
13203
  }
12179
13204
  this.logger.gc("info", "Rebuilt vector store to avoid native remove", {
@@ -12186,17 +13211,17 @@ var Indexer = class _Indexer {
12186
13211
  store.clear();
12187
13212
  } catch {
12188
13213
  }
12189
- if (existsSync11(storeIndexPath)) {
13214
+ if (existsSync12(storeIndexPath)) {
12190
13215
  unlinkSync2(storeIndexPath);
12191
13216
  }
12192
- if (existsSync11(storeMetadataPath)) {
13217
+ if (existsSync12(storeMetadataPath)) {
12193
13218
  unlinkSync2(storeMetadataPath);
12194
13219
  }
12195
- if (backedUpIndex && existsSync11(backupIndexPath)) {
12196
- renameSync3(backupIndexPath, storeIndexPath);
13220
+ if (backedUpIndex && existsSync12(backupIndexPath)) {
13221
+ renameSync4(backupIndexPath, storeIndexPath);
12197
13222
  }
12198
- if (backedUpMetadata && existsSync11(backupMetadataPath)) {
12199
- renameSync3(backupMetadataPath, storeMetadataPath);
13223
+ if (backedUpMetadata && existsSync12(backupMetadataPath)) {
13224
+ renameSync4(backupMetadataPath, storeMetadataPath);
12200
13225
  }
12201
13226
  if (backedUpIndex || backedUpMetadata) {
12202
13227
  store.load();
@@ -12211,11 +13236,11 @@ var Indexer = class _Indexer {
12211
13236
  return `Detected a corrupted local SQLite index at ${dbPath} and reset the local index. Run index_codebase to rebuild search data.`;
12212
13237
  }
12213
13238
  async removeProjectRuntimeStateArtifacts() {
12214
- if (!existsSync11(this.indexPath)) return;
13239
+ if (!existsSync12(this.indexPath)) return;
12215
13240
  const names = await fsPromises3.readdir(this.indexPath);
12216
13241
  const runtimeStatePattern = /^(?:file-hashes|failed-batches)(?:\.[a-f0-9]{16})?\.json$/;
12217
13242
  await Promise.all(
12218
- names.filter((name) => runtimeStatePattern.test(name)).map((name) => fsPromises3.rm(path19.join(this.indexPath, name), { force: true }))
13243
+ names.filter((name) => runtimeStatePattern.test(name)).map((name) => fsPromises3.rm(path20.join(this.indexPath, name), { force: true }))
12219
13244
  );
12220
13245
  }
12221
13246
  async resetLocalIndexArtifacts() {
@@ -12231,13 +13256,13 @@ var Indexer = class _Indexer {
12231
13256
  this.readerArtifactRetryAfter.clear();
12232
13257
  this.fileHashCache.clear();
12233
13258
  const resetPaths = [
12234
- path19.join(this.indexPath, "codebase.db"),
12235
- path19.join(this.indexPath, "codebase.db-shm"),
12236
- path19.join(this.indexPath, "codebase.db-wal"),
12237
- path19.join(this.indexPath, "vectors"),
12238
- path19.join(this.indexPath, "vectors.usearch"),
12239
- path19.join(this.indexPath, "vectors.meta.json"),
12240
- path19.join(this.indexPath, "inverted-index.json")
13259
+ path20.join(this.indexPath, "codebase.db"),
13260
+ path20.join(this.indexPath, "codebase.db-shm"),
13261
+ path20.join(this.indexPath, "codebase.db-wal"),
13262
+ path20.join(this.indexPath, "vectors"),
13263
+ path20.join(this.indexPath, "vectors.usearch"),
13264
+ path20.join(this.indexPath, "vectors.meta.json"),
13265
+ path20.join(this.indexPath, "inverted-index.json")
12241
13266
  ];
12242
13267
  await Promise.all(resetPaths.map((targetPath) => fsPromises3.rm(targetPath, { recursive: true, force: true })));
12243
13268
  await this.removeProjectRuntimeStateArtifacts();
@@ -12247,7 +13272,7 @@ var Indexer = class _Indexer {
12247
13272
  if (!isSqliteCorruptionError(error)) {
12248
13273
  return false;
12249
13274
  }
12250
- const dbPath = path19.join(this.indexPath, "codebase.db");
13275
+ const dbPath = path20.join(this.indexPath, "codebase.db");
12251
13276
  const warning = this.getCorruptedIndexWarning(dbPath);
12252
13277
  const errorMessage = getErrorMessage4(error);
12253
13278
  if (this.config.scope === "global") {
@@ -12481,6 +13506,70 @@ var Indexer = class _Indexer {
12481
13506
  );
12482
13507
  return createCostEstimate(files, configuredProviderInfo);
12483
13508
  }
13509
+ // Dry-run counterpart to index()/forceIndex(): parse the real file set and sum
13510
+ // estimateTokens over the embedding text of every indexable chunk, without
13511
+ // calling the embedding provider or writing to the index. Read-only and
13512
+ // lock-free (mirrors estimateCost). The token sum is the exact value "Tokens
13513
+ // used" climbs to for a force index (cache bypassed); for an incremental it is
13514
+ // an upper bound because cached chunks are counted here but not re-embedded.
13515
+ // Used by index_codebase(dryRun:true) to give a stable, monotonic progress
13516
+ // denominator that matches the live "Tokens used" basis.
13517
+ async dryRunCost() {
13518
+ const { configuredProviderInfo } = await this.ensureInitialized();
13519
+ const maxChunkTokens = getSafeEmbeddingChunkTokenLimit(configuredProviderInfo);
13520
+ const includePatterns = [...this.config.include, ...this.config.additionalInclude];
13521
+ const { files } = await collectFiles(
13522
+ this.materializedProjectRoot,
13523
+ includePatterns,
13524
+ this.config.exclude,
13525
+ this.config.indexing.maxFileSize,
13526
+ this.getMaterializedKnowledgeBases(),
13527
+ { maxDepth: this.config.indexing.maxDepth, maxFilesPerDirectory: this.config.indexing.maxFilesPerDirectory }
13528
+ );
13529
+ let filesCount = 0;
13530
+ let chunksCount = 0;
13531
+ let tokensToEmbed = 0;
13532
+ for (const batch of iterateOrderedFileBatches(files, (f) => f.size, this.fileBatchLimits)) {
13533
+ const loadedFiles = await Promise.all(batch.map(async (f) => {
13534
+ try {
13535
+ return {
13536
+ path: this.toStoredFilePath(f.path),
13537
+ content: await fsPromises3.readFile(f.path, "utf-8")
13538
+ };
13539
+ } catch {
13540
+ return null;
13541
+ }
13542
+ }));
13543
+ const readable = loadedFiles.filter(
13544
+ (f) => f !== null
13545
+ );
13546
+ filesCount += readable.length;
13547
+ const contentByPath = new Map(readable.map((f) => [f.path, f.content]));
13548
+ const parsedFiles = parseFiles(readable, this.config.indexing.linesPerChunk);
13549
+ for (const parsed of parsedFiles) {
13550
+ let chunksToProcess = parsed.chunks;
13551
+ if (this.config.indexing.fallbackToTextOnMaxChunks && chunksToProcess.length > this.config.indexing.maxChunksPerFile) {
13552
+ const content = contentByPath.get(parsed.path);
13553
+ if (content !== void 0) {
13554
+ chunksToProcess = parseFileAsText(parsed.path, content, this.config.indexing.linesPerChunk);
13555
+ }
13556
+ }
13557
+ chunksToProcess = selectIndexableChunks(
13558
+ chunksToProcess,
13559
+ this.config.indexing.maxChunksPerFile,
13560
+ this.config.indexing.semanticOnly
13561
+ );
13562
+ for (const chunk of chunksToProcess) {
13563
+ const texts = createEmbeddingTexts(chunk, parsed.path, maxChunkTokens);
13564
+ chunksCount += 1;
13565
+ for (const text3 of texts) {
13566
+ tokensToEmbed += estimateTokens(text3);
13567
+ }
13568
+ }
13569
+ }
13570
+ }
13571
+ return { filesCount, chunksCount, tokensToEmbed };
13572
+ }
12484
13573
  async index(onProgress) {
12485
13574
  return this.withIndexMutationLease("index", async (recoveredOwners) => {
12486
13575
  return this.indexUnlocked(onProgress, recoveredOwners);
@@ -12570,10 +13659,10 @@ var Indexer = class _Indexer {
12570
13659
  const reparseCachedMetalFiles = database.getMetadata(metalParserMetadataKey) !== METAL_PARSER_VERSION;
12571
13660
  const symbolExtractorMetadataKey = this.getSymbolExtractorVersionMetadataKey();
12572
13661
  const refreshCachedSymbols = database.getMetadata(symbolExtractorMetadataKey) !== SYMBOL_EXTRACTOR_VERSION;
12573
- if (reparseCachedSwiftFiles && Array.from(this.fileHashCache.keys()).some((filePath) => path19.extname(filePath).toLowerCase() === ".swift")) {
13662
+ if (reparseCachedSwiftFiles && Array.from(this.fileHashCache.keys()).some((filePath) => path20.extname(filePath).toLowerCase() === ".swift")) {
12574
13663
  this.logger.info("Reindexing cached Swift files for parser support");
12575
13664
  }
12576
- if (reparseCachedMetalFiles && Array.from(this.fileHashCache.keys()).some((filePath) => path19.extname(filePath).toLowerCase() === ".metal")) {
13665
+ if (reparseCachedMetalFiles && Array.from(this.fileHashCache.keys()).some((filePath) => path20.extname(filePath).toLowerCase() === ".metal")) {
12577
13666
  this.logger.info("Reindexing cached Metal files for parser support");
12578
13667
  }
12579
13668
  const includePatterns = [...this.config.include, ...this.config.additionalInclude];
@@ -12617,8 +13706,8 @@ var Indexer = class _Indexer {
12617
13706
  const needsCallGraphRefresh = cachedHashMatches && needsCallGraphResolutionMigration && database.getChunksByFile(storedPath).some(
12618
13707
  (chunk) => chunk.language === "php" || chunk.language === "c" || chunk.language === "cpp"
12619
13708
  );
12620
- const requiresSwiftParserUpgrade = reparseCachedSwiftFiles && path19.extname(storedPath).toLowerCase() === ".swift";
12621
- const requiresMetalParserUpgrade = reparseCachedMetalFiles && path19.extname(storedPath).toLowerCase() === ".metal";
13709
+ const requiresSwiftParserUpgrade = reparseCachedSwiftFiles && path20.extname(storedPath).toLowerCase() === ".swift";
13710
+ const requiresMetalParserUpgrade = reparseCachedMetalFiles && path20.extname(storedPath).toLowerCase() === ".metal";
12622
13711
  const inMigrationScope = forceScopedReembed && scopedRoots !== null && this.isFileInCurrentScope(storedPath, scopedRoots);
12623
13712
  if (cachedHashMatches && !inMigrationScope && !needsCallGraphRefresh && !requiresSwiftParserUpgrade && !requiresMetalParserUpgrade && !refreshCachedSymbols) {
12624
13713
  unchangedFilePaths.add(storedPath);
@@ -12677,7 +13766,7 @@ var Indexer = class _Indexer {
12677
13766
  }
12678
13767
  }
12679
13768
  }
12680
- const shouldRetryFailedPath = (filePath) => filePath !== null && currentFileHashes.has(filePath) && unchangedFilePaths.has(filePath);
13769
+ const shouldRetryFailedPath = (filePath) => filePath !== null && !this.isStoredPathExcluded(filePath) && currentFileHashes.has(filePath) && unchangedFilePaths.has(filePath);
12681
13770
  const failedProcessing = this.prepareFailedBatchProcessing(scopedRoots, shouldRetryFailedPath);
12682
13771
  const maxChunkTokens = getSafeEmbeddingChunkTokenLimit(configuredProviderInfo);
12683
13772
  const providerRateLimits = this.getProviderRateLimits(configuredProviderInfo.provider);
@@ -12781,7 +13870,7 @@ var Indexer = class _Indexer {
12781
13870
  throw new Error(`Parsed file was not present in its source batch: ${parsed.path}`);
12782
13871
  }
12783
13872
  if (parsed.chunks.length === 0) {
12784
- stats.parseFailures.push(path19.isAbsolute(parsed.path) ? path19.relative(this.projectRoot, parsed.path) : parsed.path);
13873
+ stats.parseFailures.push(path20.isAbsolute(parsed.path) ? path20.relative(this.projectRoot, parsed.path) : parsed.path);
12785
13874
  }
12786
13875
  let chunksToProcess = parsed.chunks;
12787
13876
  if (this.config.indexing.fallbackToTextOnMaxChunks && chunksToProcess.length > this.config.indexing.maxChunksPerFile) {
@@ -13116,8 +14205,8 @@ var Indexer = class _Indexer {
13116
14205
  previousBranchSymbolIds,
13117
14206
  Array.from(allSymbolIds)
13118
14207
  );
13119
- const vectorPath = path19.join(this.indexPath, "vectors");
13120
- const shouldFingerprintLegacyPair = !store.hasFingerprint() && existsSync11(vectorPath) && existsSync11(`${vectorPath}.meta.json`);
14208
+ const vectorPath = path20.join(this.indexPath, "vectors");
14209
+ const shouldFingerprintLegacyPair = !store.hasFingerprint() && existsSync12(vectorPath) && existsSync12(`${vectorPath}.meta.json`);
13121
14210
  if (backfilledBlameMetadata || shouldFingerprintLegacyPair || removedStoredChunks) {
13122
14211
  store.save();
13123
14212
  }
@@ -13912,7 +15001,7 @@ var Indexer = class _Indexer {
13912
15001
  const missingChunkKeys = [];
13913
15002
  const chunkKeysByRemovedFile = /* @__PURE__ */ new Map();
13914
15003
  for (const [filePath, chunkKeys] of filePathsToChunkKeys) {
13915
- if (!existsSync11(this.toMaterializedFilePath(filePath))) {
15004
+ if (!existsSync12(this.toMaterializedFilePath(filePath))) {
13916
15005
  chunkKeysByRemovedFile.set(filePath, chunkKeys);
13917
15006
  for (const key of chunkKeys) {
13918
15007
  missingChunkKeys.push(key);
@@ -13975,7 +15064,7 @@ var Indexer = class _Indexer {
13975
15064
  gcOrphanSymbols: 0,
13976
15065
  gcOrphanCallEdges: 0,
13977
15066
  resetCorruptedIndex: true,
13978
- warning: this.getCorruptedIndexWarning(path19.join(this.indexPath, "codebase.db"))
15067
+ warning: this.getCorruptedIndexWarning(path20.join(this.indexPath, "codebase.db"))
13979
15068
  };
13980
15069
  }
13981
15070
  this.logger.recordGc(removedCount, gcOrphanChunks, gcOrphanEmbeddings);
@@ -14005,7 +15094,8 @@ var Indexer = class _Indexer {
14005
15094
  const maxChunkTokens = getSafeEmbeddingChunkTokenLimit(configuredProviderInfo);
14006
15095
  const providerRateLimits = this.getProviderRateLimits(configuredProviderInfo.provider);
14007
15096
  const roots = this.config.scope === "global" ? this.getScopedRoots() : null;
14008
- const failedProcessing = this.prepareFailedBatchProcessing(roots, () => true);
15097
+ const shouldProcessFailedPath = (filePath) => filePath === null || !this.isStoredPathExcluded(filePath);
15098
+ const failedProcessing = this.prepareFailedBatchProcessing(roots, shouldProcessFailedPath);
14009
15099
  if (failedProcessing.latestById.size === 0) {
14010
15100
  this.finalizeFailedBatchWriteState(failedProcessing.state);
14011
15101
  return { succeeded: 0, failed: 0, remaining: 0 };
@@ -14018,7 +15108,7 @@ var Indexer = class _Indexer {
14018
15108
  const retryableChunks = this.iterateLatestFailedChunks(
14019
15109
  failedProcessing.latestById,
14020
15110
  roots,
14021
- () => true,
15111
+ shouldProcessFailedPath,
14022
15112
  maxChunkTokens
14023
15113
  );
14024
15114
  for (const retryBatch of iterateOrderedFileBatches(
@@ -14288,9 +15378,9 @@ var Indexer = class _Indexer {
14288
15378
  this.requireReadableComponents(readIssues, "database");
14289
15379
  let shortest = [];
14290
15380
  for (const branchKey of this.getBranchCatalogKeys()) {
14291
- const path25 = database.findShortestPath(fromName, toName, branchKey, maxDepth);
14292
- if (path25.length > 0 && (shortest.length === 0 || path25.length < shortest.length)) {
14293
- shortest = path25;
15381
+ const path26 = database.findShortestPath(fromName, toName, branchKey, maxDepth);
15382
+ if (path26.length > 0 && (shortest.length === 0 || path26.length < shortest.length)) {
15383
+ shortest = path26;
14294
15384
  }
14295
15385
  }
14296
15386
  return shortest.map((hop) => this.resolveFilePathRecord(hop));
@@ -14338,13 +15428,13 @@ var Indexer = class _Indexer {
14338
15428
  }
14339
15429
  }
14340
15430
  if (!found) continue;
14341
- const path25 = [];
15431
+ const path26 = [];
14342
15432
  let currentSymbolId = toSymbolId;
14343
15433
  while (true) {
14344
15434
  const symbol = symbolsById.get(currentSymbolId);
14345
15435
  if (!symbol) break;
14346
15436
  const parent = parentBySymbolId.get(currentSymbolId);
14347
- path25.push({
15437
+ path26.push({
14348
15438
  symbolId: symbol.id,
14349
15439
  symbolName: symbol.name,
14350
15440
  filePath: symbol.filePath,
@@ -14354,9 +15444,9 @@ var Indexer = class _Indexer {
14354
15444
  if (!parent) break;
14355
15445
  currentSymbolId = parent.parentId;
14356
15446
  }
14357
- path25.reverse();
14358
- if (path25.length > 0 && (shortest.length === 0 || path25.length < shortest.length)) {
14359
- shortest = path25;
15447
+ path26.reverse();
15448
+ if (path26.length > 0 && (shortest.length === 0 || path26.length < shortest.length)) {
15449
+ shortest = path26;
14360
15450
  }
14361
15451
  }
14362
15452
  return shortest.map((hop) => this.resolveFilePathRecord(hop));
@@ -14507,7 +15597,7 @@ var Indexer = class _Indexer {
14507
15597
  );
14508
15598
  }
14509
15599
  }
14510
- const toStoredChangedFiles = (filePaths) => filePaths.map((filePath) => this.toStoredFilePath(path19.resolve(this.projectRoot, filePath)));
15600
+ const toStoredChangedFiles = (filePaths) => filePaths.map((filePath) => this.toStoredFilePath(path20.resolve(this.projectRoot, filePath)));
14511
15601
  const storedChangedFiles = toStoredChangedFiles(changedFiles);
14512
15602
  const directSymbols = database.getSymbolsForFiles(storedChangedFiles, branchKey);
14513
15603
  const directIds = directSymbols.map((s) => s.id);
@@ -14656,12 +15746,12 @@ var Indexer = class _Indexer {
14656
15746
  if (meta.filePath) filePaths.add(meta.filePath);
14657
15747
  }
14658
15748
  const directory = options?.directory?.replace(/\/$/, "");
14659
- const absoluteDirectoryFilter = directory ? path19.resolve(this.projectRoot, directory) : void 0;
15749
+ const absoluteDirectoryFilter = directory ? path20.resolve(this.projectRoot, directory) : void 0;
14660
15750
  for (const filePath of filePaths) {
14661
15751
  if (directory) {
14662
15752
  const absoluteFilePath = this.resolveStoredFilePath(filePath);
14663
15753
  const matchesRelative = filePath === directory || filePath.startsWith(directory + "/");
14664
- const matchesProjectRelative = absoluteDirectoryFilter !== void 0 && (absoluteFilePath === absoluteDirectoryFilter || absoluteFilePath.startsWith(absoluteDirectoryFilter + path19.sep));
15754
+ const matchesProjectRelative = absoluteDirectoryFilter !== void 0 && (absoluteFilePath === absoluteDirectoryFilter || absoluteFilePath.startsWith(absoluteDirectoryFilter + path20.sep));
14665
15755
  if (!matchesRelative && !matchesProjectRelative) {
14666
15756
  continue;
14667
15757
  }
@@ -14778,7 +15868,10 @@ function getOrCreateIndexer(projectRoot3, host) {
14778
15868
  }
14779
15869
  const indexer = new Indexer(projectRoot3, config, host);
14780
15870
  indexerCache.set(key, indexer);
14781
- configureAutoIndex(projectRoot3, host, config, () => getOrCreateIndexer(projectRoot3, host));
15871
+ configureAutoIndex(projectRoot3, host, config, () => getOrCreateIndexer(projectRoot3, host), {
15872
+ preserveManagedWorker: true,
15873
+ synchronizeBackgroundWorker: false
15874
+ });
14782
15875
  return indexer;
14783
15876
  }
14784
15877
  function getIndexerForProject(projectRoot3, host) {
@@ -14793,7 +15886,9 @@ function refreshIndexerForDirectory(projectRoot3, host, config = parseConfig(loa
14793
15886
  const key = getIndexerCacheKey(projectRoot3, host);
14794
15887
  configCache.set(key, config);
14795
15888
  indexerCache.set(key, new Indexer(projectRoot3, config, host));
14796
- configureAutoIndex(projectRoot3, host, config, () => getOrCreateIndexer(projectRoot3, host));
15889
+ configureAutoIndex(projectRoot3, host, config, () => getOrCreateIndexer(projectRoot3, host), {
15890
+ synchronizeBackgroundWorker: true
15891
+ });
14797
15892
  return config;
14798
15893
  }
14799
15894
  var AutoIndexRetrievalUnavailableError = class extends Error {
@@ -14820,7 +15915,7 @@ function trimOrUndefined(value) {
14820
15915
  return normalized || void 0;
14821
15916
  }
14822
15917
  function normalizeCallGraphPath(value) {
14823
- let normalized = path20.posix.normalize(value.trim().replaceAll("\\", "/"));
15918
+ let normalized = path21.posix.normalize(value.trim().replaceAll("\\", "/"));
14824
15919
  if (normalized.startsWith("./")) {
14825
15920
  normalized = normalized.slice(2);
14826
15921
  }
@@ -15013,12 +16108,12 @@ async function getCallGraphPath(projectRoot3, host, from, to, maxDepth, fromFile
15013
16108
  if (fromResolution.status !== "resolved" || toResolution.status !== "resolved") {
15014
16109
  return { from: fromResolution, to: toResolution, path: [] };
15015
16110
  }
15016
- const path25 = await indexer.findCallPathBySymbolIds(
16111
+ const path26 = await indexer.findCallPathBySymbolIds(
15017
16112
  fromResolution.symbolId,
15018
16113
  toResolution.symbolId,
15019
16114
  maxDepth
15020
16115
  );
15021
- return { from: fromResolution, to: toResolution, path: path25 };
16116
+ return { from: fromResolution, to: toResolution, path: path26 };
15022
16117
  }
15023
16118
  async function runIndexCodebase(projectRoot3, host, args, onProgress) {
15024
16119
  const root = getProjectRoot(projectRoot3, host);
@@ -15027,6 +16122,9 @@ async function runIndexCodebase(projectRoot3, host, args, onProgress) {
15027
16122
  if (args.estimateOnly) {
15028
16123
  return { kind: "estimate", estimate: await indexer.estimateCost() };
15029
16124
  }
16125
+ if (args.dryRun) {
16126
+ return { kind: "dryrun", dryrun: await indexer.dryRunCost() };
16127
+ }
15030
16128
  const coordinated = runCoordinatedIndex(root, host, args.force ?? false, (progress) => {
15031
16129
  if (onProgress) {
15032
16130
  void onProgress(formatProgressTitle(progress), {
@@ -15213,15 +16311,15 @@ async function getIndexLogs(projectRoot3, host, args) {
15213
16311
  function addKnowledgeBase(projectRoot3, host, knowledgeBasePath) {
15214
16312
  const root = getProjectRoot(projectRoot3, host);
15215
16313
  const inputPath = knowledgeBasePath.trim();
15216
- const normalizedPath2 = path20.resolve(
15217
- path20.isAbsolute(inputPath) ? inputPath : resolveKnowledgeBasePath(inputPath, root)
16314
+ const normalizedPath2 = path21.resolve(
16315
+ path21.isAbsolute(inputPath) ? inputPath : resolveKnowledgeBasePath(inputPath, root)
15218
16316
  );
15219
- if (!existsSync12(normalizedPath2)) {
16317
+ if (!existsSync13(normalizedPath2)) {
15220
16318
  return `Error: Directory does not exist: ${normalizedPath2}`;
15221
16319
  }
15222
16320
  let realPath;
15223
16321
  try {
15224
- realPath = realpathSync5(normalizedPath2);
16322
+ realPath = realpathSync6(normalizedPath2);
15225
16323
  } catch {
15226
16324
  return `Error: Cannot resolve path: ${normalizedPath2}`;
15227
16325
  }
@@ -15250,7 +16348,7 @@ function addKnowledgeBase(projectRoot3, host, knowledgeBasePath) {
15250
16348
  }
15251
16349
  }
15252
16350
  for (const dotDir of sensitiveDotDirs) {
15253
- const sensitiveDir = path20.join(homeDir, dotDir);
16351
+ const sensitiveDir = path21.join(homeDir, dotDir);
15254
16352
  if (sensitiveDir && (realPath === sensitiveDir || realPath.startsWith(`${sensitiveDir}/`))) {
15255
16353
  return `Error: Adding sensitive directory as knowledge base is not allowed: ${normalizedPath2}`;
15256
16354
  }
@@ -15296,7 +16394,7 @@ function listKnowledgeBases(projectRoot3, host) {
15296
16394
  for (let i = 0; i < knowledgeBases.length; i++) {
15297
16395
  const kb = knowledgeBases[i];
15298
16396
  const resolvedPath = resolveKnowledgeBasePath(kb, root);
15299
- const exists = existsSync12(resolvedPath);
16397
+ const exists = existsSync13(resolvedPath);
15300
16398
  result += `[${i + 1}] ${kb}
15301
16399
  `;
15302
16400
  result += ` Resolved: ${resolvedPath}
@@ -15313,7 +16411,7 @@ function listKnowledgeBases(projectRoot3, host) {
15313
16411
  }
15314
16412
  result += "\n";
15315
16413
  }
15316
- const hasHostConfig = existsSync12(path20.join(root, getHostProjectConfigRelativePath(host)));
16414
+ const hasHostConfig = existsSync13(path21.join(root, getHostProjectConfigRelativePath(host)));
15317
16415
  if (hasHostConfig) {
15318
16416
  result += `
15319
16417
  Config sources: 1 file(s).`;
@@ -15786,7 +16884,7 @@ async function resolveCodebaseContextUnmeasured(projectRoot3, host, input) {
15786
16884
  const directory = input.directory ?? void 0;
15787
16885
  const tokenBudget = input.tokenBudget ?? void 0;
15788
16886
  if (from && to) {
15789
- const path25 = await getCallGraphPath(
16887
+ const path26 = await getCallGraphPath(
15790
16888
  projectRoot3,
15791
16889
  host,
15792
16890
  from,
@@ -15795,25 +16893,25 @@ async function resolveCodebaseContextUnmeasured(projectRoot3, host, input) {
15795
16893
  fromFilePath,
15796
16894
  toFilePath
15797
16895
  );
15798
- const pathText = formatCallGraphPathResult(path25);
15799
- if (path25.path.length > 0) {
16896
+ const pathText = formatCallGraphPathResult(path26);
16897
+ if (path26.path.length > 0) {
15800
16898
  const fitted2 = fitTextToContextBudget(
15801
16899
  pathText,
15802
16900
  tokenBudget
15803
16901
  );
15804
16902
  return {
15805
16903
  text: fitted2.text,
15806
- details: fittedDetails("path", fitted2, path25.path.length)
16904
+ details: fittedDetails("path", fitted2, path26.path.length)
15807
16905
  };
15808
16906
  }
15809
- if (path25.from.status !== "resolved" || path25.to.status !== "resolved") {
16907
+ if (path26.from.status !== "resolved" || path26.to.status !== "resolved") {
15810
16908
  const fitted2 = fitTextToContextBudget(pathText, tokenBudget);
15811
16909
  return {
15812
16910
  text: fitted2.text,
15813
16911
  details: fittedDetails("path", fitted2, 0)
15814
16912
  };
15815
16913
  }
15816
- const resolvedFrom = path25.from;
16914
+ const resolvedFrom = path26.from;
15817
16915
  const { callers } = await getCallGraphData(projectRoot3, host, {
15818
16916
  name: to,
15819
16917
  direction: "callers",
@@ -16254,7 +17352,7 @@ function registerPiCallGraphTools(pi) {
16254
17352
  }
16255
17353
 
16256
17354
  // src/watcher/file-watcher.ts
16257
- import { existsSync as existsSync13, statSync as statSync6 } from "fs";
17355
+ import { existsSync as existsSync14, statSync as statSync6 } from "fs";
16258
17356
 
16259
17357
  // node_modules/chokidar/index.js
16260
17358
  import { EventEmitter as EventEmitter2 } from "events";
@@ -16346,7 +17444,7 @@ var ReaddirpStream = class extends Readable {
16346
17444
  this._directoryFilter = normalizeFilter(opts.directoryFilter);
16347
17445
  const statMethod = opts.lstat ? lstat : stat;
16348
17446
  if (wantBigintFsStats) {
16349
- this._stat = (path25) => statMethod(path25, { bigint: true });
17447
+ this._stat = (path26) => statMethod(path26, { bigint: true });
16350
17448
  } else {
16351
17449
  this._stat = statMethod;
16352
17450
  }
@@ -16371,8 +17469,8 @@ var ReaddirpStream = class extends Readable {
16371
17469
  const par = this.parent;
16372
17470
  const fil = par && par.files;
16373
17471
  if (fil && fil.length > 0) {
16374
- const { path: path25, depth } = par;
16375
- const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path25));
17472
+ const { path: path26, depth } = par;
17473
+ const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path26));
16376
17474
  const awaited = await Promise.all(slice);
16377
17475
  for (const entry of awaited) {
16378
17476
  if (!entry)
@@ -16412,21 +17510,21 @@ var ReaddirpStream = class extends Readable {
16412
17510
  this.reading = false;
16413
17511
  }
16414
17512
  }
16415
- async _exploreDir(path25, depth) {
17513
+ async _exploreDir(path26, depth) {
16416
17514
  let files;
16417
17515
  try {
16418
- files = await readdir(path25, this._rdOptions);
17516
+ files = await readdir(path26, this._rdOptions);
16419
17517
  } catch (error) {
16420
17518
  this._onError(error);
16421
17519
  }
16422
- return { files, depth, path: path25 };
17520
+ return { files, depth, path: path26 };
16423
17521
  }
16424
- async _formatEntry(dirent, path25) {
17522
+ async _formatEntry(dirent, path26) {
16425
17523
  let entry;
16426
- const basename7 = this._isDirent ? dirent.name : dirent;
17524
+ const basename8 = this._isDirent ? dirent.name : dirent;
16427
17525
  try {
16428
- const fullPath = presolve(pjoin(path25, basename7));
16429
- entry = { path: prelative(this._root, fullPath), fullPath, basename: basename7 };
17526
+ const fullPath = presolve(pjoin(path26, basename8));
17527
+ entry = { path: prelative(this._root, fullPath), fullPath, basename: basename8 };
16430
17528
  entry[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath);
16431
17529
  } catch (err) {
16432
17530
  this._onError(err);
@@ -16825,16 +17923,16 @@ var delFromSet = (main, prop, item) => {
16825
17923
  };
16826
17924
  var isEmptySet = (val) => val instanceof Set ? val.size === 0 : !val;
16827
17925
  var FsWatchInstances = /* @__PURE__ */ new Map();
16828
- function createFsWatchInstance(path25, options, listener, errHandler, emitRaw) {
17926
+ function createFsWatchInstance(path26, options, listener, errHandler, emitRaw) {
16829
17927
  const handleEvent = (rawEvent, evPath) => {
16830
- listener(path25);
16831
- emitRaw(rawEvent, evPath, { watchedPath: path25 });
16832
- if (evPath && path25 !== evPath) {
16833
- fsWatchBroadcast(sp.resolve(path25, evPath), KEY_LISTENERS, sp.join(path25, evPath));
17928
+ listener(path26);
17929
+ emitRaw(rawEvent, evPath, { watchedPath: path26 });
17930
+ if (evPath && path26 !== evPath) {
17931
+ fsWatchBroadcast(sp.resolve(path26, evPath), KEY_LISTENERS, sp.join(path26, evPath));
16834
17932
  }
16835
17933
  };
16836
17934
  try {
16837
- return fs_watch(path25, {
17935
+ return fs_watch(path26, {
16838
17936
  persistent: options.persistent
16839
17937
  }, handleEvent);
16840
17938
  } catch (error) {
@@ -16850,12 +17948,12 @@ var fsWatchBroadcast = (fullPath, listenerType, val1, val2, val3) => {
16850
17948
  listener(val1, val2, val3);
16851
17949
  });
16852
17950
  };
16853
- var setFsWatchListener = (path25, fullPath, options, handlers) => {
17951
+ var setFsWatchListener = (path26, fullPath, options, handlers) => {
16854
17952
  const { listener, errHandler, rawEmitter } = handlers;
16855
17953
  let cont = FsWatchInstances.get(fullPath);
16856
17954
  let watcher;
16857
17955
  if (!options.persistent) {
16858
- watcher = createFsWatchInstance(path25, options, listener, errHandler, rawEmitter);
17956
+ watcher = createFsWatchInstance(path26, options, listener, errHandler, rawEmitter);
16859
17957
  if (!watcher)
16860
17958
  return;
16861
17959
  return watcher.close.bind(watcher);
@@ -16866,7 +17964,7 @@ var setFsWatchListener = (path25, fullPath, options, handlers) => {
16866
17964
  addAndConvert(cont, KEY_RAW, rawEmitter);
16867
17965
  } else {
16868
17966
  watcher = createFsWatchInstance(
16869
- path25,
17967
+ path26,
16870
17968
  options,
16871
17969
  fsWatchBroadcast.bind(null, fullPath, KEY_LISTENERS),
16872
17970
  errHandler,
@@ -16881,7 +17979,7 @@ var setFsWatchListener = (path25, fullPath, options, handlers) => {
16881
17979
  cont.watcherUnusable = true;
16882
17980
  if (isWindows && error.code === "EPERM") {
16883
17981
  try {
16884
- const fd = await open(path25, "r");
17982
+ const fd = await open(path26, "r");
16885
17983
  await fd.close();
16886
17984
  broadcastErr(error);
16887
17985
  } catch (err) {
@@ -16912,7 +18010,7 @@ var setFsWatchListener = (path25, fullPath, options, handlers) => {
16912
18010
  };
16913
18011
  };
16914
18012
  var FsWatchFileInstances = /* @__PURE__ */ new Map();
16915
- var setFsWatchFileListener = (path25, fullPath, options, handlers) => {
18013
+ var setFsWatchFileListener = (path26, fullPath, options, handlers) => {
16916
18014
  const { listener, rawEmitter } = handlers;
16917
18015
  let cont = FsWatchFileInstances.get(fullPath);
16918
18016
  const copts = cont && cont.options;
@@ -16934,7 +18032,7 @@ var setFsWatchFileListener = (path25, fullPath, options, handlers) => {
16934
18032
  });
16935
18033
  const currmtime = curr.mtimeMs;
16936
18034
  if (curr.size !== prev.size || currmtime > prev.mtimeMs || currmtime === 0) {
16937
- foreach(cont.listeners, (listener2) => listener2(path25, curr));
18035
+ foreach(cont.listeners, (listener2) => listener2(path26, curr));
16938
18036
  }
16939
18037
  })
16940
18038
  };
@@ -16964,13 +18062,13 @@ var NodeFsHandler = class {
16964
18062
  * @param listener on fs change
16965
18063
  * @returns closer for the watcher instance
16966
18064
  */
16967
- _watchWithNodeFs(path25, listener) {
18065
+ _watchWithNodeFs(path26, listener) {
16968
18066
  const opts = this.fsw.options;
16969
- const directory = sp.dirname(path25);
16970
- const basename7 = sp.basename(path25);
18067
+ const directory = sp.dirname(path26);
18068
+ const basename8 = sp.basename(path26);
16971
18069
  const parent = this.fsw._getWatchedDir(directory);
16972
- parent.add(basename7);
16973
- const absolutePath = sp.resolve(path25);
18070
+ parent.add(basename8);
18071
+ const absolutePath = sp.resolve(path26);
16974
18072
  const options = {
16975
18073
  persistent: opts.persistent
16976
18074
  };
@@ -16979,13 +18077,13 @@ var NodeFsHandler = class {
16979
18077
  let closer;
16980
18078
  if (opts.usePolling) {
16981
18079
  const enableBin = opts.interval !== opts.binaryInterval;
16982
- options.interval = enableBin && isBinaryPath(basename7) ? opts.binaryInterval : opts.interval;
16983
- closer = setFsWatchFileListener(path25, absolutePath, options, {
18080
+ options.interval = enableBin && isBinaryPath(basename8) ? opts.binaryInterval : opts.interval;
18081
+ closer = setFsWatchFileListener(path26, absolutePath, options, {
16984
18082
  listener,
16985
18083
  rawEmitter: this.fsw._emitRaw
16986
18084
  });
16987
18085
  } else {
16988
- closer = setFsWatchListener(path25, absolutePath, options, {
18086
+ closer = setFsWatchListener(path26, absolutePath, options, {
16989
18087
  listener,
16990
18088
  errHandler: this._boundHandleError,
16991
18089
  rawEmitter: this.fsw._emitRaw
@@ -17001,13 +18099,13 @@ var NodeFsHandler = class {
17001
18099
  if (this.fsw.closed) {
17002
18100
  return;
17003
18101
  }
17004
- const dirname13 = sp.dirname(file);
17005
- const basename7 = sp.basename(file);
17006
- const parent = this.fsw._getWatchedDir(dirname13);
18102
+ const dirname14 = sp.dirname(file);
18103
+ const basename8 = sp.basename(file);
18104
+ const parent = this.fsw._getWatchedDir(dirname14);
17007
18105
  let prevStats = stats;
17008
- if (parent.has(basename7))
18106
+ if (parent.has(basename8))
17009
18107
  return;
17010
- const listener = async (path25, newStats) => {
18108
+ const listener = async (path26, newStats) => {
17011
18109
  if (!this.fsw._throttle(THROTTLE_MODE_WATCH, file, 5))
17012
18110
  return;
17013
18111
  if (!newStats || newStats.mtimeMs === 0) {
@@ -17021,18 +18119,18 @@ var NodeFsHandler = class {
17021
18119
  this.fsw._emit(EV.CHANGE, file, newStats2);
17022
18120
  }
17023
18121
  if ((isMacos || isLinux || isFreeBSD) && prevStats.ino !== newStats2.ino) {
17024
- this.fsw._closeFile(path25);
18122
+ this.fsw._closeFile(path26);
17025
18123
  prevStats = newStats2;
17026
18124
  const closer2 = this._watchWithNodeFs(file, listener);
17027
18125
  if (closer2)
17028
- this.fsw._addPathCloser(path25, closer2);
18126
+ this.fsw._addPathCloser(path26, closer2);
17029
18127
  } else {
17030
18128
  prevStats = newStats2;
17031
18129
  }
17032
18130
  } catch (error) {
17033
- this.fsw._remove(dirname13, basename7);
18131
+ this.fsw._remove(dirname14, basename8);
17034
18132
  }
17035
- } else if (parent.has(basename7)) {
18133
+ } else if (parent.has(basename8)) {
17036
18134
  const at = newStats.atimeMs;
17037
18135
  const mt = newStats.mtimeMs;
17038
18136
  if (!at || at <= mt || mt !== prevStats.mtimeMs) {
@@ -17057,7 +18155,7 @@ var NodeFsHandler = class {
17057
18155
  * @param item basename of this item
17058
18156
  * @returns true if no more processing is needed for this entry.
17059
18157
  */
17060
- async _handleSymlink(entry, directory, path25, item) {
18158
+ async _handleSymlink(entry, directory, path26, item) {
17061
18159
  if (this.fsw.closed) {
17062
18160
  return;
17063
18161
  }
@@ -17067,7 +18165,7 @@ var NodeFsHandler = class {
17067
18165
  this.fsw._incrReadyCount();
17068
18166
  let linkPath;
17069
18167
  try {
17070
- linkPath = await fsrealpath(path25);
18168
+ linkPath = await fsrealpath(path26);
17071
18169
  } catch (e) {
17072
18170
  this.fsw._emitReady();
17073
18171
  return true;
@@ -17077,12 +18175,12 @@ var NodeFsHandler = class {
17077
18175
  if (dir.has(item)) {
17078
18176
  if (this.fsw._symlinkPaths.get(full) !== linkPath) {
17079
18177
  this.fsw._symlinkPaths.set(full, linkPath);
17080
- this.fsw._emit(EV.CHANGE, path25, entry.stats);
18178
+ this.fsw._emit(EV.CHANGE, path26, entry.stats);
17081
18179
  }
17082
18180
  } else {
17083
18181
  dir.add(item);
17084
18182
  this.fsw._symlinkPaths.set(full, linkPath);
17085
- this.fsw._emit(EV.ADD, path25, entry.stats);
18183
+ this.fsw._emit(EV.ADD, path26, entry.stats);
17086
18184
  }
17087
18185
  this.fsw._emitReady();
17088
18186
  return true;
@@ -17112,9 +18210,9 @@ var NodeFsHandler = class {
17112
18210
  return;
17113
18211
  }
17114
18212
  const item = entry.path;
17115
- let path25 = sp.join(directory, item);
18213
+ let path26 = sp.join(directory, item);
17116
18214
  current.add(item);
17117
- if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path25, item)) {
18215
+ if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path26, item)) {
17118
18216
  return;
17119
18217
  }
17120
18218
  if (this.fsw.closed) {
@@ -17123,11 +18221,11 @@ var NodeFsHandler = class {
17123
18221
  }
17124
18222
  if (item === target || !target && !previous.has(item)) {
17125
18223
  this.fsw._incrReadyCount();
17126
- path25 = sp.join(dir, sp.relative(dir, path25));
17127
- this._addToNodeFs(path25, initialAdd, wh, depth + 1);
18224
+ path26 = sp.join(dir, sp.relative(dir, path26));
18225
+ this._addToNodeFs(path26, initialAdd, wh, depth + 1);
17128
18226
  }
17129
18227
  }).on(EV.ERROR, this._boundHandleError);
17130
- return new Promise((resolve17, reject) => {
18228
+ return new Promise((resolve18, reject) => {
17131
18229
  if (!stream)
17132
18230
  return reject();
17133
18231
  stream.once(STR_END, () => {
@@ -17136,7 +18234,7 @@ var NodeFsHandler = class {
17136
18234
  return;
17137
18235
  }
17138
18236
  const wasThrottled = throttler ? throttler.clear() : false;
17139
- resolve17(void 0);
18237
+ resolve18(void 0);
17140
18238
  previous.getChildren().filter((item) => {
17141
18239
  return item !== directory && !current.has(item);
17142
18240
  }).forEach((item) => {
@@ -17193,13 +18291,13 @@ var NodeFsHandler = class {
17193
18291
  * @param depth Child path actually targeted for watch
17194
18292
  * @param target Child path actually targeted for watch
17195
18293
  */
17196
- async _addToNodeFs(path25, initialAdd, priorWh, depth, target) {
18294
+ async _addToNodeFs(path26, initialAdd, priorWh, depth, target) {
17197
18295
  const ready = this.fsw._emitReady;
17198
- if (this.fsw._isIgnored(path25) || this.fsw.closed) {
18296
+ if (this.fsw._isIgnored(path26) || this.fsw.closed) {
17199
18297
  ready();
17200
18298
  return false;
17201
18299
  }
17202
- const wh = this.fsw._getWatchHelpers(path25);
18300
+ const wh = this.fsw._getWatchHelpers(path26);
17203
18301
  if (priorWh) {
17204
18302
  wh.filterPath = (entry) => priorWh.filterPath(entry);
17205
18303
  wh.filterDir = (entry) => priorWh.filterDir(entry);
@@ -17215,8 +18313,8 @@ var NodeFsHandler = class {
17215
18313
  const follow = this.fsw.options.followSymlinks;
17216
18314
  let closer;
17217
18315
  if (stats.isDirectory()) {
17218
- const absPath = sp.resolve(path25);
17219
- const targetPath = follow ? await fsrealpath(path25) : path25;
18316
+ const absPath = sp.resolve(path26);
18317
+ const targetPath = follow ? await fsrealpath(path26) : path26;
17220
18318
  if (this.fsw.closed)
17221
18319
  return;
17222
18320
  closer = await this._handleDir(wh.watchPath, stats, initialAdd, depth, target, wh, targetPath);
@@ -17226,29 +18324,29 @@ var NodeFsHandler = class {
17226
18324
  this.fsw._symlinkPaths.set(absPath, targetPath);
17227
18325
  }
17228
18326
  } else if (stats.isSymbolicLink()) {
17229
- const targetPath = follow ? await fsrealpath(path25) : path25;
18327
+ const targetPath = follow ? await fsrealpath(path26) : path26;
17230
18328
  if (this.fsw.closed)
17231
18329
  return;
17232
18330
  const parent = sp.dirname(wh.watchPath);
17233
18331
  this.fsw._getWatchedDir(parent).add(wh.watchPath);
17234
18332
  this.fsw._emit(EV.ADD, wh.watchPath, stats);
17235
- closer = await this._handleDir(parent, stats, initialAdd, depth, path25, wh, targetPath);
18333
+ closer = await this._handleDir(parent, stats, initialAdd, depth, path26, wh, targetPath);
17236
18334
  if (this.fsw.closed)
17237
18335
  return;
17238
18336
  if (targetPath !== void 0) {
17239
- this.fsw._symlinkPaths.set(sp.resolve(path25), targetPath);
18337
+ this.fsw._symlinkPaths.set(sp.resolve(path26), targetPath);
17240
18338
  }
17241
18339
  } else {
17242
18340
  closer = this._handleFile(wh.watchPath, stats, initialAdd);
17243
18341
  }
17244
18342
  ready();
17245
18343
  if (closer)
17246
- this.fsw._addPathCloser(path25, closer);
18344
+ this.fsw._addPathCloser(path26, closer);
17247
18345
  return false;
17248
18346
  } catch (error) {
17249
18347
  if (this.fsw._handleError(error)) {
17250
18348
  ready();
17251
- return path25;
18349
+ return path26;
17252
18350
  }
17253
18351
  }
17254
18352
  }
@@ -17291,24 +18389,24 @@ function createPattern(matcher) {
17291
18389
  }
17292
18390
  return () => false;
17293
18391
  }
17294
- function normalizePath2(path25) {
17295
- if (typeof path25 !== "string")
18392
+ function normalizePath2(path26) {
18393
+ if (typeof path26 !== "string")
17296
18394
  throw new Error("string expected");
17297
- path25 = sp2.normalize(path25);
17298
- path25 = path25.replace(/\\/g, "/");
18395
+ path26 = sp2.normalize(path26);
18396
+ path26 = path26.replace(/\\/g, "/");
17299
18397
  let prepend = false;
17300
- if (path25.startsWith("//"))
18398
+ if (path26.startsWith("//"))
17301
18399
  prepend = true;
17302
- path25 = path25.replace(DOUBLE_SLASH_RE, "/");
18400
+ path26 = path26.replace(DOUBLE_SLASH_RE, "/");
17303
18401
  if (prepend)
17304
- path25 = "/" + path25;
17305
- return path25;
18402
+ path26 = "/" + path26;
18403
+ return path26;
17306
18404
  }
17307
18405
  function matchPatterns(patterns, testString, stats) {
17308
- const path25 = normalizePath2(testString);
18406
+ const path26 = normalizePath2(testString);
17309
18407
  for (let index = 0; index < patterns.length; index++) {
17310
18408
  const pattern = patterns[index];
17311
- if (pattern(path25, stats)) {
18409
+ if (pattern(path26, stats)) {
17312
18410
  return true;
17313
18411
  }
17314
18412
  }
@@ -17346,19 +18444,19 @@ var toUnix = (string) => {
17346
18444
  }
17347
18445
  return str;
17348
18446
  };
17349
- var normalizePathToUnix = (path25) => toUnix(sp2.normalize(toUnix(path25)));
17350
- var normalizeIgnored = (cwd = "") => (path25) => {
17351
- if (typeof path25 === "string") {
17352
- return normalizePathToUnix(sp2.isAbsolute(path25) ? path25 : sp2.join(cwd, path25));
18447
+ var normalizePathToUnix = (path26) => toUnix(sp2.normalize(toUnix(path26)));
18448
+ var normalizeIgnored = (cwd = "") => (path26) => {
18449
+ if (typeof path26 === "string") {
18450
+ return normalizePathToUnix(sp2.isAbsolute(path26) ? path26 : sp2.join(cwd, path26));
17353
18451
  } else {
17354
- return path25;
18452
+ return path26;
17355
18453
  }
17356
18454
  };
17357
- var getAbsolutePath = (path25, cwd) => {
17358
- if (sp2.isAbsolute(path25)) {
17359
- return path25;
18455
+ var getAbsolutePath = (path26, cwd) => {
18456
+ if (sp2.isAbsolute(path26)) {
18457
+ return path26;
17360
18458
  }
17361
- return sp2.join(cwd, path25);
18459
+ return sp2.join(cwd, path26);
17362
18460
  };
17363
18461
  var EMPTY_SET = Object.freeze(/* @__PURE__ */ new Set());
17364
18462
  var DirEntry = class {
@@ -17423,10 +18521,10 @@ var WatchHelper = class {
17423
18521
  dirParts;
17424
18522
  followSymlinks;
17425
18523
  statMethod;
17426
- constructor(path25, follow, fsw) {
18524
+ constructor(path26, follow, fsw) {
17427
18525
  this.fsw = fsw;
17428
- const watchPath = path25;
17429
- this.path = path25 = path25.replace(REPLACER_RE, "");
18526
+ const watchPath = path26;
18527
+ this.path = path26 = path26.replace(REPLACER_RE, "");
17430
18528
  this.watchPath = watchPath;
17431
18529
  this.fullWatchPath = sp2.resolve(watchPath);
17432
18530
  this.dirParts = [];
@@ -17566,20 +18664,20 @@ var FSWatcher = class extends EventEmitter2 {
17566
18664
  this._closePromise = void 0;
17567
18665
  let paths = unifyPaths(paths_);
17568
18666
  if (cwd) {
17569
- paths = paths.map((path25) => {
17570
- const absPath = getAbsolutePath(path25, cwd);
18667
+ paths = paths.map((path26) => {
18668
+ const absPath = getAbsolutePath(path26, cwd);
17571
18669
  return absPath;
17572
18670
  });
17573
18671
  }
17574
- paths.forEach((path25) => {
17575
- this._removeIgnoredPath(path25);
18672
+ paths.forEach((path26) => {
18673
+ this._removeIgnoredPath(path26);
17576
18674
  });
17577
18675
  this._userIgnored = void 0;
17578
18676
  if (!this._readyCount)
17579
18677
  this._readyCount = 0;
17580
18678
  this._readyCount += paths.length;
17581
- Promise.all(paths.map(async (path25) => {
17582
- const res = await this._nodeFsHandler._addToNodeFs(path25, !_internal, void 0, 0, _origAdd);
18679
+ Promise.all(paths.map(async (path26) => {
18680
+ const res = await this._nodeFsHandler._addToNodeFs(path26, !_internal, void 0, 0, _origAdd);
17583
18681
  if (res)
17584
18682
  this._emitReady();
17585
18683
  return res;
@@ -17601,17 +18699,17 @@ var FSWatcher = class extends EventEmitter2 {
17601
18699
  return this;
17602
18700
  const paths = unifyPaths(paths_);
17603
18701
  const { cwd } = this.options;
17604
- paths.forEach((path25) => {
17605
- if (!sp2.isAbsolute(path25) && !this._closers.has(path25)) {
18702
+ paths.forEach((path26) => {
18703
+ if (!sp2.isAbsolute(path26) && !this._closers.has(path26)) {
17606
18704
  if (cwd)
17607
- path25 = sp2.join(cwd, path25);
17608
- path25 = sp2.resolve(path25);
18705
+ path26 = sp2.join(cwd, path26);
18706
+ path26 = sp2.resolve(path26);
17609
18707
  }
17610
- this._closePath(path25);
17611
- this._addIgnoredPath(path25);
17612
- if (this._watched.has(path25)) {
18708
+ this._closePath(path26);
18709
+ this._addIgnoredPath(path26);
18710
+ if (this._watched.has(path26)) {
17613
18711
  this._addIgnoredPath({
17614
- path: path25,
18712
+ path: path26,
17615
18713
  recursive: true
17616
18714
  });
17617
18715
  }
@@ -17675,38 +18773,38 @@ var FSWatcher = class extends EventEmitter2 {
17675
18773
  * @param stats arguments to be passed with event
17676
18774
  * @returns the error if defined, otherwise the value of the FSWatcher instance's `closed` flag
17677
18775
  */
17678
- async _emit(event, path25, stats) {
18776
+ async _emit(event, path26, stats) {
17679
18777
  if (this.closed)
17680
18778
  return;
17681
18779
  const opts = this.options;
17682
18780
  if (isWindows)
17683
- path25 = sp2.normalize(path25);
18781
+ path26 = sp2.normalize(path26);
17684
18782
  if (opts.cwd)
17685
- path25 = sp2.relative(opts.cwd, path25);
17686
- const args = [path25];
18783
+ path26 = sp2.relative(opts.cwd, path26);
18784
+ const args = [path26];
17687
18785
  if (stats != null)
17688
18786
  args.push(stats);
17689
18787
  const awf = opts.awaitWriteFinish;
17690
18788
  let pw;
17691
- if (awf && (pw = this._pendingWrites.get(path25))) {
18789
+ if (awf && (pw = this._pendingWrites.get(path26))) {
17692
18790
  pw.lastChange = /* @__PURE__ */ new Date();
17693
18791
  return this;
17694
18792
  }
17695
18793
  if (opts.atomic) {
17696
18794
  if (event === EVENTS.UNLINK) {
17697
- this._pendingUnlinks.set(path25, [event, ...args]);
18795
+ this._pendingUnlinks.set(path26, [event, ...args]);
17698
18796
  setTimeout(() => {
17699
- this._pendingUnlinks.forEach((entry, path26) => {
18797
+ this._pendingUnlinks.forEach((entry, path27) => {
17700
18798
  this.emit(...entry);
17701
18799
  this.emit(EVENTS.ALL, ...entry);
17702
- this._pendingUnlinks.delete(path26);
18800
+ this._pendingUnlinks.delete(path27);
17703
18801
  });
17704
18802
  }, typeof opts.atomic === "number" ? opts.atomic : 100);
17705
18803
  return this;
17706
18804
  }
17707
- if (event === EVENTS.ADD && this._pendingUnlinks.has(path25)) {
18805
+ if (event === EVENTS.ADD && this._pendingUnlinks.has(path26)) {
17708
18806
  event = EVENTS.CHANGE;
17709
- this._pendingUnlinks.delete(path25);
18807
+ this._pendingUnlinks.delete(path26);
17710
18808
  }
17711
18809
  }
17712
18810
  if (awf && (event === EVENTS.ADD || event === EVENTS.CHANGE) && this._readyEmitted) {
@@ -17724,16 +18822,16 @@ var FSWatcher = class extends EventEmitter2 {
17724
18822
  this.emitWithAll(event, args);
17725
18823
  }
17726
18824
  };
17727
- this._awaitWriteFinish(path25, awf.stabilityThreshold, event, awfEmit);
18825
+ this._awaitWriteFinish(path26, awf.stabilityThreshold, event, awfEmit);
17728
18826
  return this;
17729
18827
  }
17730
18828
  if (event === EVENTS.CHANGE) {
17731
- const isThrottled = !this._throttle(EVENTS.CHANGE, path25, 50);
18829
+ const isThrottled = !this._throttle(EVENTS.CHANGE, path26, 50);
17732
18830
  if (isThrottled)
17733
18831
  return this;
17734
18832
  }
17735
18833
  if (opts.alwaysStat && stats === void 0 && (event === EVENTS.ADD || event === EVENTS.ADD_DIR || event === EVENTS.CHANGE)) {
17736
- const fullPath = opts.cwd ? sp2.join(opts.cwd, path25) : path25;
18834
+ const fullPath = opts.cwd ? sp2.join(opts.cwd, path26) : path26;
17737
18835
  let stats2;
17738
18836
  try {
17739
18837
  stats2 = await stat3(fullPath);
@@ -17764,23 +18862,23 @@ var FSWatcher = class extends EventEmitter2 {
17764
18862
  * @param timeout duration of time to suppress duplicate actions
17765
18863
  * @returns tracking object or false if action should be suppressed
17766
18864
  */
17767
- _throttle(actionType, path25, timeout) {
18865
+ _throttle(actionType, path26, timeout) {
17768
18866
  if (!this._throttled.has(actionType)) {
17769
18867
  this._throttled.set(actionType, /* @__PURE__ */ new Map());
17770
18868
  }
17771
18869
  const action = this._throttled.get(actionType);
17772
18870
  if (!action)
17773
18871
  throw new Error("invalid throttle");
17774
- const actionPath = action.get(path25);
18872
+ const actionPath = action.get(path26);
17775
18873
  if (actionPath) {
17776
18874
  actionPath.count++;
17777
18875
  return false;
17778
18876
  }
17779
18877
  let timeoutObject;
17780
18878
  const clear = () => {
17781
- const item = action.get(path25);
18879
+ const item = action.get(path26);
17782
18880
  const count = item ? item.count : 0;
17783
- action.delete(path25);
18881
+ action.delete(path26);
17784
18882
  clearTimeout(timeoutObject);
17785
18883
  if (item)
17786
18884
  clearTimeout(item.timeoutObject);
@@ -17788,7 +18886,7 @@ var FSWatcher = class extends EventEmitter2 {
17788
18886
  };
17789
18887
  timeoutObject = setTimeout(clear, timeout);
17790
18888
  const thr = { timeoutObject, clear, count: 0 };
17791
- action.set(path25, thr);
18889
+ action.set(path26, thr);
17792
18890
  return thr;
17793
18891
  }
17794
18892
  _incrReadyCount() {
@@ -17802,44 +18900,44 @@ var FSWatcher = class extends EventEmitter2 {
17802
18900
  * @param event
17803
18901
  * @param awfEmit Callback to be called when ready for event to be emitted.
17804
18902
  */
17805
- _awaitWriteFinish(path25, threshold, event, awfEmit) {
18903
+ _awaitWriteFinish(path26, threshold, event, awfEmit) {
17806
18904
  const awf = this.options.awaitWriteFinish;
17807
18905
  if (typeof awf !== "object")
17808
18906
  return;
17809
18907
  const pollInterval = awf.pollInterval;
17810
18908
  let timeoutHandler;
17811
- let fullPath = path25;
17812
- if (this.options.cwd && !sp2.isAbsolute(path25)) {
17813
- fullPath = sp2.join(this.options.cwd, path25);
18909
+ let fullPath = path26;
18910
+ if (this.options.cwd && !sp2.isAbsolute(path26)) {
18911
+ fullPath = sp2.join(this.options.cwd, path26);
17814
18912
  }
17815
18913
  const now2 = /* @__PURE__ */ new Date();
17816
18914
  const writes = this._pendingWrites;
17817
18915
  function awaitWriteFinishFn(prevStat) {
17818
18916
  statcb(fullPath, (err, curStat) => {
17819
- if (err || !writes.has(path25)) {
18917
+ if (err || !writes.has(path26)) {
17820
18918
  if (err && err.code !== "ENOENT")
17821
18919
  awfEmit(err);
17822
18920
  return;
17823
18921
  }
17824
18922
  const now3 = Number(/* @__PURE__ */ new Date());
17825
18923
  if (prevStat && curStat.size !== prevStat.size) {
17826
- writes.get(path25).lastChange = now3;
18924
+ writes.get(path26).lastChange = now3;
17827
18925
  }
17828
- const pw = writes.get(path25);
18926
+ const pw = writes.get(path26);
17829
18927
  const df = now3 - pw.lastChange;
17830
18928
  if (df >= threshold) {
17831
- writes.delete(path25);
18929
+ writes.delete(path26);
17832
18930
  awfEmit(void 0, curStat);
17833
18931
  } else {
17834
18932
  timeoutHandler = setTimeout(awaitWriteFinishFn, pollInterval, curStat);
17835
18933
  }
17836
18934
  });
17837
18935
  }
17838
- if (!writes.has(path25)) {
17839
- writes.set(path25, {
18936
+ if (!writes.has(path26)) {
18937
+ writes.set(path26, {
17840
18938
  lastChange: now2,
17841
18939
  cancelWait: () => {
17842
- writes.delete(path25);
18940
+ writes.delete(path26);
17843
18941
  clearTimeout(timeoutHandler);
17844
18942
  return event;
17845
18943
  }
@@ -17850,8 +18948,8 @@ var FSWatcher = class extends EventEmitter2 {
17850
18948
  /**
17851
18949
  * Determines whether user has asked to ignore this path.
17852
18950
  */
17853
- _isIgnored(path25, stats) {
17854
- if (this.options.atomic && DOT_RE.test(path25))
18951
+ _isIgnored(path26, stats) {
18952
+ if (this.options.atomic && DOT_RE.test(path26))
17855
18953
  return true;
17856
18954
  if (!this._userIgnored) {
17857
18955
  const { cwd } = this.options;
@@ -17861,17 +18959,17 @@ var FSWatcher = class extends EventEmitter2 {
17861
18959
  const list = [...ignoredPaths.map(normalizeIgnored(cwd)), ...ignored];
17862
18960
  this._userIgnored = anymatch(list, void 0);
17863
18961
  }
17864
- return this._userIgnored(path25, stats);
18962
+ return this._userIgnored(path26, stats);
17865
18963
  }
17866
- _isntIgnored(path25, stat5) {
17867
- return !this._isIgnored(path25, stat5);
18964
+ _isntIgnored(path26, stat5) {
18965
+ return !this._isIgnored(path26, stat5);
17868
18966
  }
17869
18967
  /**
17870
18968
  * Provides a set of common helpers and properties relating to symlink handling.
17871
18969
  * @param path file or directory pattern being watched
17872
18970
  */
17873
- _getWatchHelpers(path25) {
17874
- return new WatchHelper(path25, this.options.followSymlinks, this);
18971
+ _getWatchHelpers(path26) {
18972
+ return new WatchHelper(path26, this.options.followSymlinks, this);
17875
18973
  }
17876
18974
  // Directory helpers
17877
18975
  // -----------------
@@ -17903,63 +19001,63 @@ var FSWatcher = class extends EventEmitter2 {
17903
19001
  * @param item base path of item/directory
17904
19002
  */
17905
19003
  _remove(directory, item, isDirectory) {
17906
- const path25 = sp2.join(directory, item);
17907
- const fullPath = sp2.resolve(path25);
17908
- isDirectory = isDirectory != null ? isDirectory : this._watched.has(path25) || this._watched.has(fullPath);
17909
- if (!this._throttle("remove", path25, 100))
19004
+ const path26 = sp2.join(directory, item);
19005
+ const fullPath = sp2.resolve(path26);
19006
+ isDirectory = isDirectory != null ? isDirectory : this._watched.has(path26) || this._watched.has(fullPath);
19007
+ if (!this._throttle("remove", path26, 100))
17910
19008
  return;
17911
19009
  if (!isDirectory && this._watched.size === 1) {
17912
19010
  this.add(directory, item, true);
17913
19011
  }
17914
- const wp = this._getWatchedDir(path25);
19012
+ const wp = this._getWatchedDir(path26);
17915
19013
  const nestedDirectoryChildren = wp.getChildren();
17916
- nestedDirectoryChildren.forEach((nested) => this._remove(path25, nested));
19014
+ nestedDirectoryChildren.forEach((nested) => this._remove(path26, nested));
17917
19015
  const parent = this._getWatchedDir(directory);
17918
19016
  const wasTracked = parent.has(item);
17919
19017
  parent.remove(item);
17920
19018
  if (this._symlinkPaths.has(fullPath)) {
17921
19019
  this._symlinkPaths.delete(fullPath);
17922
19020
  }
17923
- let relPath = path25;
19021
+ let relPath = path26;
17924
19022
  if (this.options.cwd)
17925
- relPath = sp2.relative(this.options.cwd, path25);
19023
+ relPath = sp2.relative(this.options.cwd, path26);
17926
19024
  if (this.options.awaitWriteFinish && this._pendingWrites.has(relPath)) {
17927
19025
  const event = this._pendingWrites.get(relPath).cancelWait();
17928
19026
  if (event === EVENTS.ADD)
17929
19027
  return;
17930
19028
  }
17931
- this._watched.delete(path25);
19029
+ this._watched.delete(path26);
17932
19030
  this._watched.delete(fullPath);
17933
19031
  const eventName = isDirectory ? EVENTS.UNLINK_DIR : EVENTS.UNLINK;
17934
- if (wasTracked && !this._isIgnored(path25))
17935
- this._emit(eventName, path25);
17936
- this._closePath(path25);
19032
+ if (wasTracked && !this._isIgnored(path26))
19033
+ this._emit(eventName, path26);
19034
+ this._closePath(path26);
17937
19035
  }
17938
19036
  /**
17939
19037
  * Closes all watchers for a path
17940
19038
  */
17941
- _closePath(path25) {
17942
- this._closeFile(path25);
17943
- const dir = sp2.dirname(path25);
17944
- this._getWatchedDir(dir).remove(sp2.basename(path25));
19039
+ _closePath(path26) {
19040
+ this._closeFile(path26);
19041
+ const dir = sp2.dirname(path26);
19042
+ this._getWatchedDir(dir).remove(sp2.basename(path26));
17945
19043
  }
17946
19044
  /**
17947
19045
  * Closes only file-specific watchers
17948
19046
  */
17949
- _closeFile(path25) {
17950
- const closers = this._closers.get(path25);
19047
+ _closeFile(path26) {
19048
+ const closers = this._closers.get(path26);
17951
19049
  if (!closers)
17952
19050
  return;
17953
19051
  closers.forEach((closer) => closer());
17954
- this._closers.delete(path25);
19052
+ this._closers.delete(path26);
17955
19053
  }
17956
- _addPathCloser(path25, closer) {
19054
+ _addPathCloser(path26, closer) {
17957
19055
  if (!closer)
17958
19056
  return;
17959
- let list = this._closers.get(path25);
19057
+ let list = this._closers.get(path26);
17960
19058
  if (!list) {
17961
19059
  list = [];
17962
- this._closers.set(path25, list);
19060
+ this._closers.set(path26, list);
17963
19061
  }
17964
19062
  list.push(closer);
17965
19063
  }
@@ -17989,11 +19087,11 @@ function watch(paths, options = {}) {
17989
19087
  var chokidar_default = { watch, FSWatcher };
17990
19088
 
17991
19089
  // src/watcher/file-watcher.ts
17992
- import * as path23 from "path";
19090
+ import * as path24 from "path";
17993
19091
 
17994
19092
  // src/watcher/native-recursive-watcher.ts
17995
19093
  import { watch as watch2 } from "fs";
17996
- import * as path21 from "path";
19094
+ import * as path22 from "path";
17997
19095
  var NativeRecursiveWatcher = class {
17998
19096
  constructor(root, onChange, options = {}) {
17999
19097
  this.root = root;
@@ -18041,9 +19139,9 @@ var NativeRecursiveWatcher = class {
18041
19139
  toAbsolutePath(filename) {
18042
19140
  if (filename == null) return null;
18043
19141
  const normalizedFilename = typeof filename === "string" ? filename : filename.toString();
18044
- const absolutePath = path21.resolve(this.root, normalizedFilename);
18045
- const relativePath = path21.relative(this.root, absolutePath);
18046
- const outsideRoot = relativePath === ".." || relativePath.startsWith(`..${path21.sep}`) || path21.isAbsolute(relativePath);
19142
+ const absolutePath = path22.resolve(this.root, normalizedFilename);
19143
+ const relativePath = path22.relative(this.root, absolutePath);
19144
+ const outsideRoot = relativePath === ".." || relativePath.startsWith(`..${path22.sep}`) || path22.isAbsolute(relativePath);
18047
19145
  return outsideRoot ? null : absolutePath;
18048
19146
  }
18049
19147
  defaultWatchFactory = (root, listener, options) => watch2(root, options, listener);
@@ -18051,16 +19149,16 @@ var NativeRecursiveWatcher = class {
18051
19149
 
18052
19150
  // src/watcher/snapshot.ts
18053
19151
  import * as fsPromises4 from "fs/promises";
18054
- import * as path22 from "path";
19152
+ import * as path23 from "path";
18055
19153
  async function buildFileSnapshotScan(projectRoot3, config, configPaths = []) {
18056
- const normalizedProjectRoot = path22.resolve(projectRoot3);
19154
+ const normalizedProjectRoot = path23.resolve(projectRoot3);
18057
19155
  const ignoreFilter = createIgnoreFilter(normalizedProjectRoot);
18058
19156
  const includePatterns = [...config.include, ...config.additionalInclude ?? []];
18059
19157
  const maxDepth = config.indexing?.maxDepth ?? -1;
18060
19158
  const snapshot = /* @__PURE__ */ new Map();
18061
19159
  const unreadablePrefixes = /* @__PURE__ */ new Set();
18062
19160
  const includeFile = async (filePath) => {
18063
- const normalizedPath2 = path22.resolve(filePath);
19161
+ const normalizedPath2 = path23.resolve(filePath);
18064
19162
  if (!shouldIncludeFile(normalizedPath2, normalizedProjectRoot, includePatterns, config.exclude, ignoreFilter)) return;
18065
19163
  const stat5 = await readStatIfFile(normalizedPath2, unreadablePrefixes);
18066
19164
  if (stat5) snapshot.set(normalizedPath2, { size: stat5.size, mtimeMs: stat5.mtimeMs });
@@ -18072,16 +19170,16 @@ async function buildFileSnapshotScan(projectRoot3, config, configPaths = []) {
18072
19170
  } catch (error) {
18073
19171
  if (isMissingFsError(error)) return;
18074
19172
  if (isPermissionFsError(error)) {
18075
- unreadablePrefixes.add(path22.resolve(directoryPath));
19173
+ unreadablePrefixes.add(path23.resolve(directoryPath));
18076
19174
  return;
18077
19175
  }
18078
19176
  throw error;
18079
19177
  }
18080
19178
  for (const entry of entries) {
18081
- const fullPath = path22.join(directoryPath, entry.name);
18082
- const relativePath = path22.relative(normalizedProjectRoot, fullPath);
19179
+ const fullPath = path23.join(directoryPath, entry.name);
19180
+ const relativePath = path23.relative(normalizedProjectRoot, fullPath);
18083
19181
  if (entry.isDirectory()) {
18084
- if (hasFilteredPathSegment(relativePath, path22.sep) || isRestrictedDirectory(relativePath, path22.sep)) continue;
19182
+ if (hasFilteredPathSegment(relativePath, path23.sep) || isRestrictedDirectory(relativePath, path23.sep)) continue;
18085
19183
  if (ignoreFilter.ignores(relativePath)) continue;
18086
19184
  if (maxDepth === -1 || depth < maxDepth) await walk(fullPath, depth + 1);
18087
19185
  } else if (entry.isFile()) {
@@ -18094,19 +19192,19 @@ async function buildFileSnapshotScan(projectRoot3, config, configPaths = []) {
18094
19192
  return { entries: snapshot, unreadablePrefixes };
18095
19193
  }
18096
19194
  async function buildFileSnapshotForPathScan(projectRoot3, config, configPaths, targetPath) {
18097
- const normalizedProjectRoot = path22.resolve(projectRoot3);
18098
- const normalizedTargetPath = path22.resolve(targetPath);
19195
+ const normalizedProjectRoot = path23.resolve(projectRoot3);
19196
+ const normalizedTargetPath = path23.resolve(targetPath);
18099
19197
  if (!isWithinPath(normalizedProjectRoot, normalizedTargetPath)) {
18100
19198
  return { entries: /* @__PURE__ */ new Map(), unreadablePrefixes: /* @__PURE__ */ new Set() };
18101
19199
  }
18102
19200
  const ignoreFilter = createIgnoreFilter(normalizedProjectRoot);
18103
19201
  const includePatterns = [...config.include, ...config.additionalInclude ?? []];
18104
19202
  const maxDepth = config.indexing?.maxDepth ?? -1;
18105
- const explicitConfigPaths = new Set(configPaths.map((configPath) => path22.resolve(configPath)));
19203
+ const explicitConfigPaths = new Set(configPaths.map((configPath) => path23.resolve(configPath)));
18106
19204
  const snapshot = /* @__PURE__ */ new Map();
18107
19205
  const unreadablePrefixes = /* @__PURE__ */ new Set();
18108
19206
  const includeFile = async (filePath) => {
18109
- const normalizedPath2 = path22.resolve(filePath);
19207
+ const normalizedPath2 = path23.resolve(filePath);
18110
19208
  if (!explicitConfigPaths.has(normalizedPath2) && !shouldIncludeFile(
18111
19209
  normalizedPath2,
18112
19210
  normalizedProjectRoot,
@@ -18124,16 +19222,16 @@ async function buildFileSnapshotForPathScan(projectRoot3, config, configPaths, t
18124
19222
  } catch (error) {
18125
19223
  if (isMissingFsError(error)) return;
18126
19224
  if (isPermissionFsError(error)) {
18127
- unreadablePrefixes.add(path22.resolve(directoryPath));
19225
+ unreadablePrefixes.add(path23.resolve(directoryPath));
18128
19226
  return;
18129
19227
  }
18130
19228
  throw error;
18131
19229
  }
18132
19230
  for (const entry of entries) {
18133
- const fullPath = path22.join(directoryPath, entry.name);
18134
- const relativePath = path22.relative(normalizedProjectRoot, fullPath);
19231
+ const fullPath = path23.join(directoryPath, entry.name);
19232
+ const relativePath = path23.relative(normalizedProjectRoot, fullPath);
18135
19233
  if (entry.isDirectory()) {
18136
- if (hasFilteredPathSegment(relativePath, path22.sep) || isRestrictedDirectory(relativePath, path22.sep)) continue;
19234
+ if (hasFilteredPathSegment(relativePath, path23.sep) || isRestrictedDirectory(relativePath, path23.sep)) continue;
18137
19235
  if (ignoreFilter.ignores(relativePath)) continue;
18138
19236
  if (maxDepth === -1 || depth < maxDepth) await walk(fullPath, depth + 1);
18139
19237
  } else if (entry.isFile()) {
@@ -18157,7 +19255,7 @@ function completeFileSnapshot(previous, scan) {
18157
19255
  return completed;
18158
19256
  }
18159
19257
  async function includeExplicitConfigPaths(snapshot, unreadablePrefixes, configPaths) {
18160
- for (const configPath of [...new Set(configPaths.map((value) => path22.resolve(value)))]) {
19258
+ for (const configPath of [...new Set(configPaths.map((value) => path23.resolve(value)))]) {
18161
19259
  if (snapshot.has(configPath)) continue;
18162
19260
  const stat5 = await readStatIfFile(configPath, unreadablePrefixes);
18163
19261
  if (stat5) snapshot.set(configPath, { size: stat5.size, mtimeMs: stat5.mtimeMs });
@@ -18167,12 +19265,12 @@ async function includeExplicitConfigPathsInPath(snapshot, unreadablePrefixes, co
18167
19265
  await includeExplicitConfigPaths(
18168
19266
  snapshot,
18169
19267
  unreadablePrefixes,
18170
- configPaths.filter((configPath) => isWithinPath(targetPath, path22.resolve(configPath)))
19268
+ configPaths.filter((configPath) => isWithinPath(targetPath, path23.resolve(configPath)))
18171
19269
  );
18172
19270
  }
18173
19271
  function isWithinPath(parentPath, childPath) {
18174
- const relativePath = path22.relative(parentPath, childPath);
18175
- return relativePath === "" || !relativePath.startsWith(`..${path22.sep}`) && relativePath !== ".." && !path22.isAbsolute(relativePath);
19272
+ const relativePath = path23.relative(parentPath, childPath);
19273
+ return relativePath === "" || !relativePath.startsWith(`..${path23.sep}`) && relativePath !== ".." && !path23.isAbsolute(relativePath);
18176
19274
  }
18177
19275
  async function readStatIfFile(filePath, unreadablePrefixes) {
18178
19276
  try {
@@ -18181,7 +19279,7 @@ async function readStatIfFile(filePath, unreadablePrefixes) {
18181
19279
  } catch (error) {
18182
19280
  if (isMissingFsError(error)) return null;
18183
19281
  if (isPermissionFsError(error)) {
18184
- unreadablePrefixes.add(path22.resolve(filePath));
19282
+ unreadablePrefixes.add(path23.resolve(filePath));
18185
19283
  return null;
18186
19284
  }
18187
19285
  throw error;
@@ -18318,8 +19416,8 @@ var FileWatcher = class {
18318
19416
  this.createWatcher();
18319
19417
  }
18320
19418
  resetReady() {
18321
- this.readyPromise = new Promise((resolve17) => {
18322
- this.resolveReady = resolve17;
19419
+ this.readyPromise = new Promise((resolve18) => {
19420
+ this.resolveReady = resolve18;
18323
19421
  });
18324
19422
  this.startupReadySignals = 1;
18325
19423
  }
@@ -18350,7 +19448,7 @@ var FileWatcher = class {
18350
19448
  const resolvedWatchTargets = watchTargets ?? this.getFullChokidarWatchTargets();
18351
19449
  const watcherOptions = {
18352
19450
  ignored: (filePath) => {
18353
- const relativePath = path23.relative(this.projectRoot, filePath);
19451
+ const relativePath = path24.relative(this.projectRoot, filePath);
18354
19452
  if (!relativePath) return false;
18355
19453
  if (this.isProjectConfigPathOrAncestor(relativePath)) {
18356
19454
  return false;
@@ -18358,10 +19456,10 @@ var FileWatcher = class {
18358
19456
  if (this.isOutsideProjectPath(relativePath)) {
18359
19457
  return true;
18360
19458
  }
18361
- if (hasFilteredPathSegment(relativePath, path23.sep)) {
19459
+ if (hasFilteredPathSegment(relativePath, path24.sep)) {
18362
19460
  return true;
18363
19461
  }
18364
- if (isRestrictedDirectory(relativePath, path23.sep)) {
19462
+ if (isRestrictedDirectory(relativePath, path24.sep)) {
18365
19463
  return true;
18366
19464
  }
18367
19465
  if (ignoreFilter.ignores(relativePath)) {
@@ -18452,13 +19550,13 @@ var FileWatcher = class {
18452
19550
  getExternalConfigWatchTargets() {
18453
19551
  return [...new Set(
18454
19552
  this.projectConfigPaths.filter((projectConfigPath) => {
18455
- const relativeConfigPath = path23.relative(this.projectRoot, projectConfigPath);
19553
+ const relativeConfigPath = path24.relative(this.projectRoot, projectConfigPath);
18456
19554
  return this.isOutsideProjectPath(relativeConfigPath);
18457
19555
  }).map((projectConfigPath) => {
18458
- if (existsSync13(projectConfigPath)) {
19556
+ if (existsSync14(projectConfigPath)) {
18459
19557
  return projectConfigPath;
18460
19558
  }
18461
- return this.getNearestExistingDirectory(path23.dirname(projectConfigPath));
19559
+ return this.getNearestExistingDirectory(path24.dirname(projectConfigPath));
18462
19560
  })
18463
19561
  )];
18464
19562
  }
@@ -18520,7 +19618,7 @@ var FileWatcher = class {
18520
19618
  }
18521
19619
  scheduleNativeReconciliation(generation, filePath) {
18522
19620
  if (!this.isCurrentNativeSetup(generation)) return;
18523
- const requiresFullReconciliation = filePath === path23.join(this.projectRoot, ".gitignore");
19621
+ const requiresFullReconciliation = filePath === path24.join(this.projectRoot, ".gitignore");
18524
19622
  const invalidatedPath = requiresFullReconciliation ? null : filePath;
18525
19623
  this.nativeInvalidatedPaths.set(invalidatedPath, invalidatedPath !== null);
18526
19624
  if (this.nativeReconcileTimer) {
@@ -18615,23 +19713,23 @@ var FileWatcher = class {
18615
19713
  this.scheduleFlush();
18616
19714
  }
18617
19715
  isProjectConfigPath(filePath) {
18618
- const relativePath = path23.relative(this.projectRoot, filePath);
18619
- const normalizedRelativePath = path23.normalize(relativePath);
19716
+ const relativePath = path24.relative(this.projectRoot, filePath);
19717
+ const normalizedRelativePath = path24.normalize(relativePath);
18620
19718
  return this.getProjectConfigRelativePaths().some((configPath) => configPath === normalizedRelativePath);
18621
19719
  }
18622
19720
  isProjectConfigPathOrAncestor(relativePath) {
18623
- const normalizedRelativePath = path23.normalize(relativePath);
19721
+ const normalizedRelativePath = path24.normalize(relativePath);
18624
19722
  return this.getProjectConfigRelativePaths().some(
18625
- (configPath) => configPath === normalizedRelativePath || configPath.startsWith(`${normalizedRelativePath}${path23.sep}`)
19723
+ (configPath) => configPath === normalizedRelativePath || configPath.startsWith(`${normalizedRelativePath}${path24.sep}`)
18626
19724
  );
18627
19725
  }
18628
19726
  isOutsideProjectPath(relativePath) {
18629
- return relativePath === ".." || relativePath.startsWith(`..${path23.sep}`) || path23.isAbsolute(relativePath);
19727
+ return relativePath === ".." || relativePath.startsWith(`..${path24.sep}`) || path24.isAbsolute(relativePath);
18630
19728
  }
18631
19729
  getNearestExistingDirectory(directoryPath) {
18632
19730
  let candidate = directoryPath;
18633
- while (!existsSync13(candidate)) {
18634
- const parent = path23.dirname(candidate);
19731
+ while (!existsSync14(candidate)) {
19732
+ const parent = path24.dirname(candidate);
18635
19733
  if (parent === candidate) break;
18636
19734
  candidate = parent;
18637
19735
  }
@@ -18639,7 +19737,7 @@ var FileWatcher = class {
18639
19737
  }
18640
19738
  getProjectConfigRelativePaths() {
18641
19739
  return this.projectConfigPaths.map(
18642
- (configPath) => path23.normalize(path23.relative(this.projectRoot, configPath))
19740
+ (configPath) => path24.normalize(path24.relative(this.projectRoot, configPath))
18643
19741
  );
18644
19742
  }
18645
19743
  getConfigPathStates() {
@@ -18697,7 +19795,7 @@ var FileWatcher = class {
18697
19795
  return;
18698
19796
  }
18699
19797
  const changes = Array.from(this.pendingChanges.entries()).map(
18700
- ([path25, type]) => ({ path: path25, type })
19798
+ ([path26, type]) => ({ path: path26, type })
18701
19799
  );
18702
19800
  this.pendingChanges.clear();
18703
19801
  try {
@@ -18743,7 +19841,7 @@ var FileWatcher = class {
18743
19841
  };
18744
19842
 
18745
19843
  // src/watcher/git-head-watcher.ts
18746
- import * as path24 from "path";
19844
+ import * as path25 from "path";
18747
19845
  var GitHeadWatcher = class {
18748
19846
  watcher = null;
18749
19847
  projectRoot;
@@ -18765,13 +19863,13 @@ var GitHeadWatcher = class {
18765
19863
  this.readyPromise = Promise.resolve();
18766
19864
  return;
18767
19865
  }
18768
- this.readyPromise = new Promise((resolve17) => {
18769
- this.resolveReady = resolve17;
19866
+ this.readyPromise = new Promise((resolve18) => {
19867
+ this.resolveReady = resolve18;
18770
19868
  });
18771
19869
  this.onBranchChange = handler;
18772
19870
  this.currentBranch = getCurrentBranch(this.projectRoot);
18773
19871
  const headPath = getHeadPath(this.projectRoot);
18774
- const refsPath = path24.join(this.projectRoot, ".git", "refs", "heads");
19872
+ const refsPath = path25.join(this.projectRoot, ".git", "refs", "heads");
18775
19873
  this.watcher = chokidar_default.watch([headPath, refsPath], {
18776
19874
  persistent: true,
18777
19875
  ignoreInitial: true,
@@ -18839,7 +19937,9 @@ var GitHeadWatcher = class {
18839
19937
  function createWatcherWithIndexer(getIndexer, projectRoot3, config, host, options = {}) {
18840
19938
  const fileWatcher = new FileWatcher(projectRoot3, config, host, options);
18841
19939
  const configPaths = getConfigPaths(projectRoot3, host, options);
18842
- configureAutoIndex(projectRoot3, host, parseConfig(config), getIndexer);
19940
+ configureAutoIndex(projectRoot3, host, parseConfig(config), getIndexer, {
19941
+ synchronizeBackgroundWorker: false
19942
+ });
18843
19943
  let stopped = false;
18844
19944
  const requestReindex = () => {
18845
19945
  if (stopped) return;
@@ -18859,7 +19959,9 @@ function createWatcherWithIndexer(getIndexer, projectRoot3, config, host, option
18859
19959
  const parsedConfig = options.configPath ? parseConfig(loadConfigFile(options.configPath)) : void 0;
18860
19960
  const refreshedConfig = refreshIndexerForDirectory(projectRoot3, host, parsedConfig);
18861
19961
  if (refreshedConfig) {
18862
- configureAutoIndex(projectRoot3, host, refreshedConfig, getIndexer);
19962
+ configureAutoIndex(projectRoot3, host, refreshedConfig, getIndexer, {
19963
+ synchronizeBackgroundWorker: false
19964
+ });
18863
19965
  }
18864
19966
  }
18865
19967
  requestReindex();
@@ -18907,7 +20009,6 @@ function getConfigPaths(projectRoot3, host, options) {
18907
20009
 
18908
20010
  // src/adapters/pi/extension.ts
18909
20011
  var HOST2 = "pi";
18910
- var activeWatchers = /* @__PURE__ */ new Map();
18911
20012
  var ChunkType = Type2.Union([
18912
20013
  Type2.Literal("function"),
18913
20014
  Type2.Literal("class"),
@@ -18926,24 +20027,30 @@ function projectRoot2(ctx) {
18926
20027
  function isValidProject(projectRoot3, requireProjectMarker) {
18927
20028
  return !isHomeDirectory(projectRoot3) && (!requireProjectMarker || hasProjectMarker(projectRoot3));
18928
20029
  }
18929
- function ensureWatcher(projectRoot3) {
18930
- if (activeWatchers.has(projectRoot3)) return;
20030
+ async function ensureWatcher(projectRoot3) {
18931
20031
  const config = parseConfig(loadMergedConfig(projectRoot3, HOST2));
18932
- if (!config.indexing.watchFiles || !isValidProject(projectRoot3, config.indexing.requireProjectMarker)) {
20032
+ if (!isValidProject(projectRoot3, config.indexing.requireProjectMarker)) {
20033
+ await stopBackgroundWorker(projectRoot3, HOST2).catch((error) => {
20034
+ console.error("[codebase-index] Failed to stop Pi background worker:", error);
20035
+ });
18933
20036
  return;
18934
20037
  }
18935
- activeWatchers.set(projectRoot3, createWatcherWithIndexer(
20038
+ getIndexerForProject(projectRoot3, HOST2);
20039
+ const watcherFactoryForConfig = (refreshedConfig) => refreshedConfig.indexing.watchFiles ? () => createWatcherWithIndexer(
18936
20040
  () => getIndexerForProject(projectRoot3, HOST2),
18937
20041
  projectRoot3,
18938
- config,
20042
+ refreshedConfig,
18939
20043
  HOST2
18940
- ));
18941
- }
18942
- async function stopWatcher(projectRoot3) {
18943
- const watcher = activeWatchers.get(projectRoot3);
18944
- if (!watcher) return;
18945
- activeWatchers.delete(projectRoot3);
18946
- await watcher.stop();
20044
+ ) : null;
20045
+ configureBackgroundWorker(projectRoot3, HOST2, config, {
20046
+ startAutoIndex: (source, allowDisabledAutoIndex) => {
20047
+ startAutoIndexForBackgroundWorker(projectRoot3, HOST2, source, allowDisabledAutoIndex);
20048
+ },
20049
+ stopAutoIndex: () => stopAutoIndexForBackgroundWorker(projectRoot3, HOST2),
20050
+ watcherFactory: watcherFactoryForConfig(config),
20051
+ watcherFactoryForConfig
20052
+ });
20053
+ await waitForBackgroundWorkerStart(projectRoot3, HOST2);
18947
20054
  }
18948
20055
  function codebaseIndexPiExtension(pi) {
18949
20056
  pi.registerTool({
@@ -19103,12 +20210,14 @@ function codebaseIndexPiExtension(pi) {
19103
20210
  parameters: Type2.Object({
19104
20211
  force: Type2.Optional(Type2.Boolean({ default: false })),
19105
20212
  estimateOnly: Type2.Optional(Type2.Boolean({ default: false })),
20213
+ dryRun: Type2.Optional(Type2.Boolean({ default: false })),
19106
20214
  verbose: Type2.Optional(Type2.Boolean({ default: false }))
19107
20215
  }),
19108
20216
  async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
19109
20217
  try {
19110
20218
  const result = await runIndexCodebase(projectRoot2(ctx), HOST2, params);
19111
20219
  if (result.kind === "estimate") return text2(formatCostEstimate(result.estimate), result.estimate);
20220
+ if (result.kind === "dryrun") return text2(formatDryRunEstimate(result.dryrun), result.dryrun);
19112
20221
  if (result.kind === "busy") return text2(result.text, { code: "INDEX_BUSY" });
19113
20222
  if (result.kind === "message") return text2(result.text);
19114
20223
  return text2(formatIndexStats(result.stats, params.verbose ?? false), result.stats);
@@ -19174,16 +20283,16 @@ function codebaseIndexPiExtension(pi) {
19174
20283
  });
19175
20284
  registerPiCallGraphTools(pi);
19176
20285
  pi.on("before_agent_start", async (event, ctx) => {
19177
- ensureWatcher(projectRoot2(ctx));
20286
+ await ensureWatcher(projectRoot2(ctx));
19178
20287
  return {
19179
20288
  systemPrompt: `${event.systemPrompt}
19180
20289
 
19181
- 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.`
20290
+ 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.`
19182
20291
  };
19183
20292
  });
19184
20293
  pi.on("session_shutdown", async (_event, ctx) => {
19185
20294
  const root = projectRoot2(ctx);
19186
- await Promise.all([stopWatcher(root), stopAutoIndex(root, HOST2)]);
20295
+ await stopBackgroundWorker(root, HOST2);
19187
20296
  });
19188
20297
  pi.registerTool({
19189
20298
  name: TOOL_NAME.PR_IMPACT,