opencode-codebase-index 0.25.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.
@@ -2241,8 +2241,8 @@ function formatCodeCommunities(result) {
2241
2241
  }
2242
2242
 
2243
2243
  // src/tools/operations.ts
2244
- import { existsSync as existsSync12, realpathSync as realpathSync5, statSync as statSync5 } from "fs";
2245
- import * as path20 from "path";
2244
+ import { existsSync as existsSync13, realpathSync as realpathSync6, statSync as statSync5 } from "fs";
2245
+ import * as path21 from "path";
2246
2246
 
2247
2247
  // src/tools/knowledge-base-paths.ts
2248
2248
  import * as path9 from "path";
@@ -2954,8 +2954,8 @@ function formatExactSearchHandoff(results) {
2954
2954
  }
2955
2955
  function formatContextEvidence(result, index) {
2956
2956
  const symbol = result.name ? ` ${JSON.stringify(compactEvidenceValue(result.name, 80))}` : "";
2957
- const path25 = compactEvidenceValue(result.filePath, 120);
2958
- 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)})`;
2959
2959
  }
2960
2960
  function formatContextPack(heading, selected, candidateCount, duplicateCount, limitOmittedCount, budgetOmittedCount, includeExactSearchHandoff) {
2961
2961
  const lines = selected.map((result, index) => formatContextEvidence(result, index + 1));
@@ -3597,9 +3597,9 @@ function formatEffectivenessMetrics(snapshot) {
3597
3597
  }
3598
3598
 
3599
3599
  // src/utils/auto-index.ts
3600
- import { existsSync as existsSync8, realpathSync as realpathSync3 } from "fs";
3601
- import * as os4 from "os";
3602
- 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";
3603
3603
 
3604
3604
  // src/indexer/index-lock.ts
3605
3605
  import { randomUUID } from "crypto";
@@ -3856,7 +3856,7 @@ function removeDeadReclaimMarker(lockPath, expectedOwner) {
3856
3856
  return true;
3857
3857
  }
3858
3858
  function reclaimDeadOwner(indexPath, lockPath, expectedOwner) {
3859
- const reclaimPath = path10.join(lockPath, RECLAIM_DIRECTORY_NAME);
3859
+ const reclaimPath2 = path10.join(lockPath, RECLAIM_DIRECTORY_NAME);
3860
3860
  const reclaimOwner = {
3861
3861
  pid: process.pid,
3862
3862
  hostname: os3.hostname(),
@@ -3865,19 +3865,19 @@ function reclaimDeadOwner(indexPath, lockPath, expectedOwner) {
3865
3865
  expectedOwnerToken: expectedOwner.token
3866
3866
  };
3867
3867
  for (let attempt = 0; attempt < 2; attempt += 1) {
3868
- if (publishJsonDirectory(reclaimPath, reclaimOwner)) break;
3868
+ if (publishJsonDirectory(reclaimPath2, reclaimOwner)) break;
3869
3869
  if (attempt === 0 && removeDeadReclaimMarker(lockPath, expectedOwner)) continue;
3870
3870
  return false;
3871
3871
  }
3872
3872
  try {
3873
- const currentReclaimer = readReclaimOwner(reclaimPath);
3873
+ const currentReclaimer = readReclaimOwner(reclaimPath2);
3874
3874
  const currentOwner = readDirectoryOwner(lockPath);
3875
3875
  if (!currentReclaimer || !sameReclaimOwner(currentReclaimer, reclaimOwner) || !currentOwner || !sameOwner(currentOwner, expectedOwner) || getOwnerLiveness(currentOwner) !== "dead") {
3876
3876
  return false;
3877
3877
  }
3878
3878
  publishRecoveryMarker(indexPath, expectedOwner);
3879
3879
  const ownerBeforeQuarantine = readDirectoryOwner(lockPath);
3880
- const reclaimerBeforeQuarantine = readReclaimOwner(reclaimPath);
3880
+ const reclaimerBeforeQuarantine = readReclaimOwner(reclaimPath2);
3881
3881
  if (!ownerBeforeQuarantine || !sameOwner(ownerBeforeQuarantine, expectedOwner) || getOwnerLiveness(ownerBeforeQuarantine) !== "dead" || !reclaimerBeforeQuarantine || !sameReclaimOwner(reclaimerBeforeQuarantine, reclaimOwner)) {
3882
3882
  return false;
3883
3883
  }
@@ -4047,10 +4047,893 @@ function completeLeaseRecovery(lease) {
4047
4047
  }
4048
4048
  }
4049
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
+
4050
4933
  // src/utils/files.ts
4051
4934
  var import_ignore = __toESM(require_ignore(), 1);
4052
- import { existsSync as existsSync7, readFileSync as readFileSync6, promises as fsPromises } from "fs";
4053
- import * as path11 from "path";
4935
+ import { existsSync as existsSync8, readFileSync as readFileSync7, promises as fsPromises } from "fs";
4936
+ import * as path12 from "path";
4054
4937
  var PROJECT_MARKERS = [
4055
4938
  ".git",
4056
4939
  "package.json",
@@ -4068,7 +4951,7 @@ var PROJECT_MARKERS = [
4068
4951
  ];
4069
4952
  function hasProjectMarker(projectRoot3) {
4070
4953
  for (const marker of PROJECT_MARKERS) {
4071
- if (existsSync7(path11.join(projectRoot3, marker))) {
4954
+ if (existsSync8(path12.join(projectRoot3, marker))) {
4072
4955
  return true;
4073
4956
  }
4074
4957
  }
@@ -4095,33 +4978,53 @@ function createIgnoreFilter(projectRoot3) {
4095
4978
  "**/*build*/**"
4096
4979
  ];
4097
4980
  ig.add(defaultIgnores);
4098
- const gitignorePath = path11.join(projectRoot3, ".gitignore");
4099
- if (existsSync7(gitignorePath)) {
4100
- const gitignoreContent = readFileSync6(gitignorePath, "utf-8");
4981
+ const gitignorePath = path12.join(projectRoot3, ".gitignore");
4982
+ if (existsSync8(gitignorePath)) {
4983
+ const gitignoreContent = readFileSync7(gitignorePath, "utf-8");
4101
4984
  ig.add(gitignoreContent);
4102
4985
  }
4103
4986
  return ig;
4104
4987
  }
4105
- function shouldIncludeFile(filePath, projectRoot3, includePatterns, excludePatterns, ignoreFilter) {
4106
- const relativePath = path11.relative(projectRoot3, filePath);
4107
- if (hasFilteredPathSegment(relativePath, path11.sep)) {
4108
- return false;
4109
- }
4110
- if (ignoreFilter.ignores(relativePath)) {
4111
- 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;
4112
5002
  }
4113
5003
  for (const pattern of excludePatterns) {
4114
- if (matchGlob(relativePath, pattern)) {
4115
- return false;
5004
+ const posixPattern = toPosixRelativePath(pattern).replace(/\/+$/, "");
5005
+ if (!posixPattern.endsWith("/**")) {
5006
+ continue;
4116
5007
  }
4117
- }
4118
- for (const pattern of includePatterns) {
4119
- if (matchGlob(relativePath, pattern)) {
5008
+ const directoryPattern = posixPattern.slice(0, -3);
5009
+ if (directoryPattern && matchesAnyGlob(normalized, [directoryPattern])) {
4120
5010
  return true;
4121
5011
  }
4122
5012
  }
4123
5013
  return false;
4124
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
+ }
4125
5028
  function matchGlob(filePath, pattern) {
4126
5029
  if (pattern.startsWith("**/")) {
4127
5030
  const withoutPrefix = pattern.slice(3);
@@ -4142,8 +5045,8 @@ async function* walkDirectory(dir, projectRoot3, includePatterns, excludePattern
4142
5045
  const filesInDir = [];
4143
5046
  const subdirs = [];
4144
5047
  for (const entry of entries) {
4145
- const fullPath = path11.join(dir, entry.name);
4146
- const relativePath = path11.relative(projectRoot3, fullPath);
5048
+ const fullPath = path12.join(dir, entry.name);
5049
+ const relativePath = toPosixRelativePath(path12.relative(projectRoot3, fullPath));
4147
5050
  if (isHiddenPathSegment(entry.name)) {
4148
5051
  if (entry.isDirectory()) {
4149
5052
  skipped.push({ path: relativePath, reason: "excluded" });
@@ -4161,6 +5064,10 @@ async function* walkDirectory(dir, projectRoot3, includePatterns, excludePattern
4161
5064
  continue;
4162
5065
  }
4163
5066
  if (entry.isDirectory()) {
5067
+ if (isExcludedDirectory(relativePath, excludePatterns)) {
5068
+ skipped.push({ path: relativePath, reason: "excluded" });
5069
+ continue;
5070
+ }
4164
5071
  subdirs.push({ fullPath, relativePath });
4165
5072
  } else if (entry.isFile()) {
4166
5073
  const stat5 = await fsPromises.stat(fullPath);
@@ -4168,20 +5075,11 @@ async function* walkDirectory(dir, projectRoot3, includePatterns, excludePattern
4168
5075
  skipped.push({ path: relativePath, reason: "too_large" });
4169
5076
  continue;
4170
5077
  }
4171
- for (const pattern of excludePatterns) {
4172
- if (matchGlob(relativePath, pattern)) {
4173
- skipped.push({ path: relativePath, reason: "excluded" });
4174
- continue;
4175
- }
4176
- }
4177
- let matched = false;
4178
- for (const pattern of includePatterns) {
4179
- if (matchGlob(relativePath, pattern)) {
4180
- matched = true;
4181
- break;
4182
- }
5078
+ if (isExcludedByPatterns(relativePath, excludePatterns)) {
5079
+ skipped.push({ path: relativePath, reason: "excluded" });
5080
+ continue;
4183
5081
  }
4184
- if (matched) {
5082
+ if (matchesAnyGlob(relativePath, includePatterns)) {
4185
5083
  filesInDir.push({ path: fullPath, size: stat5.size });
4186
5084
  }
4187
5085
  }
@@ -4192,7 +5090,7 @@ async function* walkDirectory(dir, projectRoot3, includePatterns, excludePattern
4192
5090
  yield f;
4193
5091
  }
4194
5092
  for (let i = options.maxFilesPerDirectory; i < filesInDir.length; i++) {
4195
- skipped.push({ path: path11.relative(projectRoot3, filesInDir[i].path), reason: "excluded" });
5093
+ skipped.push({ path: toPosixRelativePath(path12.relative(projectRoot3, filesInDir[i].path)), reason: "excluded" });
4196
5094
  }
4197
5095
  const canRecurse = options.maxDepth === -1 || currentDepth < options.maxDepth;
4198
5096
  if (canRecurse) {
@@ -4232,8 +5130,8 @@ async function collectFiles(projectRoot3, includePatterns, excludePatterns, maxF
4232
5130
  if (additionalRoots && additionalRoots.length > 0) {
4233
5131
  const normalizedRoots = /* @__PURE__ */ new Set();
4234
5132
  for (const kbRoot of additionalRoots) {
4235
- const resolved = path11.normalize(
4236
- path11.isAbsolute(kbRoot) ? kbRoot : path11.resolve(projectRoot3, kbRoot)
5133
+ const resolved = path12.normalize(
5134
+ path12.isAbsolute(kbRoot) ? kbRoot : path12.resolve(projectRoot3, kbRoot)
4237
5135
  );
4238
5136
  normalizedRoots.add(resolved);
4239
5137
  }
@@ -4274,7 +5172,7 @@ function getErrorMessage(error) {
4274
5172
  return error instanceof Error ? error.message : String(error);
4275
5173
  }
4276
5174
  function runCommand(file, args, options) {
4277
- return new Promise((resolve17, reject) => {
5175
+ return new Promise((resolve18, reject) => {
4278
5176
  childProcess.execFile(
4279
5177
  file,
4280
5178
  args,
@@ -4284,7 +5182,7 @@ function runCommand(file, args, options) {
4284
5182
  reject(error);
4285
5183
  return;
4286
5184
  }
4287
- resolve17(stdout);
5185
+ resolve18(stdout);
4288
5186
  }
4289
5187
  );
4290
5188
  });
@@ -4376,29 +5274,29 @@ var AutoIndexCancelledError = class extends Error {
4376
5274
  function now() {
4377
5275
  return (/* @__PURE__ */ new Date()).toISOString();
4378
5276
  }
4379
- function canonicalizePath(targetPath) {
4380
- const resolved = path12.resolve(targetPath);
4381
- if (existsSync8(resolved)) {
5277
+ function canonicalizePath2(targetPath) {
5278
+ const resolved = path13.resolve(targetPath);
5279
+ if (existsSync9(resolved)) {
4382
5280
  try {
4383
- return realpathSync3.native(resolved);
5281
+ return realpathSync4.native(resolved);
4384
5282
  } catch {
4385
5283
  return resolved;
4386
5284
  }
4387
5285
  }
4388
- const parent = path12.dirname(resolved);
5286
+ const parent = path13.dirname(resolved);
4389
5287
  if (parent === resolved) return resolved;
4390
- return path12.join(canonicalizePath(parent), path12.basename(resolved));
5288
+ return path13.join(canonicalizePath2(parent), path13.basename(resolved));
4391
5289
  }
4392
5290
  function isHomeDirectory(projectRoot3) {
4393
- return canonicalizePath(projectRoot3) === canonicalizePath(os4.homedir());
5291
+ return canonicalizePath2(projectRoot3) === canonicalizePath2(os5.homedir());
4394
5292
  }
4395
- function projectLookupKey(projectRoot3, host) {
4396
- return `${host}::${canonicalizePath(projectRoot3)}`;
5293
+ function projectLookupKey2(projectRoot3, host) {
5294
+ return `${host}::${canonicalizePath2(projectRoot3)}`;
4397
5295
  }
4398
5296
  function coordinatorKey(projectRoot3, config, host) {
4399
- const canonicalProjectRoot = canonicalizePath(projectRoot3);
5297
+ const canonicalProjectRoot = canonicalizePath2(projectRoot3);
4400
5298
  const indexPath = resolveProjectIndexPath(projectRoot3, config.scope, host);
4401
- return `${canonicalizePath(indexPath)}::${canonicalProjectRoot}`;
5299
+ return `${canonicalizePath2(indexPath)}::${canonicalProjectRoot}`;
4402
5300
  }
4403
5301
  function getProjectSafety(projectRoot3, config) {
4404
5302
  if (isHomeDirectory(projectRoot3)) {
@@ -4429,10 +5327,10 @@ function safeFailureMessage(error) {
4429
5327
  }
4430
5328
  function cancellableDelay(delayMs, signal) {
4431
5329
  if (signal.aborted) return Promise.reject(new AutoIndexCancelledError());
4432
- return new Promise((resolve17, reject) => {
5330
+ return new Promise((resolve18, reject) => {
4433
5331
  const timer = setTimeout(() => {
4434
5332
  signal.removeEventListener("abort", onAbort);
4435
- resolve17();
5333
+ resolve18();
4436
5334
  }, delayMs);
4437
5335
  timer.unref?.();
4438
5336
  const onAbort = () => {
@@ -4444,18 +5342,44 @@ function cancellableDelay(delayMs, signal) {
4444
5342
  }
4445
5343
  function withTimeout(promise, timeoutMs) {
4446
5344
  if (timeoutMs <= 0) return Promise.resolve(void 0);
4447
- return new Promise((resolve17) => {
4448
- const timer = setTimeout(() => resolve17(void 0), timeoutMs);
5345
+ return new Promise((resolve18) => {
5346
+ const timer = setTimeout(() => resolve18(void 0), timeoutMs);
4449
5347
  timer.unref?.();
4450
5348
  void promise.then((value) => {
4451
5349
  clearTimeout(timer);
4452
- resolve17(value);
5350
+ resolve18(value);
4453
5351
  }, () => {
4454
5352
  clearTimeout(timer);
4455
- resolve17(void 0);
5353
+ resolve18(void 0);
4456
5354
  });
4457
5355
  });
4458
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
+ }
4459
5383
  function requestPriority(request) {
4460
5384
  if (request.force) return 4;
4461
5385
  if (request.source === "manual") return 3;
@@ -4466,6 +5390,7 @@ function mergeRequests(current, next) {
4466
5390
  if (!current) return next;
4467
5391
  const preferred = requestPriority(next) > requestPriority(current) ? next : current;
4468
5392
  return {
5393
+ allowDisabledAutoIndex: current.allowDisabledAutoIndex || next.allowDisabledAutoIndex,
4469
5394
  checkFreshness: current.checkFreshness && next.checkFreshness,
4470
5395
  force: current.force || next.force,
4471
5396
  onProgress: next.onProgress ?? current.onProgress,
@@ -4527,11 +5452,11 @@ var AutoIndexCoordinator = class {
4527
5452
  progress: this.status.progress ? { ...this.status.progress } : void 0
4528
5453
  };
4529
5454
  }
4530
- start(source) {
5455
+ start(source, allowDisabledAutoIndex = false) {
4531
5456
  this.refreshSafety();
4532
- if (!this.registration.config.indexing.autoIndex || !this.registration.safeToRun) return null;
5457
+ if (!this.registration.config.indexing.autoIndex && !allowDisabledAutoIndex || !this.registration.safeToRun) return null;
4533
5458
  if (this.status.state === "failed") return this.inFlight;
4534
- return this.request({ checkFreshness: true, force: false, source });
5459
+ return this.request({ allowDisabledAutoIndex, checkFreshness: true, force: false, source });
4535
5460
  }
4536
5461
  request(request) {
4537
5462
  if (this.stopped) {
@@ -4606,13 +5531,15 @@ var AutoIndexCoordinator = class {
4606
5531
  retryAttempt: void 0
4607
5532
  });
4608
5533
  const inFlight = this.inFlight;
4609
- if (inFlight) {
4610
- if (waitForCompletion) {
4611
- await inFlight;
4612
- } else {
4613
- await withTimeout(inFlight, SHUTDOWN_WAIT_MS);
4614
- }
5534
+ const completion = inFlight ? inFlight.then(() => void 0, () => void 0) : Promise.resolve();
5535
+ if (!inFlight) {
5536
+ return { completed: true, completion };
4615
5537
  }
5538
+ if (waitForCompletion) {
5539
+ await completion;
5540
+ return { completed: true, completion };
5541
+ }
5542
+ return { completed: await settlesWithin(completion, SHUTDOWN_WAIT_MS), completion };
4616
5543
  }
4617
5544
  startRequest(request) {
4618
5545
  if (this.stopped || !this.canRun(request)) {
@@ -4801,7 +5728,7 @@ var AutoIndexCoordinator = class {
4801
5728
  if (request.source === "manual" || request.source === "watcher") {
4802
5729
  return true;
4803
5730
  }
4804
- return this.registration.safeToRun && this.registration.config.indexing.autoIndex;
5731
+ return this.registration.safeToRun && (this.registration.config.indexing.autoIndex || request.allowDisabledAutoIndex === true);
4805
5732
  }
4806
5733
  shouldDeferForBattery(request) {
4807
5734
  return this.registration.backgroundIndexingPolicy !== null && (request.source === "startup" || request.source === "watcher");
@@ -4834,17 +5761,17 @@ var AutoIndexCoordinator = class {
4834
5761
  }
4835
5762
  }
4836
5763
  waitForBatteryRetry(delayMs) {
4837
- return new Promise((resolve17) => {
5764
+ return new Promise((resolve18) => {
4838
5765
  const timer = setTimeout(() => {
4839
5766
  if (this.batteryRetryTimer === timer) {
4840
5767
  this.batteryRetryTimer = null;
4841
5768
  this.resolveBatteryRetry = null;
4842
5769
  }
4843
- resolve17();
5770
+ resolve18();
4844
5771
  }, delayMs);
4845
5772
  timer.unref?.();
4846
5773
  this.batteryRetryTimer = timer;
4847
- this.resolveBatteryRetry = resolve17;
5774
+ this.resolveBatteryRetry = resolve18;
4848
5775
  });
4849
5776
  }
4850
5777
  cancelBatteryRetry() {
@@ -4852,9 +5779,9 @@ var AutoIndexCoordinator = class {
4852
5779
  clearTimeout(this.batteryRetryTimer);
4853
5780
  this.batteryRetryTimer = null;
4854
5781
  }
4855
- const resolve17 = this.resolveBatteryRetry;
5782
+ const resolve18 = this.resolveBatteryRetry;
4856
5783
  this.resolveBatteryRetry = null;
4857
- resolve17?.();
5784
+ resolve18?.();
4858
5785
  }
4859
5786
  finishBatteryCheck(batteryCheck) {
4860
5787
  if (this.batteryCheck !== batteryCheck) return;
@@ -4867,12 +5794,25 @@ var AutoIndexCoordinator = class {
4867
5794
  }
4868
5795
  };
4869
5796
  function getCoordinator(projectRoot3, host) {
4870
- const key = coordinatorKeysByProject.get(projectLookupKey(projectRoot3, host));
5797
+ const key = coordinatorKeysByProject.get(projectLookupKey2(projectRoot3, host));
4871
5798
  return key ? coordinators.get(key) ?? null : null;
4872
5799
  }
4873
- function configureAutoIndex(projectRoot3, host, config, getIndexer) {
4874
- 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);
4875
5811
  const safety = getProjectSafety(projectRoot3, config);
5812
+ const synchronizeWorker = options.synchronizeBackgroundWorker ?? true;
5813
+ if (options.preserveManagedWorker === true && isBackgroundWorkerManaged(projectRoot3, host)) {
5814
+ return;
5815
+ }
4876
5816
  const registration = {
4877
5817
  backgroundIndexingPolicy: createBackgroundIndexingPolicy(
4878
5818
  config.indexing.pauseBackgroundIndexingOnBattery
@@ -4890,6 +5830,9 @@ function configureAutoIndex(projectRoot3, host, config, getIndexer) {
4890
5830
  const stopPrevious = previousCoordinator?.stop(true) ?? Promise.resolve();
4891
5831
  const activation = Promise.all([previousBarrier, stopPrevious]).then(() => void 0);
4892
5832
  coordinatorReplacementBarriers.set(projectKey, activation);
5833
+ if (synchronizeWorker) {
5834
+ synchronizeBackgroundWorker(projectRoot3, host, config, safety.safeToRun);
5835
+ }
4893
5836
  coordinators.delete(previousKey);
4894
5837
  const coordinator2 = new AutoIndexCoordinator(registration);
4895
5838
  coordinator2.activateAfter(activation);
@@ -4905,8 +5848,17 @@ function configureAutoIndex(projectRoot3, host, config, getIndexer) {
4905
5848
  coordinator.update(registration);
4906
5849
  }
4907
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;
4908
5857
  }
4909
5858
  function requestBackgroundIndex(projectRoot3, host) {
5859
+ if (isBackgroundWorkerManaged(projectRoot3, host) && !isBackgroundWorkerLeader(projectRoot3, host)) {
5860
+ return null;
5861
+ }
4910
5862
  return getCoordinator(projectRoot3, host)?.request({
4911
5863
  checkFreshness: false,
4912
5864
  force: false,
@@ -4946,15 +5898,23 @@ async function waitForAutoIndexForRetrieval(projectRoot3, host) {
4946
5898
  };
4947
5899
  }
4948
5900
  try {
4949
- 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);
4950
5906
  } catch {
4951
5907
  }
4952
- const job = coordinator.start("retrieval") ?? coordinator.currentJob();
5908
+ const job = startRetrievalRefresh(projectRoot3, host, coordinator);
4953
5909
  if (job) {
4954
5910
  await withTimeout(job, coordinator.getWaitMs());
5911
+ } else if (isBackgroundWorkerManaged(projectRoot3, host)) {
5912
+ await waitForPublishedSnapshot(coordinator, coordinator.getWaitMs());
4955
5913
  }
4956
5914
  try {
4957
- 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);
4958
5918
  } catch {
4959
5919
  }
4960
5920
  const status = coordinator.snapshot();
@@ -4975,21 +5935,52 @@ async function waitForAutoIndexForRetrieval(projectRoot3, host) {
4975
5935
  text: `Automatic indexing is ${status.state}. Retry shortly or call index_status for progress. You can also run index_codebase explicitly.`
4976
5936
  };
4977
5937
  }
4978
- async function stopAutoIndex(projectRoot3, host) {
4979
- 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);
4980
5944
  }
4981
- async function hasReadableCurrentIndex(coordinator) {
5945
+ async function getSearchReadiness(coordinator) {
4982
5946
  const indexer = coordinator.getIndexer();
4983
5947
  if (indexer.getIndexFreshness) {
4984
5948
  const freshness = await indexer.getIndexFreshness();
4985
- 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())));
4986
5978
  }
4987
- return (await indexer.getStatus()).indexed;
4988
5979
  }
4989
5980
 
4990
5981
  // src/tools/config-state.ts
4991
- import { existsSync as existsSync9, mkdirSync as mkdirSync2, writeFileSync as writeFileSync2 } from "fs";
4992
- import * as path13 from "path";
5982
+ import { existsSync as existsSync10, mkdirSync as mkdirSync3, writeFileSync as writeFileSync3 } from "fs";
5983
+ import * as path14 from "path";
4993
5984
  function normalizeKnowledgeBasePaths(config, projectRoot3) {
4994
5985
  const normalized = { ...config };
4995
5986
  if (Array.isArray(normalized.knowledgeBases)) {
@@ -5016,10 +6007,10 @@ function loadEditableConfig(projectRoot3, host) {
5016
6007
  }
5017
6008
  function saveConfig(projectRoot3, config, host) {
5018
6009
  const configPath = getConfigPath(projectRoot3, host);
5019
- const configDir = path13.dirname(configPath);
5020
- const configBaseDir = path13.dirname(configDir);
5021
- if (!existsSync9(configDir)) {
5022
- 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 });
5023
6014
  }
5024
6015
  const serializableConfig = { ...config };
5025
6016
  if (Array.isArray(serializableConfig.knowledgeBases)) {
@@ -5027,12 +6018,12 @@ function saveConfig(projectRoot3, config, host) {
5027
6018
  (kb) => serializeConfigPathValue(kb, configBaseDir)
5028
6019
  );
5029
6020
  }
5030
- writeFileSync2(configPath, JSON.stringify(serializableConfig, null, 2) + "\n", "utf-8");
6021
+ writeFileSync3(configPath, JSON.stringify(serializableConfig, null, 2) + "\n", "utf-8");
5031
6022
  }
5032
6023
 
5033
6024
  // src/indexer/index.ts
5034
- 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";
5035
- 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";
5036
6027
  import { performance as performance2 } from "perf_hooks";
5037
6028
  import { execFile as execFile5 } from "child_process";
5038
6029
  import { promisify as promisify4 } from "util";
@@ -5059,7 +6050,7 @@ function pTimeout(promise, options) {
5059
6050
  } = options;
5060
6051
  let timer;
5061
6052
  let abortHandler;
5062
- const wrappedPromise = new Promise((resolve17, reject) => {
6053
+ const wrappedPromise = new Promise((resolve18, reject) => {
5063
6054
  if (typeof milliseconds !== "number" || Math.sign(milliseconds) !== 1) {
5064
6055
  throw new TypeError(`Expected \`milliseconds\` to be a positive number, got \`${milliseconds}\``);
5065
6056
  }
@@ -5073,7 +6064,7 @@ function pTimeout(promise, options) {
5073
6064
  };
5074
6065
  signal.addEventListener("abort", abortHandler, { once: true });
5075
6066
  }
5076
- promise.then(resolve17, reject);
6067
+ promise.then(resolve18, reject);
5077
6068
  if (milliseconds === Number.POSITIVE_INFINITY) {
5078
6069
  return;
5079
6070
  }
@@ -5081,7 +6072,7 @@ function pTimeout(promise, options) {
5081
6072
  timer = customTimers.setTimeout.call(void 0, () => {
5082
6073
  if (fallback) {
5083
6074
  try {
5084
- resolve17(fallback());
6075
+ resolve18(fallback());
5085
6076
  } catch (error) {
5086
6077
  reject(error);
5087
6078
  }
@@ -5091,7 +6082,7 @@ function pTimeout(promise, options) {
5091
6082
  promise.cancel();
5092
6083
  }
5093
6084
  if (message === false) {
5094
- resolve17();
6085
+ resolve18();
5095
6086
  } else if (message instanceof Error) {
5096
6087
  reject(message);
5097
6088
  } else {
@@ -5493,7 +6484,7 @@ var PQueue = class extends import_index.default {
5493
6484
  // Assign unique ID if not provided
5494
6485
  id: options.id ?? (this.#idAssigner++).toString()
5495
6486
  };
5496
- return new Promise((resolve17, reject) => {
6487
+ return new Promise((resolve18, reject) => {
5497
6488
  const taskSymbol = /* @__PURE__ */ Symbol(`task-${options.id}`);
5498
6489
  let cleanupQueueAbortHandler = () => void 0;
5499
6490
  const run = async () => {
@@ -5533,7 +6524,7 @@ var PQueue = class extends import_index.default {
5533
6524
  })]);
5534
6525
  }
5535
6526
  const result = await operation;
5536
- resolve17(result);
6527
+ resolve18(result);
5537
6528
  this.emit("completed", result);
5538
6529
  } catch (error) {
5539
6530
  reject(error);
@@ -5721,13 +6712,13 @@ var PQueue = class extends import_index.default {
5721
6712
  });
5722
6713
  }
5723
6714
  async #onEvent(event, filter) {
5724
- return new Promise((resolve17) => {
6715
+ return new Promise((resolve18) => {
5725
6716
  const listener = () => {
5726
6717
  if (filter && !filter()) {
5727
6718
  return;
5728
6719
  }
5729
6720
  this.off(event, listener);
5730
- resolve17();
6721
+ resolve18();
5731
6722
  };
5732
6723
  this.on(event, listener);
5733
6724
  });
@@ -6013,7 +7004,7 @@ async function onAttemptFailure({ error, attemptNumber, retriesConsumed, startTi
6013
7004
  const finalDelay = Math.min(delayTime, remainingTime);
6014
7005
  options.signal?.throwIfAborted();
6015
7006
  if (finalDelay > 0) {
6016
- await new Promise((resolve17, reject) => {
7007
+ await new Promise((resolve18, reject) => {
6017
7008
  const onAbort = () => {
6018
7009
  clearTimeout(timeoutToken);
6019
7010
  options.signal?.removeEventListener("abort", onAbort);
@@ -6021,7 +7012,7 @@ async function onAttemptFailure({ error, attemptNumber, retriesConsumed, startTi
6021
7012
  };
6022
7013
  const timeoutToken = setTimeout(() => {
6023
7014
  options.signal?.removeEventListener("abort", onAbort);
6024
- resolve17();
7015
+ resolve18();
6025
7016
  }, finalDelay);
6026
7017
  if (options.unref) {
6027
7018
  timeoutToken.unref?.();
@@ -6139,17 +7130,17 @@ function validateExternalUrl(urlString) {
6139
7130
  if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
6140
7131
  return { valid: false, reason: `Blocked protocol: ${parsed.protocol}` };
6141
7132
  }
6142
- const hostname2 = parsed.hostname.toLowerCase();
6143
- if (BLOCKED_HOSTNAMES.has(hostname2)) {
6144
- 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})` };
6145
7136
  }
6146
7137
  for (const pattern of BLOCKED_METADATA_IPS) {
6147
- if (pattern.test(hostname2)) {
6148
- return { valid: false, reason: `Blocked: cloud metadata IP (${hostname2})` };
7138
+ if (pattern.test(hostname3)) {
7139
+ return { valid: false, reason: `Blocked: cloud metadata IP (${hostname3})` };
6149
7140
  }
6150
7141
  }
6151
- if (/^169\.254\./.test(hostname2)) {
6152
- 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})` };
6153
7144
  }
6154
7145
  return { valid: true };
6155
7146
  }
@@ -7161,8 +8152,8 @@ function extractParamNames(params) {
7161
8152
  }
7162
8153
 
7163
8154
  // src/native/binding.ts
7164
- import * as os5 from "os";
7165
- import * as path14 from "path";
8155
+ import * as os6 from "os";
8156
+ import * as path15 from "path";
7166
8157
  import * as module from "module";
7167
8158
  import { fileURLToPath } from "url";
7168
8159
 
@@ -7198,7 +8189,7 @@ var MCP_BINARY_CURRENT_NAME = CURRENT_PRODUCT.mcpBinary;
7198
8189
  var STABLE_NATIVE_BINARY_NAME = IDENTITY_CATALOG.native.binaryName;
7199
8190
 
7200
8191
  // src/native/binding.ts
7201
- function getNativeBindingFilename(platform2 = os5.platform(), arch2 = os5.arch()) {
8192
+ function getNativeBindingFilename(platform2 = os6.platform(), arch2 = os6.arch()) {
7202
8193
  if (platform2 === "darwin" && arch2 === "arm64") {
7203
8194
  return `${STABLE_NATIVE_BINARY_NAME}.darwin-arm64.node`;
7204
8195
  }
@@ -7216,25 +8207,25 @@ function getNativeBindingFilename(platform2 = os5.platform(), arch2 = os5.arch()
7216
8207
  }
7217
8208
  throw new Error(`Unsupported platform: ${platform2}-${arch2}`);
7218
8209
  }
7219
- function resolveNativeBindingPath(packageRoot, platform2 = os5.platform(), arch2 = os5.arch()) {
7220
- 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));
7221
8212
  }
7222
8213
  function getNativeBinding() {
7223
8214
  let currentDir;
7224
8215
  let requireTarget;
7225
8216
  if (typeof import.meta !== "undefined" && import.meta.url) {
7226
- currentDir = path14.dirname(fileURLToPath(import.meta.url));
8217
+ currentDir = path15.dirname(fileURLToPath(import.meta.url));
7227
8218
  requireTarget = import.meta.url;
7228
8219
  } else if (typeof __dirname !== "undefined") {
7229
8220
  currentDir = __dirname;
7230
8221
  requireTarget = __filename;
7231
8222
  } else {
7232
8223
  currentDir = process.cwd();
7233
- requireTarget = path14.join(currentDir, "index.js");
8224
+ requireTarget = path15.join(currentDir, "index.js");
7234
8225
  }
7235
8226
  const normalizedDir = currentDir.replace(/\\/g, "/");
7236
- const isDevMode = normalizedDir.includes("/src/native") || currentDir.includes(path14.join("src", "native"));
7237
- 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, "..");
7238
8229
  const nativePath = resolveNativeBindingPath(packageRoot);
7239
8230
  const require2 = module.createRequire(requireTarget);
7240
8231
  return require2(nativePath);
@@ -7818,8 +8809,8 @@ var Database = class _Database {
7818
8809
 
7819
8810
  // src/git/branch-materialization.ts
7820
8811
  import { promises as fsPromises2 } from "fs";
7821
- import * as os6 from "os";
7822
- import * as path15 from "path";
8812
+ import * as os7 from "os";
8813
+ import * as path16 from "path";
7823
8814
 
7824
8815
  // src/git/branch-resolution.ts
7825
8816
  import { execFile as execFile2 } from "child_process";
@@ -8138,13 +9129,13 @@ async function isWorktreeRegistered(projectRoot3, worktreePath) {
8138
9129
  return false;
8139
9130
  }
8140
9131
  function isPathWithinRoot(filePath, rootPath) {
8141
- const relative13 = path15.relative(path15.resolve(rootPath), path15.resolve(filePath));
8142
- 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);
8143
9134
  }
8144
9135
  async function pruneExactMissingWorktreeRegistration(projectRoot3, worktreePath) {
8145
9136
  if (await pathExists(worktreePath)) return false;
8146
9137
  const commonDir = await runGit(projectRoot3, ["rev-parse", "--path-format=absolute", "--git-common-dir"]);
8147
- const registrationsRoot = path15.join(commonDir, "worktrees");
9138
+ const registrationsRoot = path16.join(commonDir, "worktrees");
8148
9139
  let entries;
8149
9140
  try {
8150
9141
  entries = await fsPromises2.readdir(registrationsRoot, { withFileTypes: true });
@@ -8155,16 +9146,16 @@ async function pruneExactMissingWorktreeRegistration(projectRoot3, worktreePath)
8155
9146
  const target = canonicalizePathForComparison(worktreePath);
8156
9147
  for (const entry of entries) {
8157
9148
  if (!entry.isDirectory()) continue;
8158
- const registrationPath = path15.join(registrationsRoot, entry.name);
9149
+ const registrationPath = path16.join(registrationsRoot, entry.name);
8159
9150
  if (!isPathWithinRoot(registrationPath, registrationsRoot)) continue;
8160
9151
  let gitdirPath;
8161
9152
  try {
8162
- gitdirPath = (await fsPromises2.readFile(path15.join(registrationPath, "gitdir"), "utf8")).trim();
9153
+ gitdirPath = (await fsPromises2.readFile(path16.join(registrationPath, "gitdir"), "utf8")).trim();
8163
9154
  } catch {
8164
9155
  continue;
8165
9156
  }
8166
- const resolvedGitdirPath = path15.isAbsolute(gitdirPath) ? gitdirPath : path15.resolve(registrationPath, gitdirPath);
8167
- 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;
8168
9159
  await fsPromises2.rm(registrationPath, { recursive: true, force: true });
8169
9160
  return true;
8170
9161
  }
@@ -8182,7 +9173,7 @@ async function removeWorktree(projectRoot3, worktreePath) {
8182
9173
  registered = await isWorktreeRegistered(projectRoot3, worktreePath);
8183
9174
  } catch (error) {
8184
9175
  errors.push(asError(error));
8185
- 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)}`);
8186
9177
  }
8187
9178
  if (registered) {
8188
9179
  try {
@@ -8198,7 +9189,7 @@ async function removeWorktree(projectRoot3, worktreePath) {
8198
9189
  registered = await isWorktreeRegistered(projectRoot3, worktreePath);
8199
9190
  } catch (error) {
8200
9191
  errors.push(asError(error));
8201
- 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)}`);
8202
9193
  }
8203
9194
  }
8204
9195
  if (registered && !await pathExists(worktreePath)) {
@@ -8211,13 +9202,13 @@ async function removeWorktree(projectRoot3, worktreePath) {
8211
9202
  }
8212
9203
  if (registered) {
8213
9204
  errors.push(new Error(`Temporary worktree remains registered: ${worktreePath}`));
8214
- 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)}`);
8215
9206
  }
8216
9207
  try {
8217
- await fsPromises2.rm(path15.dirname(worktreePath), { recursive: true, force: true });
9208
+ await fsPromises2.rm(path16.dirname(worktreePath), { recursive: true, force: true });
8218
9209
  } catch (error) {
8219
9210
  errors.push(asError(error));
8220
- 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)}`);
8221
9212
  }
8222
9213
  }
8223
9214
  async function cleanupTemporaryWorktree(projectRoot3, worktreePath, temporaryRoot) {
@@ -8253,9 +9244,9 @@ async function withMaterializedBranch(request, callback) {
8253
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.`
8254
9245
  );
8255
9246
  }
8256
- const temporaryRoot = await fsPromises2.mkdtemp(path15.join(os6.tmpdir(), "codebase-index-branch-"));
8257
- const worktreePath = path15.join(temporaryRoot, "worktree");
8258
- 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");
8259
9250
  await fsPromises2.mkdir(hooksPath);
8260
9251
  const info = {
8261
9252
  branch: request.branch,
@@ -8306,8 +9297,8 @@ async function withMaterializedBranch(request, callback) {
8306
9297
 
8307
9298
  // src/tools/changed-files.ts
8308
9299
  import { execFile as execFile3 } from "child_process";
8309
- import { realpathSync as realpathSync4 } from "fs";
8310
- import * as path16 from "path";
9300
+ import { realpathSync as realpathSync5 } from "fs";
9301
+ import * as path17 from "path";
8311
9302
  import { promisify as promisify2 } from "util";
8312
9303
  var execFileAsync2 = promisify2(execFile3);
8313
9304
  var GH_PR_VIEW_FIELDS = [
@@ -8449,9 +9440,9 @@ function getHeadRepositoryIdentity(data, host) {
8449
9440
  return `${host}/${owner.toLowerCase()}/${repository.replace(/\.git$/i, "").toLowerCase()}`;
8450
9441
  }
8451
9442
  function getLocalRepositoryIdentity(projectRoot3) {
8452
- let canonicalRoot = path16.resolve(projectRoot3);
9443
+ let canonicalRoot = path17.resolve(projectRoot3);
8453
9444
  try {
8454
- canonicalRoot = realpathSync4.native(canonicalRoot);
9445
+ canonicalRoot = realpathSync5.native(canonicalRoot);
8455
9446
  } catch {
8456
9447
  }
8457
9448
  return `local:${canonicalRoot}`;
@@ -8510,17 +9501,17 @@ async function getMergeBase(projectRoot3, baseCommit, headCommit) {
8510
9501
  return commit;
8511
9502
  }
8512
9503
  function normalizeFiles(rawFiles, projectRoot3) {
8513
- const root = path16.resolve(projectRoot3);
9504
+ const root = path17.resolve(projectRoot3);
8514
9505
  const seen = /* @__PURE__ */ new Set();
8515
9506
  const result = [];
8516
9507
  for (const raw of rawFiles) {
8517
9508
  if (raw.length === 0) continue;
8518
- const absolute = path16.resolve(root, raw);
8519
- const relative13 = path16.relative(root, absolute);
8520
- 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)) {
8521
9512
  throw new Error(`Changed file escapes the project root: ${JSON.stringify(raw)}`);
8522
9513
  }
8523
- const cleaned = relative13.startsWith(`.${path16.sep}`) ? relative13.slice(2) : relative13;
9514
+ const cleaned = relative13.startsWith(`.${path17.sep}`) ? relative13.slice(2) : relative13;
8524
9515
  if (!seen.has(cleaned)) {
8525
9516
  seen.add(cleaned);
8526
9517
  result.push(cleaned);
@@ -8531,7 +9522,7 @@ function normalizeFiles(rawFiles, projectRoot3) {
8531
9522
 
8532
9523
  // src/indexer/git-blame.ts
8533
9524
  import { execFile as execFile4 } from "child_process";
8534
- import * as path17 from "path";
9525
+ import * as path18 from "path";
8535
9526
  import { promisify as promisify3 } from "util";
8536
9527
  var execFileAsync3 = promisify3(execFile4);
8537
9528
  function parseGitBlamePorcelain(output) {
@@ -8569,7 +9560,7 @@ function parseGitBlamePorcelain(output) {
8569
9560
  return Array.from(commits.values()).filter((commit) => commit.lines > 0).sort((a, b) => b.lines - a.lines || b.committedAt - a.committedAt)[0];
8570
9561
  }
8571
9562
  async function getChunkGitBlame(projectRoot3, filePath, startLine, endLine) {
8572
- const relativePath = path17.relative(projectRoot3, filePath);
9563
+ const relativePath = path18.relative(projectRoot3, filePath);
8573
9564
  try {
8574
9565
  const { stdout } = await execFileAsync3(
8575
9566
  "git",
@@ -9148,8 +10139,8 @@ function pathSegmentsForAffinityMatch(filePath) {
9148
10139
  if (segments.length === 0) {
9149
10140
  return [];
9150
10141
  }
9151
- const basename7 = segments[segments.length - 1] ?? "";
9152
- const basenameWithoutExt = basename7.replace(/\.[^/.]+$/u, "");
10142
+ const basename8 = segments[segments.length - 1] ?? "";
10143
+ const basenameWithoutExt = basename8.replace(/\.[^/.]+$/u, "");
9153
10144
  const normalizedSegments = segments.map((segment) => segment.toLowerCase());
9154
10145
  return Array.from(/* @__PURE__ */ new Set([
9155
10146
  ...normalizedSegments,
@@ -9458,8 +10449,8 @@ function hasAllEmbeddingParts(parts, expectedPartCount) {
9458
10449
 
9459
10450
  // src/indexer/failed-state-persistence.ts
9460
10451
  import * as fs2 from "fs";
9461
- import { createHash, randomBytes as randomBytes3 } from "crypto";
9462
- import * as path18 from "path";
10452
+ import { createHash as createHash2, randomBytes as randomBytes3 } from "crypto";
10453
+ import * as path19 from "path";
9463
10454
  import { StringDecoder } from "string_decoder";
9464
10455
  var CURRENT_FAILED_BATCH_VERSION = 1;
9465
10456
  var DEFAULT_MALFORMED_LINE_ACTION = "skip";
@@ -9477,7 +10468,7 @@ function* readFailedBatchRecords(filePath, options = {}) {
9477
10468
  function createFailedBatchWriter(targetPath) {
9478
10469
  const temporaryPath = createTemporaryPath(targetPath);
9479
10470
  let finalized = false;
9480
- fs2.mkdirSync(path18.dirname(targetPath), { recursive: true });
10471
+ fs2.mkdirSync(path19.dirname(targetPath), { recursive: true });
9481
10472
  fs2.closeSync(fs2.openSync(temporaryPath, "w"));
9482
10473
  const write = (record) => {
9483
10474
  if (finalized) {
@@ -9496,7 +10487,7 @@ function createFailedBatchWriter(targetPath) {
9496
10487
  if (lines.length === 0) {
9497
10488
  return;
9498
10489
  }
9499
- fs2.mkdirSync(path18.dirname(targetPath), { recursive: true });
10490
+ fs2.mkdirSync(path19.dirname(targetPath), { recursive: true });
9500
10491
  fs2.appendFileSync(temporaryPath, `${lines.join("\n")}
9501
10492
  `, "utf-8");
9502
10493
  };
@@ -9504,7 +10495,7 @@ function createFailedBatchWriter(targetPath) {
9504
10495
  if (finalized) {
9505
10496
  return;
9506
10497
  }
9507
- fs2.mkdirSync(path18.dirname(targetPath), { recursive: true });
10498
+ fs2.mkdirSync(path19.dirname(targetPath), { recursive: true });
9508
10499
  fs2.renameSync(temporaryPath, targetPath);
9509
10500
  finalized = true;
9510
10501
  };
@@ -9650,10 +10641,10 @@ function stripLeadingBomAndWhitespace(value) {
9650
10641
  return result;
9651
10642
  }
9652
10643
  function createTemporaryPath(targetPath) {
9653
- const randomId = createHash("sha1").update(`${Date.now()}:${randomBytes3(8).toString("hex")}`).digest("hex");
9654
- const targetDir = path18.dirname(targetPath);
9655
- const baseName = path18.basename(targetPath);
9656
- 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`);
9657
10648
  }
9658
10649
  function handleMalformedLine(filePath, lineNumber, line, error, options) {
9659
10650
  const action = options.malformedLineAction ?? DEFAULT_MALFORMED_LINE_ACTION;
@@ -9899,9 +10890,9 @@ var SWIFT_PARSER_VERSION = "1";
9899
10890
  var METAL_PARSER_VERSION = "1";
9900
10891
  var SYMBOL_EXTRACTOR_VERSION = "1";
9901
10892
  function isPathWithinRoot2(filePath, rootPath) {
9902
- const normalizedFilePath = path19.resolve(filePath);
9903
- const normalizedRoot = path19.resolve(rootPath);
9904
- 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}`);
9905
10896
  }
9906
10897
  function promoteIdentifierMatches(query, combined, semanticCandidates, keywordCandidates, database, branchChunkIds, prioritizeSourcePaths = classifyQueryIntentRaw(query) === "source") {
9907
10898
  if (combined.length === 0) {
@@ -10232,10 +11223,10 @@ function matchesHardSearchFilters(candidate, options, projectRoot3) {
10232
11223
  }
10233
11224
  if (options?.directory) {
10234
11225
  const candidatePath = canonicalizePathForComparison(
10235
- path19.resolve(projectRoot3, candidate.metadata.filePath.replace(/\\/g, path19.sep))
11226
+ path20.resolve(projectRoot3, candidate.metadata.filePath.replace(/\\/g, path20.sep))
10236
11227
  );
10237
11228
  const directoryPath = canonicalizePathForComparison(
10238
- path19.resolve(projectRoot3, options.directory.trim().replace(/\\/g, path19.sep))
11229
+ path20.resolve(projectRoot3, options.directory.trim().replace(/\\/g, path20.sep))
10239
11230
  );
10240
11231
  if (!isPathWithinRoot2(candidatePath, directoryPath)) return false;
10241
11232
  }
@@ -10348,26 +11339,37 @@ var Indexer = class _Indexer {
10348
11339
  return this.indexPathOverride ?? resolveProjectIndexPath(this.projectRoot, this.config.scope, this.host);
10349
11340
  }
10350
11341
  toCanonicalFilePath(filePath) {
10351
- if (!path19.isAbsolute(filePath)) {
11342
+ if (!path20.isAbsolute(filePath)) {
10352
11343
  return this.resolveStoredFilePath(filePath, this.projectRoot);
10353
11344
  }
10354
- 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)) {
10355
11346
  return filePath;
10356
11347
  }
10357
- return path19.resolve(this.projectRoot, path19.relative(this.materializedProjectRoot, filePath));
11348
+ return path20.resolve(this.projectRoot, path20.relative(this.materializedProjectRoot, filePath));
10358
11349
  }
10359
11350
  toStoredFilePath(filePath) {
10360
11351
  const canonicalFilePath = this.toCanonicalFilePath(filePath);
10361
11352
  if (this.config.scope !== "project" || !isPathWithinRoot2(canonicalFilePath, this.projectRoot)) {
10362
11353
  return canonicalFilePath;
10363
11354
  }
10364
- 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);
10365
11367
  }
10366
11368
  resolveStoredFilePath(filePath, rootPath = this.projectRoot) {
10367
- if (path19.isAbsolute(filePath)) {
11369
+ if (path20.isAbsolute(filePath)) {
10368
11370
  return filePath;
10369
11371
  }
10370
- const resolvedPath = path19.resolve(rootPath, ...filePath.split("/"));
11372
+ const resolvedPath = path20.resolve(rootPath, ...filePath.split("/"));
10371
11373
  if (!isPathWithinRoot2(resolvedPath, rootPath)) {
10372
11374
  throw new Error(`Stored project path escapes project root: ${JSON.stringify(filePath)}`);
10373
11375
  }
@@ -10391,7 +11393,7 @@ var Indexer = class _Indexer {
10391
11393
  }
10392
11394
  toMaterializedFilePath(filePath) {
10393
11395
  const storedFilePath = this.toStoredFilePath(filePath);
10394
- if (path19.isAbsolute(storedFilePath)) {
11396
+ if (path20.isAbsolute(storedFilePath)) {
10395
11397
  return storedFilePath;
10396
11398
  }
10397
11399
  return this.resolveStoredFilePath(storedFilePath, this.materializedProjectRoot);
@@ -10408,10 +11410,10 @@ var Indexer = class _Indexer {
10408
11410
  }
10409
11411
  getRuntimeArtifactPath(fileName) {
10410
11412
  const namespace = this.getRuntimeArtifactNamespace();
10411
- if (!namespace) return path19.join(this.indexPath, fileName);
10412
- const extension = path19.extname(fileName);
11413
+ if (!namespace) return path20.join(this.indexPath, fileName);
11414
+ const extension = path20.extname(fileName);
10413
11415
  const baseName = fileName.slice(0, fileName.length - extension.length);
10414
- return path19.join(this.indexPath, `${baseName}.${namespace}${extension}`);
11416
+ return path20.join(this.indexPath, `${baseName}.${namespace}${extension}`);
10415
11417
  }
10416
11418
  refreshRuntimeArtifactPaths() {
10417
11419
  this.fileHashCachePath = this.getRuntimeArtifactPath("file-hashes.json");
@@ -10424,14 +11426,14 @@ var Indexer = class _Indexer {
10424
11426
  getMaterializedKnowledgeBases() {
10425
11427
  const canonicalProjectRoot = this.getCanonicalPath(this.projectRoot);
10426
11428
  return this.config.knowledgeBases.map((knowledgeBase) => {
10427
- const configuredPath = path19.isAbsolute(knowledgeBase) ? knowledgeBase : path19.resolve(this.projectRoot, knowledgeBase);
11429
+ const configuredPath = path20.isAbsolute(knowledgeBase) ? knowledgeBase : path20.resolve(this.projectRoot, knowledgeBase);
10428
11430
  const canonicalPath = this.getCanonicalPath(configuredPath);
10429
11431
  if (!isPathWithinRoot2(canonicalPath, canonicalProjectRoot)) {
10430
11432
  return canonicalPath;
10431
11433
  }
10432
- return path19.resolve(
11434
+ return path20.resolve(
10433
11435
  this.materializedProjectRoot,
10434
- path19.relative(canonicalProjectRoot, canonicalPath)
11436
+ path20.relative(canonicalProjectRoot, canonicalPath)
10435
11437
  );
10436
11438
  });
10437
11439
  }
@@ -10439,7 +11441,7 @@ var Indexer = class _Indexer {
10439
11441
  try {
10440
11442
  return canonicalizePathForComparison(targetPath);
10441
11443
  } catch {
10442
- return path19.resolve(targetPath);
11444
+ return path20.resolve(targetPath);
10443
11445
  }
10444
11446
  }
10445
11447
  getProjectIdentityHash(projectRoot3) {
@@ -10520,7 +11522,7 @@ var Indexer = class _Indexer {
10520
11522
  } catch (error) {
10521
11523
  releaseError = error;
10522
11524
  this.writerArtifactFingerprint = null;
10523
- if (!existsSync11(lease.lockPath) && this.activeIndexLease?.owner.token === lease.owner.token) {
11525
+ if (!existsSync12(lease.lockPath) && this.activeIndexLease?.owner.token === lease.owner.token) {
10524
11526
  this.activeIndexLease = null;
10525
11527
  }
10526
11528
  }
@@ -10538,12 +11540,12 @@ var Indexer = class _Indexer {
10538
11540
  return this.activeIndexLease;
10539
11541
  }
10540
11542
  loadFileHashCache() {
10541
- if (!existsSync11(this.fileHashCachePath)) {
11543
+ if (!existsSync12(this.fileHashCachePath)) {
10542
11544
  this.fileHashCache = /* @__PURE__ */ new Map();
10543
11545
  return;
10544
11546
  }
10545
11547
  try {
10546
- const data = readFileSync8(this.fileHashCachePath, "utf-8");
11548
+ const data = readFileSync9(this.fileHashCachePath, "utf-8");
10547
11549
  const parsed = JSON.parse(data);
10548
11550
  this.fileHashCache = new Map(Object.entries(parsed));
10549
11551
  } catch (error) {
@@ -10565,24 +11567,24 @@ var Indexer = class _Indexer {
10565
11567
  atomicWriteSync(targetPath, data) {
10566
11568
  const lease = this.requireActiveLease();
10567
11569
  const tempPath = createLeaseTemporaryPath(targetPath, lease.owner, "tmp");
10568
- mkdirSync4(path19.dirname(targetPath), { recursive: true });
11570
+ mkdirSync5(path20.dirname(targetPath), { recursive: true });
10569
11571
  try {
10570
- writeFileSync3(tempPath, data);
10571
- renameSync3(tempPath, targetPath);
11572
+ writeFileSync4(tempPath, data);
11573
+ renameSync4(tempPath, targetPath);
10572
11574
  } finally {
10573
11575
  removeLeaseTemporaryPath(tempPath);
10574
11576
  }
10575
11577
  }
10576
11578
  saveInvertedIndex(invertedIndex) {
10577
11579
  this.atomicWriteSync(
10578
- path19.join(this.indexPath, "inverted-index.json"),
11580
+ path20.join(this.indexPath, "inverted-index.json"),
10579
11581
  invertedIndex.serialize()
10580
11582
  );
10581
11583
  }
10582
11584
  getScopedRoots(projectRoot3 = this.projectRoot) {
10583
11585
  const roots = /* @__PURE__ */ new Set([this.getCanonicalPath(projectRoot3)]);
10584
11586
  for (const kbRoot of this.config.knowledgeBases) {
10585
- roots.add(this.getCanonicalPath(path19.resolve(projectRoot3, kbRoot)));
11587
+ roots.add(this.getCanonicalPath(path20.resolve(projectRoot3, kbRoot)));
10586
11588
  }
10587
11589
  return Array.from(roots);
10588
11590
  }
@@ -10999,7 +12001,7 @@ var Indexer = class _Indexer {
10999
12001
  return configuredProviderInfo !== null && recovery.embeddingProvider === configuredProviderInfo.provider && recovery.embeddingModel === configuredProviderInfo.modelInfo.model && recovery.embeddingDimensions === configuredProviderInfo.modelInfo.dimensions && recovery.embeddingStrategyVersion === EMBEDDING_STRATEGY_VERSION;
11000
12002
  }
11001
12003
  hasUnknownLegacyForceIndexClear(owner) {
11002
- 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"));
11003
12005
  }
11004
12006
  async recoverFromInterruptedIndexingUnlocked(owners) {
11005
12007
  for (const owner of owners) {
@@ -11185,7 +12187,7 @@ var Indexer = class _Indexer {
11185
12187
  }
11186
12188
  }
11187
12189
  clearFailedBatchState() {
11188
- if (existsSync11(this.failedBatchesPath)) {
12190
+ if (existsSync12(this.failedBatchesPath)) {
11189
12191
  try {
11190
12192
  unlinkSync2(this.failedBatchesPath);
11191
12193
  } catch {
@@ -11387,7 +12389,7 @@ var Indexer = class _Indexer {
11387
12389
  await options.queue.onSizeLessThan(Math.max(1, options.providerRateLimits.concurrency));
11388
12390
  const task = options.queue.add(async () => {
11389
12391
  if (options.rateLimitState.backoffMs > 0) {
11390
- await new Promise((resolve17) => setTimeout(resolve17, options.rateLimitState.backoffMs));
12392
+ await new Promise((resolve18) => setTimeout(resolve18, options.rateLimitState.backoffMs));
11391
12393
  }
11392
12394
  try {
11393
12395
  const embeddingResult = await pRetry(
@@ -11806,12 +12808,12 @@ var Indexer = class _Indexer {
11806
12808
  }
11807
12809
  }
11808
12810
  captureReaderArtifactFingerprint() {
11809
- const storePath = path19.join(this.indexPath, "vectors");
12811
+ const storePath = path20.join(this.indexPath, "vectors");
11810
12812
  return {
11811
12813
  vectors: `${this.getReaderFileFingerprint(storePath)}|${this.getReaderFileFingerprint(`${storePath}.meta.json`)}`,
11812
- keyword: this.getReaderFileFingerprint(path19.join(this.indexPath, "inverted-index.json")),
11813
- database: this.getReaderFileFingerprint(path19.join(this.indexPath, "codebase.db")),
11814
- 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)
11815
12817
  };
11816
12818
  }
11817
12819
  refreshReaderArtifacts() {
@@ -11836,13 +12838,13 @@ var Indexer = class _Indexer {
11836
12838
  issues.set(component, this.createReadIssue(component, message));
11837
12839
  this.readerArtifactRetryAfter.set(component, Date.now() + READER_ARTIFACT_RETRY_INTERVAL_MS);
11838
12840
  };
11839
- const storePath = path19.join(this.indexPath, "vectors");
12841
+ const storePath = path20.join(this.indexPath, "vectors");
11840
12842
  const vectorMetadataPath = `${storePath}.meta.json`;
11841
- const invertedIndexPath = path19.join(this.indexPath, "inverted-index.json");
11842
- 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");
11843
12845
  if (vectorsChanged || retryDue("vectors")) {
11844
- const vectorStoreExists = existsSync11(storePath);
11845
- const vectorMetadataExists = existsSync11(vectorMetadataPath);
12846
+ const vectorStoreExists = existsSync12(storePath);
12847
+ const vectorMetadataExists = existsSync12(vectorMetadataPath);
11846
12848
  if (vectorStoreExists && vectorMetadataExists) {
11847
12849
  try {
11848
12850
  const store = new VectorStore(storePath, this.configuredProviderInfo.modelInfo.dimensions);
@@ -11857,8 +12859,8 @@ var Indexer = class _Indexer {
11857
12859
  setIssue("vectors", this.getVectorReadIssueMessage());
11858
12860
  }
11859
12861
  }
11860
- if (keywordChanged || retryDue("keyword") || !existsSync11(invertedIndexPath) && (this.store?.count() ?? 0) > 0) {
11861
- if (existsSync11(invertedIndexPath)) {
12862
+ if (keywordChanged || retryDue("keyword") || !existsSync12(invertedIndexPath) && (this.store?.count() ?? 0) > 0) {
12863
+ if (existsSync12(invertedIndexPath)) {
11862
12864
  try {
11863
12865
  const invertedIndex = new InvertedIndex(invertedIndexPath);
11864
12866
  invertedIndex.load();
@@ -11873,7 +12875,7 @@ var Indexer = class _Indexer {
11873
12875
  }
11874
12876
  }
11875
12877
  if (databaseReplaced || databaseChanged && issues.has("database") || retryDue("database")) {
11876
- if (existsSync11(dbPath)) {
12878
+ if (existsSync12(dbPath)) {
11877
12879
  try {
11878
12880
  const database = Database.openReadOnly(dbPath);
11879
12881
  if (this.database) {
@@ -11955,11 +12957,11 @@ var Indexer = class _Indexer {
11955
12957
  });
11956
12958
  this.provider = createEmbeddingProvider(this.configuredProviderInfo);
11957
12959
  const dimensions = this.configuredProviderInfo.modelInfo.dimensions;
11958
- const storePath = path19.join(this.indexPath, "vectors");
12960
+ const storePath = path20.join(this.indexPath, "vectors");
11959
12961
  const vectorMetadataPath = `${storePath}.meta.json`;
11960
- const invertedIndexPath = path19.join(this.indexPath, "inverted-index.json");
11961
- const dbPath = path19.join(this.indexPath, "codebase.db");
11962
- 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);
11963
12965
  const readerArtifactFingerprint = mode === "reader" ? this.captureReaderArtifactFingerprint() : null;
11964
12966
  if (mode === "writer") {
11965
12967
  await fsPromises3.mkdir(this.indexPath, { recursive: true });
@@ -11991,14 +12993,14 @@ var Indexer = class _Indexer {
11991
12993
  }
11992
12994
  }
11993
12995
  this.store = new VectorStore(storePath, dimensions);
11994
- if (existsSync11(storePath) || existsSync11(vectorMetadataPath)) {
12996
+ if (existsSync12(storePath) || existsSync12(vectorMetadataPath)) {
11995
12997
  this.store.load();
11996
12998
  }
11997
12999
  this.invertedIndex = new InvertedIndex(invertedIndexPath);
11998
13000
  try {
11999
13001
  this.invertedIndex.load();
12000
13002
  } catch {
12001
- if (existsSync11(invertedIndexPath)) {
13003
+ if (existsSync12(invertedIndexPath)) {
12002
13004
  await fsPromises3.unlink(invertedIndexPath);
12003
13005
  }
12004
13006
  this.invertedIndex = new InvertedIndex(invertedIndexPath);
@@ -12016,8 +13018,8 @@ var Indexer = class _Indexer {
12016
13018
  }
12017
13019
  } else {
12018
13020
  this.store = new VectorStore(storePath, dimensions);
12019
- const vectorStoreExists = existsSync11(storePath);
12020
- const vectorMetadataExists = existsSync11(vectorMetadataPath);
13021
+ const vectorStoreExists = existsSync12(storePath);
13022
+ const vectorMetadataExists = existsSync12(vectorMetadataPath);
12021
13023
  const vectorReadFailureMessage = this.getVectorReadIssueMessage();
12022
13024
  if (vectorStoreExists !== vectorMetadataExists) {
12023
13025
  this.recordReadIssue("vectors", vectorReadFailureMessage);
@@ -12030,7 +13032,7 @@ var Indexer = class _Indexer {
12030
13032
  }
12031
13033
  }
12032
13034
  this.invertedIndex = new InvertedIndex(invertedIndexPath);
12033
- if (existsSync11(invertedIndexPath)) {
13035
+ if (existsSync12(invertedIndexPath)) {
12034
13036
  try {
12035
13037
  this.invertedIndex.load();
12036
13038
  } catch (error) {
@@ -12044,7 +13046,7 @@ var Indexer = class _Indexer {
12044
13046
  } else if (this.store.count() > 0) {
12045
13047
  this.recordReadIssue("keyword", this.getKeywordReadIssueMessage());
12046
13048
  }
12047
- if (existsSync11(dbPath)) {
13049
+ if (existsSync12(dbPath)) {
12048
13050
  try {
12049
13051
  this.database = Database.openReadOnly(dbPath);
12050
13052
  } catch (error) {
@@ -12136,7 +13138,7 @@ var Indexer = class _Indexer {
12136
13138
  if (await this.tryResetCorruptedIndex("running automatic orphan garbage collection", error)) {
12137
13139
  return {
12138
13140
  resetCorruptedIndex: true,
12139
- warning: this.getCorruptedIndexWarning(path19.join(this.indexPath, "codebase.db"))
13141
+ warning: this.getCorruptedIndexWarning(path20.join(this.indexPath, "codebase.db"))
12140
13142
  };
12141
13143
  }
12142
13144
  throw error;
@@ -12151,7 +13153,7 @@ var Indexer = class _Indexer {
12151
13153
  return;
12152
13154
  }
12153
13155
  const retainedEntries = store.getAllMetadata().filter(({ key }) => !excludedSet.has(key));
12154
- const storeBasePath = path19.join(this.indexPath, "vectors");
13156
+ const storeBasePath = path20.join(this.indexPath, "vectors");
12155
13157
  const storeIndexPath = storeBasePath;
12156
13158
  const storeMetadataPath = `${storeBasePath}.meta.json`;
12157
13159
  const lease = this.requireActiveLease();
@@ -12161,19 +13163,19 @@ var Indexer = class _Indexer {
12161
13163
  let backedUpMetadata = false;
12162
13164
  let rebuiltCount = 0;
12163
13165
  let skippedCount = 0;
12164
- if (existsSync11(backupIndexPath)) {
13166
+ if (existsSync12(backupIndexPath)) {
12165
13167
  unlinkSync2(backupIndexPath);
12166
13168
  }
12167
- if (existsSync11(backupMetadataPath)) {
13169
+ if (existsSync12(backupMetadataPath)) {
12168
13170
  unlinkSync2(backupMetadataPath);
12169
13171
  }
12170
13172
  try {
12171
- if (existsSync11(storeIndexPath)) {
12172
- renameSync3(storeIndexPath, backupIndexPath);
13173
+ if (existsSync12(storeIndexPath)) {
13174
+ renameSync4(storeIndexPath, backupIndexPath);
12173
13175
  backedUpIndex = true;
12174
13176
  }
12175
- if (existsSync11(storeMetadataPath)) {
12176
- renameSync3(storeMetadataPath, backupMetadataPath);
13177
+ if (existsSync12(storeMetadataPath)) {
13178
+ renameSync4(storeMetadataPath, backupMetadataPath);
12177
13179
  backedUpMetadata = true;
12178
13180
  }
12179
13181
  store.clear();
@@ -12193,10 +13195,10 @@ var Indexer = class _Indexer {
12193
13195
  rebuiltCount += 1;
12194
13196
  }
12195
13197
  store.save();
12196
- if (backedUpIndex && existsSync11(backupIndexPath)) {
13198
+ if (backedUpIndex && existsSync12(backupIndexPath)) {
12197
13199
  unlinkSync2(backupIndexPath);
12198
13200
  }
12199
- if (backedUpMetadata && existsSync11(backupMetadataPath)) {
13201
+ if (backedUpMetadata && existsSync12(backupMetadataPath)) {
12200
13202
  unlinkSync2(backupMetadataPath);
12201
13203
  }
12202
13204
  this.logger.gc("info", "Rebuilt vector store to avoid native remove", {
@@ -12209,17 +13211,17 @@ var Indexer = class _Indexer {
12209
13211
  store.clear();
12210
13212
  } catch {
12211
13213
  }
12212
- if (existsSync11(storeIndexPath)) {
13214
+ if (existsSync12(storeIndexPath)) {
12213
13215
  unlinkSync2(storeIndexPath);
12214
13216
  }
12215
- if (existsSync11(storeMetadataPath)) {
13217
+ if (existsSync12(storeMetadataPath)) {
12216
13218
  unlinkSync2(storeMetadataPath);
12217
13219
  }
12218
- if (backedUpIndex && existsSync11(backupIndexPath)) {
12219
- renameSync3(backupIndexPath, storeIndexPath);
13220
+ if (backedUpIndex && existsSync12(backupIndexPath)) {
13221
+ renameSync4(backupIndexPath, storeIndexPath);
12220
13222
  }
12221
- if (backedUpMetadata && existsSync11(backupMetadataPath)) {
12222
- renameSync3(backupMetadataPath, storeMetadataPath);
13223
+ if (backedUpMetadata && existsSync12(backupMetadataPath)) {
13224
+ renameSync4(backupMetadataPath, storeMetadataPath);
12223
13225
  }
12224
13226
  if (backedUpIndex || backedUpMetadata) {
12225
13227
  store.load();
@@ -12234,11 +13236,11 @@ var Indexer = class _Indexer {
12234
13236
  return `Detected a corrupted local SQLite index at ${dbPath} and reset the local index. Run index_codebase to rebuild search data.`;
12235
13237
  }
12236
13238
  async removeProjectRuntimeStateArtifacts() {
12237
- if (!existsSync11(this.indexPath)) return;
13239
+ if (!existsSync12(this.indexPath)) return;
12238
13240
  const names = await fsPromises3.readdir(this.indexPath);
12239
13241
  const runtimeStatePattern = /^(?:file-hashes|failed-batches)(?:\.[a-f0-9]{16})?\.json$/;
12240
13242
  await Promise.all(
12241
- 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 }))
12242
13244
  );
12243
13245
  }
12244
13246
  async resetLocalIndexArtifacts() {
@@ -12254,13 +13256,13 @@ var Indexer = class _Indexer {
12254
13256
  this.readerArtifactRetryAfter.clear();
12255
13257
  this.fileHashCache.clear();
12256
13258
  const resetPaths = [
12257
- path19.join(this.indexPath, "codebase.db"),
12258
- path19.join(this.indexPath, "codebase.db-shm"),
12259
- path19.join(this.indexPath, "codebase.db-wal"),
12260
- path19.join(this.indexPath, "vectors"),
12261
- path19.join(this.indexPath, "vectors.usearch"),
12262
- path19.join(this.indexPath, "vectors.meta.json"),
12263
- 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")
12264
13266
  ];
12265
13267
  await Promise.all(resetPaths.map((targetPath) => fsPromises3.rm(targetPath, { recursive: true, force: true })));
12266
13268
  await this.removeProjectRuntimeStateArtifacts();
@@ -12270,7 +13272,7 @@ var Indexer = class _Indexer {
12270
13272
  if (!isSqliteCorruptionError(error)) {
12271
13273
  return false;
12272
13274
  }
12273
- const dbPath = path19.join(this.indexPath, "codebase.db");
13275
+ const dbPath = path20.join(this.indexPath, "codebase.db");
12274
13276
  const warning = this.getCorruptedIndexWarning(dbPath);
12275
13277
  const errorMessage = getErrorMessage4(error);
12276
13278
  if (this.config.scope === "global") {
@@ -12657,10 +13659,10 @@ var Indexer = class _Indexer {
12657
13659
  const reparseCachedMetalFiles = database.getMetadata(metalParserMetadataKey) !== METAL_PARSER_VERSION;
12658
13660
  const symbolExtractorMetadataKey = this.getSymbolExtractorVersionMetadataKey();
12659
13661
  const refreshCachedSymbols = database.getMetadata(symbolExtractorMetadataKey) !== SYMBOL_EXTRACTOR_VERSION;
12660
- 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")) {
12661
13663
  this.logger.info("Reindexing cached Swift files for parser support");
12662
13664
  }
12663
- 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")) {
12664
13666
  this.logger.info("Reindexing cached Metal files for parser support");
12665
13667
  }
12666
13668
  const includePatterns = [...this.config.include, ...this.config.additionalInclude];
@@ -12704,8 +13706,8 @@ var Indexer = class _Indexer {
12704
13706
  const needsCallGraphRefresh = cachedHashMatches && needsCallGraphResolutionMigration && database.getChunksByFile(storedPath).some(
12705
13707
  (chunk) => chunk.language === "php" || chunk.language === "c" || chunk.language === "cpp"
12706
13708
  );
12707
- const requiresSwiftParserUpgrade = reparseCachedSwiftFiles && path19.extname(storedPath).toLowerCase() === ".swift";
12708
- 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";
12709
13711
  const inMigrationScope = forceScopedReembed && scopedRoots !== null && this.isFileInCurrentScope(storedPath, scopedRoots);
12710
13712
  if (cachedHashMatches && !inMigrationScope && !needsCallGraphRefresh && !requiresSwiftParserUpgrade && !requiresMetalParserUpgrade && !refreshCachedSymbols) {
12711
13713
  unchangedFilePaths.add(storedPath);
@@ -12764,7 +13766,7 @@ var Indexer = class _Indexer {
12764
13766
  }
12765
13767
  }
12766
13768
  }
12767
- 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);
12768
13770
  const failedProcessing = this.prepareFailedBatchProcessing(scopedRoots, shouldRetryFailedPath);
12769
13771
  const maxChunkTokens = getSafeEmbeddingChunkTokenLimit(configuredProviderInfo);
12770
13772
  const providerRateLimits = this.getProviderRateLimits(configuredProviderInfo.provider);
@@ -12868,7 +13870,7 @@ var Indexer = class _Indexer {
12868
13870
  throw new Error(`Parsed file was not present in its source batch: ${parsed.path}`);
12869
13871
  }
12870
13872
  if (parsed.chunks.length === 0) {
12871
- 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);
12872
13874
  }
12873
13875
  let chunksToProcess = parsed.chunks;
12874
13876
  if (this.config.indexing.fallbackToTextOnMaxChunks && chunksToProcess.length > this.config.indexing.maxChunksPerFile) {
@@ -13203,8 +14205,8 @@ var Indexer = class _Indexer {
13203
14205
  previousBranchSymbolIds,
13204
14206
  Array.from(allSymbolIds)
13205
14207
  );
13206
- const vectorPath = path19.join(this.indexPath, "vectors");
13207
- 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`);
13208
14210
  if (backfilledBlameMetadata || shouldFingerprintLegacyPair || removedStoredChunks) {
13209
14211
  store.save();
13210
14212
  }
@@ -13999,7 +15001,7 @@ var Indexer = class _Indexer {
13999
15001
  const missingChunkKeys = [];
14000
15002
  const chunkKeysByRemovedFile = /* @__PURE__ */ new Map();
14001
15003
  for (const [filePath, chunkKeys] of filePathsToChunkKeys) {
14002
- if (!existsSync11(this.toMaterializedFilePath(filePath))) {
15004
+ if (!existsSync12(this.toMaterializedFilePath(filePath))) {
14003
15005
  chunkKeysByRemovedFile.set(filePath, chunkKeys);
14004
15006
  for (const key of chunkKeys) {
14005
15007
  missingChunkKeys.push(key);
@@ -14062,7 +15064,7 @@ var Indexer = class _Indexer {
14062
15064
  gcOrphanSymbols: 0,
14063
15065
  gcOrphanCallEdges: 0,
14064
15066
  resetCorruptedIndex: true,
14065
- warning: this.getCorruptedIndexWarning(path19.join(this.indexPath, "codebase.db"))
15067
+ warning: this.getCorruptedIndexWarning(path20.join(this.indexPath, "codebase.db"))
14066
15068
  };
14067
15069
  }
14068
15070
  this.logger.recordGc(removedCount, gcOrphanChunks, gcOrphanEmbeddings);
@@ -14092,7 +15094,8 @@ var Indexer = class _Indexer {
14092
15094
  const maxChunkTokens = getSafeEmbeddingChunkTokenLimit(configuredProviderInfo);
14093
15095
  const providerRateLimits = this.getProviderRateLimits(configuredProviderInfo.provider);
14094
15096
  const roots = this.config.scope === "global" ? this.getScopedRoots() : null;
14095
- const failedProcessing = this.prepareFailedBatchProcessing(roots, () => true);
15097
+ const shouldProcessFailedPath = (filePath) => filePath === null || !this.isStoredPathExcluded(filePath);
15098
+ const failedProcessing = this.prepareFailedBatchProcessing(roots, shouldProcessFailedPath);
14096
15099
  if (failedProcessing.latestById.size === 0) {
14097
15100
  this.finalizeFailedBatchWriteState(failedProcessing.state);
14098
15101
  return { succeeded: 0, failed: 0, remaining: 0 };
@@ -14105,7 +15108,7 @@ var Indexer = class _Indexer {
14105
15108
  const retryableChunks = this.iterateLatestFailedChunks(
14106
15109
  failedProcessing.latestById,
14107
15110
  roots,
14108
- () => true,
15111
+ shouldProcessFailedPath,
14109
15112
  maxChunkTokens
14110
15113
  );
14111
15114
  for (const retryBatch of iterateOrderedFileBatches(
@@ -14375,9 +15378,9 @@ var Indexer = class _Indexer {
14375
15378
  this.requireReadableComponents(readIssues, "database");
14376
15379
  let shortest = [];
14377
15380
  for (const branchKey of this.getBranchCatalogKeys()) {
14378
- const path25 = database.findShortestPath(fromName, toName, branchKey, maxDepth);
14379
- if (path25.length > 0 && (shortest.length === 0 || path25.length < shortest.length)) {
14380
- 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;
14381
15384
  }
14382
15385
  }
14383
15386
  return shortest.map((hop) => this.resolveFilePathRecord(hop));
@@ -14425,13 +15428,13 @@ var Indexer = class _Indexer {
14425
15428
  }
14426
15429
  }
14427
15430
  if (!found) continue;
14428
- const path25 = [];
15431
+ const path26 = [];
14429
15432
  let currentSymbolId = toSymbolId;
14430
15433
  while (true) {
14431
15434
  const symbol = symbolsById.get(currentSymbolId);
14432
15435
  if (!symbol) break;
14433
15436
  const parent = parentBySymbolId.get(currentSymbolId);
14434
- path25.push({
15437
+ path26.push({
14435
15438
  symbolId: symbol.id,
14436
15439
  symbolName: symbol.name,
14437
15440
  filePath: symbol.filePath,
@@ -14441,9 +15444,9 @@ var Indexer = class _Indexer {
14441
15444
  if (!parent) break;
14442
15445
  currentSymbolId = parent.parentId;
14443
15446
  }
14444
- path25.reverse();
14445
- if (path25.length > 0 && (shortest.length === 0 || path25.length < shortest.length)) {
14446
- shortest = path25;
15447
+ path26.reverse();
15448
+ if (path26.length > 0 && (shortest.length === 0 || path26.length < shortest.length)) {
15449
+ shortest = path26;
14447
15450
  }
14448
15451
  }
14449
15452
  return shortest.map((hop) => this.resolveFilePathRecord(hop));
@@ -14594,7 +15597,7 @@ var Indexer = class _Indexer {
14594
15597
  );
14595
15598
  }
14596
15599
  }
14597
- 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)));
14598
15601
  const storedChangedFiles = toStoredChangedFiles(changedFiles);
14599
15602
  const directSymbols = database.getSymbolsForFiles(storedChangedFiles, branchKey);
14600
15603
  const directIds = directSymbols.map((s) => s.id);
@@ -14743,12 +15746,12 @@ var Indexer = class _Indexer {
14743
15746
  if (meta.filePath) filePaths.add(meta.filePath);
14744
15747
  }
14745
15748
  const directory = options?.directory?.replace(/\/$/, "");
14746
- const absoluteDirectoryFilter = directory ? path19.resolve(this.projectRoot, directory) : void 0;
15749
+ const absoluteDirectoryFilter = directory ? path20.resolve(this.projectRoot, directory) : void 0;
14747
15750
  for (const filePath of filePaths) {
14748
15751
  if (directory) {
14749
15752
  const absoluteFilePath = this.resolveStoredFilePath(filePath);
14750
15753
  const matchesRelative = filePath === directory || filePath.startsWith(directory + "/");
14751
- 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));
14752
15755
  if (!matchesRelative && !matchesProjectRelative) {
14753
15756
  continue;
14754
15757
  }
@@ -14865,7 +15868,10 @@ function getOrCreateIndexer(projectRoot3, host) {
14865
15868
  }
14866
15869
  const indexer = new Indexer(projectRoot3, config, host);
14867
15870
  indexerCache.set(key, indexer);
14868
- configureAutoIndex(projectRoot3, host, config, () => getOrCreateIndexer(projectRoot3, host));
15871
+ configureAutoIndex(projectRoot3, host, config, () => getOrCreateIndexer(projectRoot3, host), {
15872
+ preserveManagedWorker: true,
15873
+ synchronizeBackgroundWorker: false
15874
+ });
14869
15875
  return indexer;
14870
15876
  }
14871
15877
  function getIndexerForProject(projectRoot3, host) {
@@ -14880,7 +15886,9 @@ function refreshIndexerForDirectory(projectRoot3, host, config = parseConfig(loa
14880
15886
  const key = getIndexerCacheKey(projectRoot3, host);
14881
15887
  configCache.set(key, config);
14882
15888
  indexerCache.set(key, new Indexer(projectRoot3, config, host));
14883
- configureAutoIndex(projectRoot3, host, config, () => getOrCreateIndexer(projectRoot3, host));
15889
+ configureAutoIndex(projectRoot3, host, config, () => getOrCreateIndexer(projectRoot3, host), {
15890
+ synchronizeBackgroundWorker: true
15891
+ });
14884
15892
  return config;
14885
15893
  }
14886
15894
  var AutoIndexRetrievalUnavailableError = class extends Error {
@@ -14907,7 +15915,7 @@ function trimOrUndefined(value) {
14907
15915
  return normalized || void 0;
14908
15916
  }
14909
15917
  function normalizeCallGraphPath(value) {
14910
- let normalized = path20.posix.normalize(value.trim().replaceAll("\\", "/"));
15918
+ let normalized = path21.posix.normalize(value.trim().replaceAll("\\", "/"));
14911
15919
  if (normalized.startsWith("./")) {
14912
15920
  normalized = normalized.slice(2);
14913
15921
  }
@@ -15100,12 +16108,12 @@ async function getCallGraphPath(projectRoot3, host, from, to, maxDepth, fromFile
15100
16108
  if (fromResolution.status !== "resolved" || toResolution.status !== "resolved") {
15101
16109
  return { from: fromResolution, to: toResolution, path: [] };
15102
16110
  }
15103
- const path25 = await indexer.findCallPathBySymbolIds(
16111
+ const path26 = await indexer.findCallPathBySymbolIds(
15104
16112
  fromResolution.symbolId,
15105
16113
  toResolution.symbolId,
15106
16114
  maxDepth
15107
16115
  );
15108
- return { from: fromResolution, to: toResolution, path: path25 };
16116
+ return { from: fromResolution, to: toResolution, path: path26 };
15109
16117
  }
15110
16118
  async function runIndexCodebase(projectRoot3, host, args, onProgress) {
15111
16119
  const root = getProjectRoot(projectRoot3, host);
@@ -15303,15 +16311,15 @@ async function getIndexLogs(projectRoot3, host, args) {
15303
16311
  function addKnowledgeBase(projectRoot3, host, knowledgeBasePath) {
15304
16312
  const root = getProjectRoot(projectRoot3, host);
15305
16313
  const inputPath = knowledgeBasePath.trim();
15306
- const normalizedPath2 = path20.resolve(
15307
- path20.isAbsolute(inputPath) ? inputPath : resolveKnowledgeBasePath(inputPath, root)
16314
+ const normalizedPath2 = path21.resolve(
16315
+ path21.isAbsolute(inputPath) ? inputPath : resolveKnowledgeBasePath(inputPath, root)
15308
16316
  );
15309
- if (!existsSync12(normalizedPath2)) {
16317
+ if (!existsSync13(normalizedPath2)) {
15310
16318
  return `Error: Directory does not exist: ${normalizedPath2}`;
15311
16319
  }
15312
16320
  let realPath;
15313
16321
  try {
15314
- realPath = realpathSync5(normalizedPath2);
16322
+ realPath = realpathSync6(normalizedPath2);
15315
16323
  } catch {
15316
16324
  return `Error: Cannot resolve path: ${normalizedPath2}`;
15317
16325
  }
@@ -15340,7 +16348,7 @@ function addKnowledgeBase(projectRoot3, host, knowledgeBasePath) {
15340
16348
  }
15341
16349
  }
15342
16350
  for (const dotDir of sensitiveDotDirs) {
15343
- const sensitiveDir = path20.join(homeDir, dotDir);
16351
+ const sensitiveDir = path21.join(homeDir, dotDir);
15344
16352
  if (sensitiveDir && (realPath === sensitiveDir || realPath.startsWith(`${sensitiveDir}/`))) {
15345
16353
  return `Error: Adding sensitive directory as knowledge base is not allowed: ${normalizedPath2}`;
15346
16354
  }
@@ -15386,7 +16394,7 @@ function listKnowledgeBases(projectRoot3, host) {
15386
16394
  for (let i = 0; i < knowledgeBases.length; i++) {
15387
16395
  const kb = knowledgeBases[i];
15388
16396
  const resolvedPath = resolveKnowledgeBasePath(kb, root);
15389
- const exists = existsSync12(resolvedPath);
16397
+ const exists = existsSync13(resolvedPath);
15390
16398
  result += `[${i + 1}] ${kb}
15391
16399
  `;
15392
16400
  result += ` Resolved: ${resolvedPath}
@@ -15403,7 +16411,7 @@ function listKnowledgeBases(projectRoot3, host) {
15403
16411
  }
15404
16412
  result += "\n";
15405
16413
  }
15406
- const hasHostConfig = existsSync12(path20.join(root, getHostProjectConfigRelativePath(host)));
16414
+ const hasHostConfig = existsSync13(path21.join(root, getHostProjectConfigRelativePath(host)));
15407
16415
  if (hasHostConfig) {
15408
16416
  result += `
15409
16417
  Config sources: 1 file(s).`;
@@ -15876,7 +16884,7 @@ async function resolveCodebaseContextUnmeasured(projectRoot3, host, input) {
15876
16884
  const directory = input.directory ?? void 0;
15877
16885
  const tokenBudget = input.tokenBudget ?? void 0;
15878
16886
  if (from && to) {
15879
- const path25 = await getCallGraphPath(
16887
+ const path26 = await getCallGraphPath(
15880
16888
  projectRoot3,
15881
16889
  host,
15882
16890
  from,
@@ -15885,25 +16893,25 @@ async function resolveCodebaseContextUnmeasured(projectRoot3, host, input) {
15885
16893
  fromFilePath,
15886
16894
  toFilePath
15887
16895
  );
15888
- const pathText = formatCallGraphPathResult(path25);
15889
- if (path25.path.length > 0) {
16896
+ const pathText = formatCallGraphPathResult(path26);
16897
+ if (path26.path.length > 0) {
15890
16898
  const fitted2 = fitTextToContextBudget(
15891
16899
  pathText,
15892
16900
  tokenBudget
15893
16901
  );
15894
16902
  return {
15895
16903
  text: fitted2.text,
15896
- details: fittedDetails("path", fitted2, path25.path.length)
16904
+ details: fittedDetails("path", fitted2, path26.path.length)
15897
16905
  };
15898
16906
  }
15899
- if (path25.from.status !== "resolved" || path25.to.status !== "resolved") {
16907
+ if (path26.from.status !== "resolved" || path26.to.status !== "resolved") {
15900
16908
  const fitted2 = fitTextToContextBudget(pathText, tokenBudget);
15901
16909
  return {
15902
16910
  text: fitted2.text,
15903
16911
  details: fittedDetails("path", fitted2, 0)
15904
16912
  };
15905
16913
  }
15906
- const resolvedFrom = path25.from;
16914
+ const resolvedFrom = path26.from;
15907
16915
  const { callers } = await getCallGraphData(projectRoot3, host, {
15908
16916
  name: to,
15909
16917
  direction: "callers",
@@ -16344,7 +17352,7 @@ function registerPiCallGraphTools(pi) {
16344
17352
  }
16345
17353
 
16346
17354
  // src/watcher/file-watcher.ts
16347
- import { existsSync as existsSync13, statSync as statSync6 } from "fs";
17355
+ import { existsSync as existsSync14, statSync as statSync6 } from "fs";
16348
17356
 
16349
17357
  // node_modules/chokidar/index.js
16350
17358
  import { EventEmitter as EventEmitter2 } from "events";
@@ -16436,7 +17444,7 @@ var ReaddirpStream = class extends Readable {
16436
17444
  this._directoryFilter = normalizeFilter(opts.directoryFilter);
16437
17445
  const statMethod = opts.lstat ? lstat : stat;
16438
17446
  if (wantBigintFsStats) {
16439
- this._stat = (path25) => statMethod(path25, { bigint: true });
17447
+ this._stat = (path26) => statMethod(path26, { bigint: true });
16440
17448
  } else {
16441
17449
  this._stat = statMethod;
16442
17450
  }
@@ -16461,8 +17469,8 @@ var ReaddirpStream = class extends Readable {
16461
17469
  const par = this.parent;
16462
17470
  const fil = par && par.files;
16463
17471
  if (fil && fil.length > 0) {
16464
- const { path: path25, depth } = par;
16465
- 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));
16466
17474
  const awaited = await Promise.all(slice);
16467
17475
  for (const entry of awaited) {
16468
17476
  if (!entry)
@@ -16502,21 +17510,21 @@ var ReaddirpStream = class extends Readable {
16502
17510
  this.reading = false;
16503
17511
  }
16504
17512
  }
16505
- async _exploreDir(path25, depth) {
17513
+ async _exploreDir(path26, depth) {
16506
17514
  let files;
16507
17515
  try {
16508
- files = await readdir(path25, this._rdOptions);
17516
+ files = await readdir(path26, this._rdOptions);
16509
17517
  } catch (error) {
16510
17518
  this._onError(error);
16511
17519
  }
16512
- return { files, depth, path: path25 };
17520
+ return { files, depth, path: path26 };
16513
17521
  }
16514
- async _formatEntry(dirent, path25) {
17522
+ async _formatEntry(dirent, path26) {
16515
17523
  let entry;
16516
- const basename7 = this._isDirent ? dirent.name : dirent;
17524
+ const basename8 = this._isDirent ? dirent.name : dirent;
16517
17525
  try {
16518
- const fullPath = presolve(pjoin(path25, basename7));
16519
- 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 };
16520
17528
  entry[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath);
16521
17529
  } catch (err) {
16522
17530
  this._onError(err);
@@ -16915,16 +17923,16 @@ var delFromSet = (main, prop, item) => {
16915
17923
  };
16916
17924
  var isEmptySet = (val) => val instanceof Set ? val.size === 0 : !val;
16917
17925
  var FsWatchInstances = /* @__PURE__ */ new Map();
16918
- function createFsWatchInstance(path25, options, listener, errHandler, emitRaw) {
17926
+ function createFsWatchInstance(path26, options, listener, errHandler, emitRaw) {
16919
17927
  const handleEvent = (rawEvent, evPath) => {
16920
- listener(path25);
16921
- emitRaw(rawEvent, evPath, { watchedPath: path25 });
16922
- if (evPath && path25 !== evPath) {
16923
- 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));
16924
17932
  }
16925
17933
  };
16926
17934
  try {
16927
- return fs_watch(path25, {
17935
+ return fs_watch(path26, {
16928
17936
  persistent: options.persistent
16929
17937
  }, handleEvent);
16930
17938
  } catch (error) {
@@ -16940,12 +17948,12 @@ var fsWatchBroadcast = (fullPath, listenerType, val1, val2, val3) => {
16940
17948
  listener(val1, val2, val3);
16941
17949
  });
16942
17950
  };
16943
- var setFsWatchListener = (path25, fullPath, options, handlers) => {
17951
+ var setFsWatchListener = (path26, fullPath, options, handlers) => {
16944
17952
  const { listener, errHandler, rawEmitter } = handlers;
16945
17953
  let cont = FsWatchInstances.get(fullPath);
16946
17954
  let watcher;
16947
17955
  if (!options.persistent) {
16948
- watcher = createFsWatchInstance(path25, options, listener, errHandler, rawEmitter);
17956
+ watcher = createFsWatchInstance(path26, options, listener, errHandler, rawEmitter);
16949
17957
  if (!watcher)
16950
17958
  return;
16951
17959
  return watcher.close.bind(watcher);
@@ -16956,7 +17964,7 @@ var setFsWatchListener = (path25, fullPath, options, handlers) => {
16956
17964
  addAndConvert(cont, KEY_RAW, rawEmitter);
16957
17965
  } else {
16958
17966
  watcher = createFsWatchInstance(
16959
- path25,
17967
+ path26,
16960
17968
  options,
16961
17969
  fsWatchBroadcast.bind(null, fullPath, KEY_LISTENERS),
16962
17970
  errHandler,
@@ -16971,7 +17979,7 @@ var setFsWatchListener = (path25, fullPath, options, handlers) => {
16971
17979
  cont.watcherUnusable = true;
16972
17980
  if (isWindows && error.code === "EPERM") {
16973
17981
  try {
16974
- const fd = await open(path25, "r");
17982
+ const fd = await open(path26, "r");
16975
17983
  await fd.close();
16976
17984
  broadcastErr(error);
16977
17985
  } catch (err) {
@@ -17002,7 +18010,7 @@ var setFsWatchListener = (path25, fullPath, options, handlers) => {
17002
18010
  };
17003
18011
  };
17004
18012
  var FsWatchFileInstances = /* @__PURE__ */ new Map();
17005
- var setFsWatchFileListener = (path25, fullPath, options, handlers) => {
18013
+ var setFsWatchFileListener = (path26, fullPath, options, handlers) => {
17006
18014
  const { listener, rawEmitter } = handlers;
17007
18015
  let cont = FsWatchFileInstances.get(fullPath);
17008
18016
  const copts = cont && cont.options;
@@ -17024,7 +18032,7 @@ var setFsWatchFileListener = (path25, fullPath, options, handlers) => {
17024
18032
  });
17025
18033
  const currmtime = curr.mtimeMs;
17026
18034
  if (curr.size !== prev.size || currmtime > prev.mtimeMs || currmtime === 0) {
17027
- foreach(cont.listeners, (listener2) => listener2(path25, curr));
18035
+ foreach(cont.listeners, (listener2) => listener2(path26, curr));
17028
18036
  }
17029
18037
  })
17030
18038
  };
@@ -17054,13 +18062,13 @@ var NodeFsHandler = class {
17054
18062
  * @param listener on fs change
17055
18063
  * @returns closer for the watcher instance
17056
18064
  */
17057
- _watchWithNodeFs(path25, listener) {
18065
+ _watchWithNodeFs(path26, listener) {
17058
18066
  const opts = this.fsw.options;
17059
- const directory = sp.dirname(path25);
17060
- const basename7 = sp.basename(path25);
18067
+ const directory = sp.dirname(path26);
18068
+ const basename8 = sp.basename(path26);
17061
18069
  const parent = this.fsw._getWatchedDir(directory);
17062
- parent.add(basename7);
17063
- const absolutePath = sp.resolve(path25);
18070
+ parent.add(basename8);
18071
+ const absolutePath = sp.resolve(path26);
17064
18072
  const options = {
17065
18073
  persistent: opts.persistent
17066
18074
  };
@@ -17069,13 +18077,13 @@ var NodeFsHandler = class {
17069
18077
  let closer;
17070
18078
  if (opts.usePolling) {
17071
18079
  const enableBin = opts.interval !== opts.binaryInterval;
17072
- options.interval = enableBin && isBinaryPath(basename7) ? opts.binaryInterval : opts.interval;
17073
- closer = setFsWatchFileListener(path25, absolutePath, options, {
18080
+ options.interval = enableBin && isBinaryPath(basename8) ? opts.binaryInterval : opts.interval;
18081
+ closer = setFsWatchFileListener(path26, absolutePath, options, {
17074
18082
  listener,
17075
18083
  rawEmitter: this.fsw._emitRaw
17076
18084
  });
17077
18085
  } else {
17078
- closer = setFsWatchListener(path25, absolutePath, options, {
18086
+ closer = setFsWatchListener(path26, absolutePath, options, {
17079
18087
  listener,
17080
18088
  errHandler: this._boundHandleError,
17081
18089
  rawEmitter: this.fsw._emitRaw
@@ -17091,13 +18099,13 @@ var NodeFsHandler = class {
17091
18099
  if (this.fsw.closed) {
17092
18100
  return;
17093
18101
  }
17094
- const dirname13 = sp.dirname(file);
17095
- const basename7 = sp.basename(file);
17096
- 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);
17097
18105
  let prevStats = stats;
17098
- if (parent.has(basename7))
18106
+ if (parent.has(basename8))
17099
18107
  return;
17100
- const listener = async (path25, newStats) => {
18108
+ const listener = async (path26, newStats) => {
17101
18109
  if (!this.fsw._throttle(THROTTLE_MODE_WATCH, file, 5))
17102
18110
  return;
17103
18111
  if (!newStats || newStats.mtimeMs === 0) {
@@ -17111,18 +18119,18 @@ var NodeFsHandler = class {
17111
18119
  this.fsw._emit(EV.CHANGE, file, newStats2);
17112
18120
  }
17113
18121
  if ((isMacos || isLinux || isFreeBSD) && prevStats.ino !== newStats2.ino) {
17114
- this.fsw._closeFile(path25);
18122
+ this.fsw._closeFile(path26);
17115
18123
  prevStats = newStats2;
17116
18124
  const closer2 = this._watchWithNodeFs(file, listener);
17117
18125
  if (closer2)
17118
- this.fsw._addPathCloser(path25, closer2);
18126
+ this.fsw._addPathCloser(path26, closer2);
17119
18127
  } else {
17120
18128
  prevStats = newStats2;
17121
18129
  }
17122
18130
  } catch (error) {
17123
- this.fsw._remove(dirname13, basename7);
18131
+ this.fsw._remove(dirname14, basename8);
17124
18132
  }
17125
- } else if (parent.has(basename7)) {
18133
+ } else if (parent.has(basename8)) {
17126
18134
  const at = newStats.atimeMs;
17127
18135
  const mt = newStats.mtimeMs;
17128
18136
  if (!at || at <= mt || mt !== prevStats.mtimeMs) {
@@ -17147,7 +18155,7 @@ var NodeFsHandler = class {
17147
18155
  * @param item basename of this item
17148
18156
  * @returns true if no more processing is needed for this entry.
17149
18157
  */
17150
- async _handleSymlink(entry, directory, path25, item) {
18158
+ async _handleSymlink(entry, directory, path26, item) {
17151
18159
  if (this.fsw.closed) {
17152
18160
  return;
17153
18161
  }
@@ -17157,7 +18165,7 @@ var NodeFsHandler = class {
17157
18165
  this.fsw._incrReadyCount();
17158
18166
  let linkPath;
17159
18167
  try {
17160
- linkPath = await fsrealpath(path25);
18168
+ linkPath = await fsrealpath(path26);
17161
18169
  } catch (e) {
17162
18170
  this.fsw._emitReady();
17163
18171
  return true;
@@ -17167,12 +18175,12 @@ var NodeFsHandler = class {
17167
18175
  if (dir.has(item)) {
17168
18176
  if (this.fsw._symlinkPaths.get(full) !== linkPath) {
17169
18177
  this.fsw._symlinkPaths.set(full, linkPath);
17170
- this.fsw._emit(EV.CHANGE, path25, entry.stats);
18178
+ this.fsw._emit(EV.CHANGE, path26, entry.stats);
17171
18179
  }
17172
18180
  } else {
17173
18181
  dir.add(item);
17174
18182
  this.fsw._symlinkPaths.set(full, linkPath);
17175
- this.fsw._emit(EV.ADD, path25, entry.stats);
18183
+ this.fsw._emit(EV.ADD, path26, entry.stats);
17176
18184
  }
17177
18185
  this.fsw._emitReady();
17178
18186
  return true;
@@ -17202,9 +18210,9 @@ var NodeFsHandler = class {
17202
18210
  return;
17203
18211
  }
17204
18212
  const item = entry.path;
17205
- let path25 = sp.join(directory, item);
18213
+ let path26 = sp.join(directory, item);
17206
18214
  current.add(item);
17207
- if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path25, item)) {
18215
+ if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path26, item)) {
17208
18216
  return;
17209
18217
  }
17210
18218
  if (this.fsw.closed) {
@@ -17213,11 +18221,11 @@ var NodeFsHandler = class {
17213
18221
  }
17214
18222
  if (item === target || !target && !previous.has(item)) {
17215
18223
  this.fsw._incrReadyCount();
17216
- path25 = sp.join(dir, sp.relative(dir, path25));
17217
- this._addToNodeFs(path25, initialAdd, wh, depth + 1);
18224
+ path26 = sp.join(dir, sp.relative(dir, path26));
18225
+ this._addToNodeFs(path26, initialAdd, wh, depth + 1);
17218
18226
  }
17219
18227
  }).on(EV.ERROR, this._boundHandleError);
17220
- return new Promise((resolve17, reject) => {
18228
+ return new Promise((resolve18, reject) => {
17221
18229
  if (!stream)
17222
18230
  return reject();
17223
18231
  stream.once(STR_END, () => {
@@ -17226,7 +18234,7 @@ var NodeFsHandler = class {
17226
18234
  return;
17227
18235
  }
17228
18236
  const wasThrottled = throttler ? throttler.clear() : false;
17229
- resolve17(void 0);
18237
+ resolve18(void 0);
17230
18238
  previous.getChildren().filter((item) => {
17231
18239
  return item !== directory && !current.has(item);
17232
18240
  }).forEach((item) => {
@@ -17283,13 +18291,13 @@ var NodeFsHandler = class {
17283
18291
  * @param depth Child path actually targeted for watch
17284
18292
  * @param target Child path actually targeted for watch
17285
18293
  */
17286
- async _addToNodeFs(path25, initialAdd, priorWh, depth, target) {
18294
+ async _addToNodeFs(path26, initialAdd, priorWh, depth, target) {
17287
18295
  const ready = this.fsw._emitReady;
17288
- if (this.fsw._isIgnored(path25) || this.fsw.closed) {
18296
+ if (this.fsw._isIgnored(path26) || this.fsw.closed) {
17289
18297
  ready();
17290
18298
  return false;
17291
18299
  }
17292
- const wh = this.fsw._getWatchHelpers(path25);
18300
+ const wh = this.fsw._getWatchHelpers(path26);
17293
18301
  if (priorWh) {
17294
18302
  wh.filterPath = (entry) => priorWh.filterPath(entry);
17295
18303
  wh.filterDir = (entry) => priorWh.filterDir(entry);
@@ -17305,8 +18313,8 @@ var NodeFsHandler = class {
17305
18313
  const follow = this.fsw.options.followSymlinks;
17306
18314
  let closer;
17307
18315
  if (stats.isDirectory()) {
17308
- const absPath = sp.resolve(path25);
17309
- const targetPath = follow ? await fsrealpath(path25) : path25;
18316
+ const absPath = sp.resolve(path26);
18317
+ const targetPath = follow ? await fsrealpath(path26) : path26;
17310
18318
  if (this.fsw.closed)
17311
18319
  return;
17312
18320
  closer = await this._handleDir(wh.watchPath, stats, initialAdd, depth, target, wh, targetPath);
@@ -17316,29 +18324,29 @@ var NodeFsHandler = class {
17316
18324
  this.fsw._symlinkPaths.set(absPath, targetPath);
17317
18325
  }
17318
18326
  } else if (stats.isSymbolicLink()) {
17319
- const targetPath = follow ? await fsrealpath(path25) : path25;
18327
+ const targetPath = follow ? await fsrealpath(path26) : path26;
17320
18328
  if (this.fsw.closed)
17321
18329
  return;
17322
18330
  const parent = sp.dirname(wh.watchPath);
17323
18331
  this.fsw._getWatchedDir(parent).add(wh.watchPath);
17324
18332
  this.fsw._emit(EV.ADD, wh.watchPath, stats);
17325
- closer = await this._handleDir(parent, stats, initialAdd, depth, path25, wh, targetPath);
18333
+ closer = await this._handleDir(parent, stats, initialAdd, depth, path26, wh, targetPath);
17326
18334
  if (this.fsw.closed)
17327
18335
  return;
17328
18336
  if (targetPath !== void 0) {
17329
- this.fsw._symlinkPaths.set(sp.resolve(path25), targetPath);
18337
+ this.fsw._symlinkPaths.set(sp.resolve(path26), targetPath);
17330
18338
  }
17331
18339
  } else {
17332
18340
  closer = this._handleFile(wh.watchPath, stats, initialAdd);
17333
18341
  }
17334
18342
  ready();
17335
18343
  if (closer)
17336
- this.fsw._addPathCloser(path25, closer);
18344
+ this.fsw._addPathCloser(path26, closer);
17337
18345
  return false;
17338
18346
  } catch (error) {
17339
18347
  if (this.fsw._handleError(error)) {
17340
18348
  ready();
17341
- return path25;
18349
+ return path26;
17342
18350
  }
17343
18351
  }
17344
18352
  }
@@ -17381,24 +18389,24 @@ function createPattern(matcher) {
17381
18389
  }
17382
18390
  return () => false;
17383
18391
  }
17384
- function normalizePath2(path25) {
17385
- if (typeof path25 !== "string")
18392
+ function normalizePath2(path26) {
18393
+ if (typeof path26 !== "string")
17386
18394
  throw new Error("string expected");
17387
- path25 = sp2.normalize(path25);
17388
- path25 = path25.replace(/\\/g, "/");
18395
+ path26 = sp2.normalize(path26);
18396
+ path26 = path26.replace(/\\/g, "/");
17389
18397
  let prepend = false;
17390
- if (path25.startsWith("//"))
18398
+ if (path26.startsWith("//"))
17391
18399
  prepend = true;
17392
- path25 = path25.replace(DOUBLE_SLASH_RE, "/");
18400
+ path26 = path26.replace(DOUBLE_SLASH_RE, "/");
17393
18401
  if (prepend)
17394
- path25 = "/" + path25;
17395
- return path25;
18402
+ path26 = "/" + path26;
18403
+ return path26;
17396
18404
  }
17397
18405
  function matchPatterns(patterns, testString, stats) {
17398
- const path25 = normalizePath2(testString);
18406
+ const path26 = normalizePath2(testString);
17399
18407
  for (let index = 0; index < patterns.length; index++) {
17400
18408
  const pattern = patterns[index];
17401
- if (pattern(path25, stats)) {
18409
+ if (pattern(path26, stats)) {
17402
18410
  return true;
17403
18411
  }
17404
18412
  }
@@ -17436,19 +18444,19 @@ var toUnix = (string) => {
17436
18444
  }
17437
18445
  return str;
17438
18446
  };
17439
- var normalizePathToUnix = (path25) => toUnix(sp2.normalize(toUnix(path25)));
17440
- var normalizeIgnored = (cwd = "") => (path25) => {
17441
- if (typeof path25 === "string") {
17442
- 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));
17443
18451
  } else {
17444
- return path25;
18452
+ return path26;
17445
18453
  }
17446
18454
  };
17447
- var getAbsolutePath = (path25, cwd) => {
17448
- if (sp2.isAbsolute(path25)) {
17449
- return path25;
18455
+ var getAbsolutePath = (path26, cwd) => {
18456
+ if (sp2.isAbsolute(path26)) {
18457
+ return path26;
17450
18458
  }
17451
- return sp2.join(cwd, path25);
18459
+ return sp2.join(cwd, path26);
17452
18460
  };
17453
18461
  var EMPTY_SET = Object.freeze(/* @__PURE__ */ new Set());
17454
18462
  var DirEntry = class {
@@ -17513,10 +18521,10 @@ var WatchHelper = class {
17513
18521
  dirParts;
17514
18522
  followSymlinks;
17515
18523
  statMethod;
17516
- constructor(path25, follow, fsw) {
18524
+ constructor(path26, follow, fsw) {
17517
18525
  this.fsw = fsw;
17518
- const watchPath = path25;
17519
- this.path = path25 = path25.replace(REPLACER_RE, "");
18526
+ const watchPath = path26;
18527
+ this.path = path26 = path26.replace(REPLACER_RE, "");
17520
18528
  this.watchPath = watchPath;
17521
18529
  this.fullWatchPath = sp2.resolve(watchPath);
17522
18530
  this.dirParts = [];
@@ -17656,20 +18664,20 @@ var FSWatcher = class extends EventEmitter2 {
17656
18664
  this._closePromise = void 0;
17657
18665
  let paths = unifyPaths(paths_);
17658
18666
  if (cwd) {
17659
- paths = paths.map((path25) => {
17660
- const absPath = getAbsolutePath(path25, cwd);
18667
+ paths = paths.map((path26) => {
18668
+ const absPath = getAbsolutePath(path26, cwd);
17661
18669
  return absPath;
17662
18670
  });
17663
18671
  }
17664
- paths.forEach((path25) => {
17665
- this._removeIgnoredPath(path25);
18672
+ paths.forEach((path26) => {
18673
+ this._removeIgnoredPath(path26);
17666
18674
  });
17667
18675
  this._userIgnored = void 0;
17668
18676
  if (!this._readyCount)
17669
18677
  this._readyCount = 0;
17670
18678
  this._readyCount += paths.length;
17671
- Promise.all(paths.map(async (path25) => {
17672
- 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);
17673
18681
  if (res)
17674
18682
  this._emitReady();
17675
18683
  return res;
@@ -17691,17 +18699,17 @@ var FSWatcher = class extends EventEmitter2 {
17691
18699
  return this;
17692
18700
  const paths = unifyPaths(paths_);
17693
18701
  const { cwd } = this.options;
17694
- paths.forEach((path25) => {
17695
- if (!sp2.isAbsolute(path25) && !this._closers.has(path25)) {
18702
+ paths.forEach((path26) => {
18703
+ if (!sp2.isAbsolute(path26) && !this._closers.has(path26)) {
17696
18704
  if (cwd)
17697
- path25 = sp2.join(cwd, path25);
17698
- path25 = sp2.resolve(path25);
18705
+ path26 = sp2.join(cwd, path26);
18706
+ path26 = sp2.resolve(path26);
17699
18707
  }
17700
- this._closePath(path25);
17701
- this._addIgnoredPath(path25);
17702
- if (this._watched.has(path25)) {
18708
+ this._closePath(path26);
18709
+ this._addIgnoredPath(path26);
18710
+ if (this._watched.has(path26)) {
17703
18711
  this._addIgnoredPath({
17704
- path: path25,
18712
+ path: path26,
17705
18713
  recursive: true
17706
18714
  });
17707
18715
  }
@@ -17765,38 +18773,38 @@ var FSWatcher = class extends EventEmitter2 {
17765
18773
  * @param stats arguments to be passed with event
17766
18774
  * @returns the error if defined, otherwise the value of the FSWatcher instance's `closed` flag
17767
18775
  */
17768
- async _emit(event, path25, stats) {
18776
+ async _emit(event, path26, stats) {
17769
18777
  if (this.closed)
17770
18778
  return;
17771
18779
  const opts = this.options;
17772
18780
  if (isWindows)
17773
- path25 = sp2.normalize(path25);
18781
+ path26 = sp2.normalize(path26);
17774
18782
  if (opts.cwd)
17775
- path25 = sp2.relative(opts.cwd, path25);
17776
- const args = [path25];
18783
+ path26 = sp2.relative(opts.cwd, path26);
18784
+ const args = [path26];
17777
18785
  if (stats != null)
17778
18786
  args.push(stats);
17779
18787
  const awf = opts.awaitWriteFinish;
17780
18788
  let pw;
17781
- if (awf && (pw = this._pendingWrites.get(path25))) {
18789
+ if (awf && (pw = this._pendingWrites.get(path26))) {
17782
18790
  pw.lastChange = /* @__PURE__ */ new Date();
17783
18791
  return this;
17784
18792
  }
17785
18793
  if (opts.atomic) {
17786
18794
  if (event === EVENTS.UNLINK) {
17787
- this._pendingUnlinks.set(path25, [event, ...args]);
18795
+ this._pendingUnlinks.set(path26, [event, ...args]);
17788
18796
  setTimeout(() => {
17789
- this._pendingUnlinks.forEach((entry, path26) => {
18797
+ this._pendingUnlinks.forEach((entry, path27) => {
17790
18798
  this.emit(...entry);
17791
18799
  this.emit(EVENTS.ALL, ...entry);
17792
- this._pendingUnlinks.delete(path26);
18800
+ this._pendingUnlinks.delete(path27);
17793
18801
  });
17794
18802
  }, typeof opts.atomic === "number" ? opts.atomic : 100);
17795
18803
  return this;
17796
18804
  }
17797
- if (event === EVENTS.ADD && this._pendingUnlinks.has(path25)) {
18805
+ if (event === EVENTS.ADD && this._pendingUnlinks.has(path26)) {
17798
18806
  event = EVENTS.CHANGE;
17799
- this._pendingUnlinks.delete(path25);
18807
+ this._pendingUnlinks.delete(path26);
17800
18808
  }
17801
18809
  }
17802
18810
  if (awf && (event === EVENTS.ADD || event === EVENTS.CHANGE) && this._readyEmitted) {
@@ -17814,16 +18822,16 @@ var FSWatcher = class extends EventEmitter2 {
17814
18822
  this.emitWithAll(event, args);
17815
18823
  }
17816
18824
  };
17817
- this._awaitWriteFinish(path25, awf.stabilityThreshold, event, awfEmit);
18825
+ this._awaitWriteFinish(path26, awf.stabilityThreshold, event, awfEmit);
17818
18826
  return this;
17819
18827
  }
17820
18828
  if (event === EVENTS.CHANGE) {
17821
- const isThrottled = !this._throttle(EVENTS.CHANGE, path25, 50);
18829
+ const isThrottled = !this._throttle(EVENTS.CHANGE, path26, 50);
17822
18830
  if (isThrottled)
17823
18831
  return this;
17824
18832
  }
17825
18833
  if (opts.alwaysStat && stats === void 0 && (event === EVENTS.ADD || event === EVENTS.ADD_DIR || event === EVENTS.CHANGE)) {
17826
- const fullPath = opts.cwd ? sp2.join(opts.cwd, path25) : path25;
18834
+ const fullPath = opts.cwd ? sp2.join(opts.cwd, path26) : path26;
17827
18835
  let stats2;
17828
18836
  try {
17829
18837
  stats2 = await stat3(fullPath);
@@ -17854,23 +18862,23 @@ var FSWatcher = class extends EventEmitter2 {
17854
18862
  * @param timeout duration of time to suppress duplicate actions
17855
18863
  * @returns tracking object or false if action should be suppressed
17856
18864
  */
17857
- _throttle(actionType, path25, timeout) {
18865
+ _throttle(actionType, path26, timeout) {
17858
18866
  if (!this._throttled.has(actionType)) {
17859
18867
  this._throttled.set(actionType, /* @__PURE__ */ new Map());
17860
18868
  }
17861
18869
  const action = this._throttled.get(actionType);
17862
18870
  if (!action)
17863
18871
  throw new Error("invalid throttle");
17864
- const actionPath = action.get(path25);
18872
+ const actionPath = action.get(path26);
17865
18873
  if (actionPath) {
17866
18874
  actionPath.count++;
17867
18875
  return false;
17868
18876
  }
17869
18877
  let timeoutObject;
17870
18878
  const clear = () => {
17871
- const item = action.get(path25);
18879
+ const item = action.get(path26);
17872
18880
  const count = item ? item.count : 0;
17873
- action.delete(path25);
18881
+ action.delete(path26);
17874
18882
  clearTimeout(timeoutObject);
17875
18883
  if (item)
17876
18884
  clearTimeout(item.timeoutObject);
@@ -17878,7 +18886,7 @@ var FSWatcher = class extends EventEmitter2 {
17878
18886
  };
17879
18887
  timeoutObject = setTimeout(clear, timeout);
17880
18888
  const thr = { timeoutObject, clear, count: 0 };
17881
- action.set(path25, thr);
18889
+ action.set(path26, thr);
17882
18890
  return thr;
17883
18891
  }
17884
18892
  _incrReadyCount() {
@@ -17892,44 +18900,44 @@ var FSWatcher = class extends EventEmitter2 {
17892
18900
  * @param event
17893
18901
  * @param awfEmit Callback to be called when ready for event to be emitted.
17894
18902
  */
17895
- _awaitWriteFinish(path25, threshold, event, awfEmit) {
18903
+ _awaitWriteFinish(path26, threshold, event, awfEmit) {
17896
18904
  const awf = this.options.awaitWriteFinish;
17897
18905
  if (typeof awf !== "object")
17898
18906
  return;
17899
18907
  const pollInterval = awf.pollInterval;
17900
18908
  let timeoutHandler;
17901
- let fullPath = path25;
17902
- if (this.options.cwd && !sp2.isAbsolute(path25)) {
17903
- 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);
17904
18912
  }
17905
18913
  const now2 = /* @__PURE__ */ new Date();
17906
18914
  const writes = this._pendingWrites;
17907
18915
  function awaitWriteFinishFn(prevStat) {
17908
18916
  statcb(fullPath, (err, curStat) => {
17909
- if (err || !writes.has(path25)) {
18917
+ if (err || !writes.has(path26)) {
17910
18918
  if (err && err.code !== "ENOENT")
17911
18919
  awfEmit(err);
17912
18920
  return;
17913
18921
  }
17914
18922
  const now3 = Number(/* @__PURE__ */ new Date());
17915
18923
  if (prevStat && curStat.size !== prevStat.size) {
17916
- writes.get(path25).lastChange = now3;
18924
+ writes.get(path26).lastChange = now3;
17917
18925
  }
17918
- const pw = writes.get(path25);
18926
+ const pw = writes.get(path26);
17919
18927
  const df = now3 - pw.lastChange;
17920
18928
  if (df >= threshold) {
17921
- writes.delete(path25);
18929
+ writes.delete(path26);
17922
18930
  awfEmit(void 0, curStat);
17923
18931
  } else {
17924
18932
  timeoutHandler = setTimeout(awaitWriteFinishFn, pollInterval, curStat);
17925
18933
  }
17926
18934
  });
17927
18935
  }
17928
- if (!writes.has(path25)) {
17929
- writes.set(path25, {
18936
+ if (!writes.has(path26)) {
18937
+ writes.set(path26, {
17930
18938
  lastChange: now2,
17931
18939
  cancelWait: () => {
17932
- writes.delete(path25);
18940
+ writes.delete(path26);
17933
18941
  clearTimeout(timeoutHandler);
17934
18942
  return event;
17935
18943
  }
@@ -17940,8 +18948,8 @@ var FSWatcher = class extends EventEmitter2 {
17940
18948
  /**
17941
18949
  * Determines whether user has asked to ignore this path.
17942
18950
  */
17943
- _isIgnored(path25, stats) {
17944
- if (this.options.atomic && DOT_RE.test(path25))
18951
+ _isIgnored(path26, stats) {
18952
+ if (this.options.atomic && DOT_RE.test(path26))
17945
18953
  return true;
17946
18954
  if (!this._userIgnored) {
17947
18955
  const { cwd } = this.options;
@@ -17951,17 +18959,17 @@ var FSWatcher = class extends EventEmitter2 {
17951
18959
  const list = [...ignoredPaths.map(normalizeIgnored(cwd)), ...ignored];
17952
18960
  this._userIgnored = anymatch(list, void 0);
17953
18961
  }
17954
- return this._userIgnored(path25, stats);
18962
+ return this._userIgnored(path26, stats);
17955
18963
  }
17956
- _isntIgnored(path25, stat5) {
17957
- return !this._isIgnored(path25, stat5);
18964
+ _isntIgnored(path26, stat5) {
18965
+ return !this._isIgnored(path26, stat5);
17958
18966
  }
17959
18967
  /**
17960
18968
  * Provides a set of common helpers and properties relating to symlink handling.
17961
18969
  * @param path file or directory pattern being watched
17962
18970
  */
17963
- _getWatchHelpers(path25) {
17964
- return new WatchHelper(path25, this.options.followSymlinks, this);
18971
+ _getWatchHelpers(path26) {
18972
+ return new WatchHelper(path26, this.options.followSymlinks, this);
17965
18973
  }
17966
18974
  // Directory helpers
17967
18975
  // -----------------
@@ -17993,63 +19001,63 @@ var FSWatcher = class extends EventEmitter2 {
17993
19001
  * @param item base path of item/directory
17994
19002
  */
17995
19003
  _remove(directory, item, isDirectory) {
17996
- const path25 = sp2.join(directory, item);
17997
- const fullPath = sp2.resolve(path25);
17998
- isDirectory = isDirectory != null ? isDirectory : this._watched.has(path25) || this._watched.has(fullPath);
17999
- 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))
18000
19008
  return;
18001
19009
  if (!isDirectory && this._watched.size === 1) {
18002
19010
  this.add(directory, item, true);
18003
19011
  }
18004
- const wp = this._getWatchedDir(path25);
19012
+ const wp = this._getWatchedDir(path26);
18005
19013
  const nestedDirectoryChildren = wp.getChildren();
18006
- nestedDirectoryChildren.forEach((nested) => this._remove(path25, nested));
19014
+ nestedDirectoryChildren.forEach((nested) => this._remove(path26, nested));
18007
19015
  const parent = this._getWatchedDir(directory);
18008
19016
  const wasTracked = parent.has(item);
18009
19017
  parent.remove(item);
18010
19018
  if (this._symlinkPaths.has(fullPath)) {
18011
19019
  this._symlinkPaths.delete(fullPath);
18012
19020
  }
18013
- let relPath = path25;
19021
+ let relPath = path26;
18014
19022
  if (this.options.cwd)
18015
- relPath = sp2.relative(this.options.cwd, path25);
19023
+ relPath = sp2.relative(this.options.cwd, path26);
18016
19024
  if (this.options.awaitWriteFinish && this._pendingWrites.has(relPath)) {
18017
19025
  const event = this._pendingWrites.get(relPath).cancelWait();
18018
19026
  if (event === EVENTS.ADD)
18019
19027
  return;
18020
19028
  }
18021
- this._watched.delete(path25);
19029
+ this._watched.delete(path26);
18022
19030
  this._watched.delete(fullPath);
18023
19031
  const eventName = isDirectory ? EVENTS.UNLINK_DIR : EVENTS.UNLINK;
18024
- if (wasTracked && !this._isIgnored(path25))
18025
- this._emit(eventName, path25);
18026
- this._closePath(path25);
19032
+ if (wasTracked && !this._isIgnored(path26))
19033
+ this._emit(eventName, path26);
19034
+ this._closePath(path26);
18027
19035
  }
18028
19036
  /**
18029
19037
  * Closes all watchers for a path
18030
19038
  */
18031
- _closePath(path25) {
18032
- this._closeFile(path25);
18033
- const dir = sp2.dirname(path25);
18034
- 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));
18035
19043
  }
18036
19044
  /**
18037
19045
  * Closes only file-specific watchers
18038
19046
  */
18039
- _closeFile(path25) {
18040
- const closers = this._closers.get(path25);
19047
+ _closeFile(path26) {
19048
+ const closers = this._closers.get(path26);
18041
19049
  if (!closers)
18042
19050
  return;
18043
19051
  closers.forEach((closer) => closer());
18044
- this._closers.delete(path25);
19052
+ this._closers.delete(path26);
18045
19053
  }
18046
- _addPathCloser(path25, closer) {
19054
+ _addPathCloser(path26, closer) {
18047
19055
  if (!closer)
18048
19056
  return;
18049
- let list = this._closers.get(path25);
19057
+ let list = this._closers.get(path26);
18050
19058
  if (!list) {
18051
19059
  list = [];
18052
- this._closers.set(path25, list);
19060
+ this._closers.set(path26, list);
18053
19061
  }
18054
19062
  list.push(closer);
18055
19063
  }
@@ -18079,11 +19087,11 @@ function watch(paths, options = {}) {
18079
19087
  var chokidar_default = { watch, FSWatcher };
18080
19088
 
18081
19089
  // src/watcher/file-watcher.ts
18082
- import * as path23 from "path";
19090
+ import * as path24 from "path";
18083
19091
 
18084
19092
  // src/watcher/native-recursive-watcher.ts
18085
19093
  import { watch as watch2 } from "fs";
18086
- import * as path21 from "path";
19094
+ import * as path22 from "path";
18087
19095
  var NativeRecursiveWatcher = class {
18088
19096
  constructor(root, onChange, options = {}) {
18089
19097
  this.root = root;
@@ -18131,9 +19139,9 @@ var NativeRecursiveWatcher = class {
18131
19139
  toAbsolutePath(filename) {
18132
19140
  if (filename == null) return null;
18133
19141
  const normalizedFilename = typeof filename === "string" ? filename : filename.toString();
18134
- const absolutePath = path21.resolve(this.root, normalizedFilename);
18135
- const relativePath = path21.relative(this.root, absolutePath);
18136
- 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);
18137
19145
  return outsideRoot ? null : absolutePath;
18138
19146
  }
18139
19147
  defaultWatchFactory = (root, listener, options) => watch2(root, options, listener);
@@ -18141,16 +19149,16 @@ var NativeRecursiveWatcher = class {
18141
19149
 
18142
19150
  // src/watcher/snapshot.ts
18143
19151
  import * as fsPromises4 from "fs/promises";
18144
- import * as path22 from "path";
19152
+ import * as path23 from "path";
18145
19153
  async function buildFileSnapshotScan(projectRoot3, config, configPaths = []) {
18146
- const normalizedProjectRoot = path22.resolve(projectRoot3);
19154
+ const normalizedProjectRoot = path23.resolve(projectRoot3);
18147
19155
  const ignoreFilter = createIgnoreFilter(normalizedProjectRoot);
18148
19156
  const includePatterns = [...config.include, ...config.additionalInclude ?? []];
18149
19157
  const maxDepth = config.indexing?.maxDepth ?? -1;
18150
19158
  const snapshot = /* @__PURE__ */ new Map();
18151
19159
  const unreadablePrefixes = /* @__PURE__ */ new Set();
18152
19160
  const includeFile = async (filePath) => {
18153
- const normalizedPath2 = path22.resolve(filePath);
19161
+ const normalizedPath2 = path23.resolve(filePath);
18154
19162
  if (!shouldIncludeFile(normalizedPath2, normalizedProjectRoot, includePatterns, config.exclude, ignoreFilter)) return;
18155
19163
  const stat5 = await readStatIfFile(normalizedPath2, unreadablePrefixes);
18156
19164
  if (stat5) snapshot.set(normalizedPath2, { size: stat5.size, mtimeMs: stat5.mtimeMs });
@@ -18162,16 +19170,16 @@ async function buildFileSnapshotScan(projectRoot3, config, configPaths = []) {
18162
19170
  } catch (error) {
18163
19171
  if (isMissingFsError(error)) return;
18164
19172
  if (isPermissionFsError(error)) {
18165
- unreadablePrefixes.add(path22.resolve(directoryPath));
19173
+ unreadablePrefixes.add(path23.resolve(directoryPath));
18166
19174
  return;
18167
19175
  }
18168
19176
  throw error;
18169
19177
  }
18170
19178
  for (const entry of entries) {
18171
- const fullPath = path22.join(directoryPath, entry.name);
18172
- const relativePath = path22.relative(normalizedProjectRoot, fullPath);
19179
+ const fullPath = path23.join(directoryPath, entry.name);
19180
+ const relativePath = path23.relative(normalizedProjectRoot, fullPath);
18173
19181
  if (entry.isDirectory()) {
18174
- if (hasFilteredPathSegment(relativePath, path22.sep) || isRestrictedDirectory(relativePath, path22.sep)) continue;
19182
+ if (hasFilteredPathSegment(relativePath, path23.sep) || isRestrictedDirectory(relativePath, path23.sep)) continue;
18175
19183
  if (ignoreFilter.ignores(relativePath)) continue;
18176
19184
  if (maxDepth === -1 || depth < maxDepth) await walk(fullPath, depth + 1);
18177
19185
  } else if (entry.isFile()) {
@@ -18184,19 +19192,19 @@ async function buildFileSnapshotScan(projectRoot3, config, configPaths = []) {
18184
19192
  return { entries: snapshot, unreadablePrefixes };
18185
19193
  }
18186
19194
  async function buildFileSnapshotForPathScan(projectRoot3, config, configPaths, targetPath) {
18187
- const normalizedProjectRoot = path22.resolve(projectRoot3);
18188
- const normalizedTargetPath = path22.resolve(targetPath);
19195
+ const normalizedProjectRoot = path23.resolve(projectRoot3);
19196
+ const normalizedTargetPath = path23.resolve(targetPath);
18189
19197
  if (!isWithinPath(normalizedProjectRoot, normalizedTargetPath)) {
18190
19198
  return { entries: /* @__PURE__ */ new Map(), unreadablePrefixes: /* @__PURE__ */ new Set() };
18191
19199
  }
18192
19200
  const ignoreFilter = createIgnoreFilter(normalizedProjectRoot);
18193
19201
  const includePatterns = [...config.include, ...config.additionalInclude ?? []];
18194
19202
  const maxDepth = config.indexing?.maxDepth ?? -1;
18195
- const explicitConfigPaths = new Set(configPaths.map((configPath) => path22.resolve(configPath)));
19203
+ const explicitConfigPaths = new Set(configPaths.map((configPath) => path23.resolve(configPath)));
18196
19204
  const snapshot = /* @__PURE__ */ new Map();
18197
19205
  const unreadablePrefixes = /* @__PURE__ */ new Set();
18198
19206
  const includeFile = async (filePath) => {
18199
- const normalizedPath2 = path22.resolve(filePath);
19207
+ const normalizedPath2 = path23.resolve(filePath);
18200
19208
  if (!explicitConfigPaths.has(normalizedPath2) && !shouldIncludeFile(
18201
19209
  normalizedPath2,
18202
19210
  normalizedProjectRoot,
@@ -18214,16 +19222,16 @@ async function buildFileSnapshotForPathScan(projectRoot3, config, configPaths, t
18214
19222
  } catch (error) {
18215
19223
  if (isMissingFsError(error)) return;
18216
19224
  if (isPermissionFsError(error)) {
18217
- unreadablePrefixes.add(path22.resolve(directoryPath));
19225
+ unreadablePrefixes.add(path23.resolve(directoryPath));
18218
19226
  return;
18219
19227
  }
18220
19228
  throw error;
18221
19229
  }
18222
19230
  for (const entry of entries) {
18223
- const fullPath = path22.join(directoryPath, entry.name);
18224
- const relativePath = path22.relative(normalizedProjectRoot, fullPath);
19231
+ const fullPath = path23.join(directoryPath, entry.name);
19232
+ const relativePath = path23.relative(normalizedProjectRoot, fullPath);
18225
19233
  if (entry.isDirectory()) {
18226
- if (hasFilteredPathSegment(relativePath, path22.sep) || isRestrictedDirectory(relativePath, path22.sep)) continue;
19234
+ if (hasFilteredPathSegment(relativePath, path23.sep) || isRestrictedDirectory(relativePath, path23.sep)) continue;
18227
19235
  if (ignoreFilter.ignores(relativePath)) continue;
18228
19236
  if (maxDepth === -1 || depth < maxDepth) await walk(fullPath, depth + 1);
18229
19237
  } else if (entry.isFile()) {
@@ -18247,7 +19255,7 @@ function completeFileSnapshot(previous, scan) {
18247
19255
  return completed;
18248
19256
  }
18249
19257
  async function includeExplicitConfigPaths(snapshot, unreadablePrefixes, configPaths) {
18250
- 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)))]) {
18251
19259
  if (snapshot.has(configPath)) continue;
18252
19260
  const stat5 = await readStatIfFile(configPath, unreadablePrefixes);
18253
19261
  if (stat5) snapshot.set(configPath, { size: stat5.size, mtimeMs: stat5.mtimeMs });
@@ -18257,12 +19265,12 @@ async function includeExplicitConfigPathsInPath(snapshot, unreadablePrefixes, co
18257
19265
  await includeExplicitConfigPaths(
18258
19266
  snapshot,
18259
19267
  unreadablePrefixes,
18260
- configPaths.filter((configPath) => isWithinPath(targetPath, path22.resolve(configPath)))
19268
+ configPaths.filter((configPath) => isWithinPath(targetPath, path23.resolve(configPath)))
18261
19269
  );
18262
19270
  }
18263
19271
  function isWithinPath(parentPath, childPath) {
18264
- const relativePath = path22.relative(parentPath, childPath);
18265
- 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);
18266
19274
  }
18267
19275
  async function readStatIfFile(filePath, unreadablePrefixes) {
18268
19276
  try {
@@ -18271,7 +19279,7 @@ async function readStatIfFile(filePath, unreadablePrefixes) {
18271
19279
  } catch (error) {
18272
19280
  if (isMissingFsError(error)) return null;
18273
19281
  if (isPermissionFsError(error)) {
18274
- unreadablePrefixes.add(path22.resolve(filePath));
19282
+ unreadablePrefixes.add(path23.resolve(filePath));
18275
19283
  return null;
18276
19284
  }
18277
19285
  throw error;
@@ -18408,8 +19416,8 @@ var FileWatcher = class {
18408
19416
  this.createWatcher();
18409
19417
  }
18410
19418
  resetReady() {
18411
- this.readyPromise = new Promise((resolve17) => {
18412
- this.resolveReady = resolve17;
19419
+ this.readyPromise = new Promise((resolve18) => {
19420
+ this.resolveReady = resolve18;
18413
19421
  });
18414
19422
  this.startupReadySignals = 1;
18415
19423
  }
@@ -18440,7 +19448,7 @@ var FileWatcher = class {
18440
19448
  const resolvedWatchTargets = watchTargets ?? this.getFullChokidarWatchTargets();
18441
19449
  const watcherOptions = {
18442
19450
  ignored: (filePath) => {
18443
- const relativePath = path23.relative(this.projectRoot, filePath);
19451
+ const relativePath = path24.relative(this.projectRoot, filePath);
18444
19452
  if (!relativePath) return false;
18445
19453
  if (this.isProjectConfigPathOrAncestor(relativePath)) {
18446
19454
  return false;
@@ -18448,10 +19456,10 @@ var FileWatcher = class {
18448
19456
  if (this.isOutsideProjectPath(relativePath)) {
18449
19457
  return true;
18450
19458
  }
18451
- if (hasFilteredPathSegment(relativePath, path23.sep)) {
19459
+ if (hasFilteredPathSegment(relativePath, path24.sep)) {
18452
19460
  return true;
18453
19461
  }
18454
- if (isRestrictedDirectory(relativePath, path23.sep)) {
19462
+ if (isRestrictedDirectory(relativePath, path24.sep)) {
18455
19463
  return true;
18456
19464
  }
18457
19465
  if (ignoreFilter.ignores(relativePath)) {
@@ -18542,13 +19550,13 @@ var FileWatcher = class {
18542
19550
  getExternalConfigWatchTargets() {
18543
19551
  return [...new Set(
18544
19552
  this.projectConfigPaths.filter((projectConfigPath) => {
18545
- const relativeConfigPath = path23.relative(this.projectRoot, projectConfigPath);
19553
+ const relativeConfigPath = path24.relative(this.projectRoot, projectConfigPath);
18546
19554
  return this.isOutsideProjectPath(relativeConfigPath);
18547
19555
  }).map((projectConfigPath) => {
18548
- if (existsSync13(projectConfigPath)) {
19556
+ if (existsSync14(projectConfigPath)) {
18549
19557
  return projectConfigPath;
18550
19558
  }
18551
- return this.getNearestExistingDirectory(path23.dirname(projectConfigPath));
19559
+ return this.getNearestExistingDirectory(path24.dirname(projectConfigPath));
18552
19560
  })
18553
19561
  )];
18554
19562
  }
@@ -18610,7 +19618,7 @@ var FileWatcher = class {
18610
19618
  }
18611
19619
  scheduleNativeReconciliation(generation, filePath) {
18612
19620
  if (!this.isCurrentNativeSetup(generation)) return;
18613
- const requiresFullReconciliation = filePath === path23.join(this.projectRoot, ".gitignore");
19621
+ const requiresFullReconciliation = filePath === path24.join(this.projectRoot, ".gitignore");
18614
19622
  const invalidatedPath = requiresFullReconciliation ? null : filePath;
18615
19623
  this.nativeInvalidatedPaths.set(invalidatedPath, invalidatedPath !== null);
18616
19624
  if (this.nativeReconcileTimer) {
@@ -18705,23 +19713,23 @@ var FileWatcher = class {
18705
19713
  this.scheduleFlush();
18706
19714
  }
18707
19715
  isProjectConfigPath(filePath) {
18708
- const relativePath = path23.relative(this.projectRoot, filePath);
18709
- const normalizedRelativePath = path23.normalize(relativePath);
19716
+ const relativePath = path24.relative(this.projectRoot, filePath);
19717
+ const normalizedRelativePath = path24.normalize(relativePath);
18710
19718
  return this.getProjectConfigRelativePaths().some((configPath) => configPath === normalizedRelativePath);
18711
19719
  }
18712
19720
  isProjectConfigPathOrAncestor(relativePath) {
18713
- const normalizedRelativePath = path23.normalize(relativePath);
19721
+ const normalizedRelativePath = path24.normalize(relativePath);
18714
19722
  return this.getProjectConfigRelativePaths().some(
18715
- (configPath) => configPath === normalizedRelativePath || configPath.startsWith(`${normalizedRelativePath}${path23.sep}`)
19723
+ (configPath) => configPath === normalizedRelativePath || configPath.startsWith(`${normalizedRelativePath}${path24.sep}`)
18716
19724
  );
18717
19725
  }
18718
19726
  isOutsideProjectPath(relativePath) {
18719
- return relativePath === ".." || relativePath.startsWith(`..${path23.sep}`) || path23.isAbsolute(relativePath);
19727
+ return relativePath === ".." || relativePath.startsWith(`..${path24.sep}`) || path24.isAbsolute(relativePath);
18720
19728
  }
18721
19729
  getNearestExistingDirectory(directoryPath) {
18722
19730
  let candidate = directoryPath;
18723
- while (!existsSync13(candidate)) {
18724
- const parent = path23.dirname(candidate);
19731
+ while (!existsSync14(candidate)) {
19732
+ const parent = path24.dirname(candidate);
18725
19733
  if (parent === candidate) break;
18726
19734
  candidate = parent;
18727
19735
  }
@@ -18729,7 +19737,7 @@ var FileWatcher = class {
18729
19737
  }
18730
19738
  getProjectConfigRelativePaths() {
18731
19739
  return this.projectConfigPaths.map(
18732
- (configPath) => path23.normalize(path23.relative(this.projectRoot, configPath))
19740
+ (configPath) => path24.normalize(path24.relative(this.projectRoot, configPath))
18733
19741
  );
18734
19742
  }
18735
19743
  getConfigPathStates() {
@@ -18787,7 +19795,7 @@ var FileWatcher = class {
18787
19795
  return;
18788
19796
  }
18789
19797
  const changes = Array.from(this.pendingChanges.entries()).map(
18790
- ([path25, type]) => ({ path: path25, type })
19798
+ ([path26, type]) => ({ path: path26, type })
18791
19799
  );
18792
19800
  this.pendingChanges.clear();
18793
19801
  try {
@@ -18833,7 +19841,7 @@ var FileWatcher = class {
18833
19841
  };
18834
19842
 
18835
19843
  // src/watcher/git-head-watcher.ts
18836
- import * as path24 from "path";
19844
+ import * as path25 from "path";
18837
19845
  var GitHeadWatcher = class {
18838
19846
  watcher = null;
18839
19847
  projectRoot;
@@ -18855,13 +19863,13 @@ var GitHeadWatcher = class {
18855
19863
  this.readyPromise = Promise.resolve();
18856
19864
  return;
18857
19865
  }
18858
- this.readyPromise = new Promise((resolve17) => {
18859
- this.resolveReady = resolve17;
19866
+ this.readyPromise = new Promise((resolve18) => {
19867
+ this.resolveReady = resolve18;
18860
19868
  });
18861
19869
  this.onBranchChange = handler;
18862
19870
  this.currentBranch = getCurrentBranch(this.projectRoot);
18863
19871
  const headPath = getHeadPath(this.projectRoot);
18864
- const refsPath = path24.join(this.projectRoot, ".git", "refs", "heads");
19872
+ const refsPath = path25.join(this.projectRoot, ".git", "refs", "heads");
18865
19873
  this.watcher = chokidar_default.watch([headPath, refsPath], {
18866
19874
  persistent: true,
18867
19875
  ignoreInitial: true,
@@ -18929,7 +19937,9 @@ var GitHeadWatcher = class {
18929
19937
  function createWatcherWithIndexer(getIndexer, projectRoot3, config, host, options = {}) {
18930
19938
  const fileWatcher = new FileWatcher(projectRoot3, config, host, options);
18931
19939
  const configPaths = getConfigPaths(projectRoot3, host, options);
18932
- configureAutoIndex(projectRoot3, host, parseConfig(config), getIndexer);
19940
+ configureAutoIndex(projectRoot3, host, parseConfig(config), getIndexer, {
19941
+ synchronizeBackgroundWorker: false
19942
+ });
18933
19943
  let stopped = false;
18934
19944
  const requestReindex = () => {
18935
19945
  if (stopped) return;
@@ -18949,7 +19959,9 @@ function createWatcherWithIndexer(getIndexer, projectRoot3, config, host, option
18949
19959
  const parsedConfig = options.configPath ? parseConfig(loadConfigFile(options.configPath)) : void 0;
18950
19960
  const refreshedConfig = refreshIndexerForDirectory(projectRoot3, host, parsedConfig);
18951
19961
  if (refreshedConfig) {
18952
- configureAutoIndex(projectRoot3, host, refreshedConfig, getIndexer);
19962
+ configureAutoIndex(projectRoot3, host, refreshedConfig, getIndexer, {
19963
+ synchronizeBackgroundWorker: false
19964
+ });
18953
19965
  }
18954
19966
  }
18955
19967
  requestReindex();
@@ -18997,7 +20009,6 @@ function getConfigPaths(projectRoot3, host, options) {
18997
20009
 
18998
20010
  // src/adapters/pi/extension.ts
18999
20011
  var HOST2 = "pi";
19000
- var activeWatchers = /* @__PURE__ */ new Map();
19001
20012
  var ChunkType = Type2.Union([
19002
20013
  Type2.Literal("function"),
19003
20014
  Type2.Literal("class"),
@@ -19016,24 +20027,30 @@ function projectRoot2(ctx) {
19016
20027
  function isValidProject(projectRoot3, requireProjectMarker) {
19017
20028
  return !isHomeDirectory(projectRoot3) && (!requireProjectMarker || hasProjectMarker(projectRoot3));
19018
20029
  }
19019
- function ensureWatcher(projectRoot3) {
19020
- if (activeWatchers.has(projectRoot3)) return;
20030
+ async function ensureWatcher(projectRoot3) {
19021
20031
  const config = parseConfig(loadMergedConfig(projectRoot3, HOST2));
19022
- 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
+ });
19023
20036
  return;
19024
20037
  }
19025
- activeWatchers.set(projectRoot3, createWatcherWithIndexer(
20038
+ getIndexerForProject(projectRoot3, HOST2);
20039
+ const watcherFactoryForConfig = (refreshedConfig) => refreshedConfig.indexing.watchFiles ? () => createWatcherWithIndexer(
19026
20040
  () => getIndexerForProject(projectRoot3, HOST2),
19027
20041
  projectRoot3,
19028
- config,
20042
+ refreshedConfig,
19029
20043
  HOST2
19030
- ));
19031
- }
19032
- async function stopWatcher(projectRoot3) {
19033
- const watcher = activeWatchers.get(projectRoot3);
19034
- if (!watcher) return;
19035
- activeWatchers.delete(projectRoot3);
19036
- 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);
19037
20054
  }
19038
20055
  function codebaseIndexPiExtension(pi) {
19039
20056
  pi.registerTool({
@@ -19266,7 +20283,7 @@ function codebaseIndexPiExtension(pi) {
19266
20283
  });
19267
20284
  registerPiCallGraphTools(pi);
19268
20285
  pi.on("before_agent_start", async (event, ctx) => {
19269
- ensureWatcher(projectRoot2(ctx));
20286
+ await ensureWatcher(projectRoot2(ctx));
19270
20287
  return {
19271
20288
  systemPrompt: `${event.systemPrompt}
19272
20289
 
@@ -19275,7 +20292,7 @@ Check index_status first when index readiness is unknown. Use codebase_context o
19275
20292
  });
19276
20293
  pi.on("session_shutdown", async (_event, ctx) => {
19277
20294
  const root = projectRoot2(ctx);
19278
- await Promise.all([stopWatcher(root), stopAutoIndex(root, HOST2)]);
20295
+ await stopBackgroundWorker(root, HOST2);
19279
20296
  });
19280
20297
  pi.registerTool({
19281
20298
  name: TOOL_NAME.PR_IMPACT,