open-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.
package/dist/cbi.cjs CHANGED
@@ -334,7 +334,7 @@ var require_ignore = __commonJS({
334
334
  // path matching.
335
335
  // - check `string` either `MODE_IGNORE` or `MODE_CHECK_IGNORE`
336
336
  // @returns {TestResult} true if a file is ignored
337
- test(path34, checkUnignored, mode) {
337
+ test(path35, checkUnignored, mode) {
338
338
  let ignored = false;
339
339
  let unignored = false;
340
340
  let matchedRule;
@@ -343,7 +343,7 @@ var require_ignore = __commonJS({
343
343
  if (unignored === negative && ignored !== unignored || negative && !ignored && !unignored && !checkUnignored) {
344
344
  return;
345
345
  }
346
- const matched = rule[mode].test(path34);
346
+ const matched = rule[mode].test(path35);
347
347
  if (!matched) {
348
348
  return;
349
349
  }
@@ -364,17 +364,17 @@ var require_ignore = __commonJS({
364
364
  var throwError = (message, Ctor) => {
365
365
  throw new Ctor(message);
366
366
  };
367
- var checkPath = (path34, originalPath, doThrow) => {
368
- if (!isString(path34)) {
367
+ var checkPath = (path35, originalPath, doThrow) => {
368
+ if (!isString(path35)) {
369
369
  return doThrow(
370
370
  `path must be a string, but got \`${originalPath}\``,
371
371
  TypeError
372
372
  );
373
373
  }
374
- if (!path34) {
374
+ if (!path35) {
375
375
  return doThrow(`path must not be empty`, TypeError);
376
376
  }
377
- if (checkPath.isNotRelative(path34)) {
377
+ if (checkPath.isNotRelative(path35)) {
378
378
  const r = "`path.relative()`d";
379
379
  return doThrow(
380
380
  `path should be a ${r} string, but got "${originalPath}"`,
@@ -383,7 +383,7 @@ var require_ignore = __commonJS({
383
383
  }
384
384
  return true;
385
385
  };
386
- var isNotRelative = (path34) => REGEX_TEST_INVALID_PATH.test(path34);
386
+ var isNotRelative = (path35) => REGEX_TEST_INVALID_PATH.test(path35);
387
387
  checkPath.isNotRelative = isNotRelative;
388
388
  checkPath.convert = (p) => p;
389
389
  var Ignore2 = class {
@@ -413,19 +413,19 @@ var require_ignore = __commonJS({
413
413
  }
414
414
  // @returns {TestResult}
415
415
  _test(originalPath, cache, checkUnignored, slices) {
416
- const path34 = originalPath && checkPath.convert(originalPath);
416
+ const path35 = originalPath && checkPath.convert(originalPath);
417
417
  checkPath(
418
- path34,
418
+ path35,
419
419
  originalPath,
420
420
  this._strictPathCheck ? throwError : RETURN_FALSE
421
421
  );
422
- return this._t(path34, cache, checkUnignored, slices);
422
+ return this._t(path35, cache, checkUnignored, slices);
423
423
  }
424
- checkIgnore(path34) {
425
- if (!REGEX_TEST_TRAILING_SLASH.test(path34)) {
426
- return this.test(path34);
424
+ checkIgnore(path35) {
425
+ if (!REGEX_TEST_TRAILING_SLASH.test(path35)) {
426
+ return this.test(path35);
427
427
  }
428
- const slices = path34.split(SLASH).filter(Boolean);
428
+ const slices = path35.split(SLASH).filter(Boolean);
429
429
  slices.pop();
430
430
  if (slices.length) {
431
431
  const parent = this._t(
@@ -438,18 +438,18 @@ var require_ignore = __commonJS({
438
438
  return parent;
439
439
  }
440
440
  }
441
- return this._rules.test(path34, false, MODE_CHECK_IGNORE);
441
+ return this._rules.test(path35, false, MODE_CHECK_IGNORE);
442
442
  }
443
- _t(path34, cache, checkUnignored, slices) {
444
- if (path34 in cache) {
445
- return cache[path34];
443
+ _t(path35, cache, checkUnignored, slices) {
444
+ if (path35 in cache) {
445
+ return cache[path35];
446
446
  }
447
447
  if (!slices) {
448
- slices = path34.split(SLASH).filter(Boolean);
448
+ slices = path35.split(SLASH).filter(Boolean);
449
449
  }
450
450
  slices.pop();
451
451
  if (!slices.length) {
452
- return cache[path34] = this._rules.test(path34, checkUnignored, MODE_IGNORE);
452
+ return cache[path35] = this._rules.test(path35, checkUnignored, MODE_IGNORE);
453
453
  }
454
454
  const parent = this._t(
455
455
  slices.join(SLASH) + SLASH,
@@ -457,29 +457,29 @@ var require_ignore = __commonJS({
457
457
  checkUnignored,
458
458
  slices
459
459
  );
460
- return cache[path34] = parent.ignored ? parent : this._rules.test(path34, checkUnignored, MODE_IGNORE);
460
+ return cache[path35] = parent.ignored ? parent : this._rules.test(path35, checkUnignored, MODE_IGNORE);
461
461
  }
462
- ignores(path34) {
463
- return this._test(path34, this._ignoreCache, false).ignored;
462
+ ignores(path35) {
463
+ return this._test(path35, this._ignoreCache, false).ignored;
464
464
  }
465
465
  createFilter() {
466
- return (path34) => !this.ignores(path34);
466
+ return (path35) => !this.ignores(path35);
467
467
  }
468
468
  filter(paths) {
469
469
  return makeArray(paths).filter(this.createFilter());
470
470
  }
471
471
  // @returns {TestResult}
472
- test(path34) {
473
- return this._test(path34, this._testCache, true);
472
+ test(path35) {
473
+ return this._test(path35, this._testCache, true);
474
474
  }
475
475
  };
476
476
  var factory = (options) => new Ignore2(options);
477
- var isPathValid = (path34) => checkPath(path34 && checkPath.convert(path34), path34, RETURN_FALSE);
477
+ var isPathValid = (path35) => checkPath(path35 && checkPath.convert(path35), path35, RETURN_FALSE);
478
478
  var setupWindows = () => {
479
479
  const makePosix = (str) => /^\\\\\?\\/.test(str) || /["<>|\u0000-\u001F]+/u.test(str) ? str : str.replace(/\\/g, "/");
480
480
  checkPath.convert = makePosix;
481
481
  const REGEX_TEST_WINDOWS_PATH_ABSOLUTE = /^[a-z]:\//i;
482
- checkPath.isNotRelative = (path34) => REGEX_TEST_WINDOWS_PATH_ABSOLUTE.test(path34) || isNotRelative(path34);
482
+ checkPath.isNotRelative = (path35) => REGEX_TEST_WINDOWS_PATH_ABSOLUTE.test(path35) || isNotRelative(path35);
483
483
  };
484
484
  if (
485
485
  // Detect `process` so that it can run in browsers.
@@ -665,8 +665,8 @@ __export(cbi_exports, {
665
665
  module.exports = __toCommonJS(cbi_exports);
666
666
 
667
667
  // src/adapters/cbi.ts
668
- var import_node_fs2 = require("fs");
669
- var path33 = __toESM(require("path"), 1);
668
+ var import_node_fs3 = require("fs");
669
+ var path34 = __toESM(require("path"), 1);
670
670
  var import_node_url2 = require("url");
671
671
 
672
672
  // src/config/host.ts
@@ -1734,7 +1734,7 @@ var autoDetectProviders = AUTO_DETECT_PROVIDER_ORDER.filter(
1734
1734
 
1735
1735
  // src/tools/operations.ts
1736
1736
  var import_fs13 = require("fs");
1737
- var path20 = __toESM(require("path"), 1);
1737
+ var path21 = __toESM(require("path"), 1);
1738
1738
 
1739
1739
  // src/tools/knowledge-base-paths.ts
1740
1740
  var path8 = __toESM(require("path"), 1);
@@ -2535,8 +2535,8 @@ ${truncateContent(r.content)}
2535
2535
 
2536
2536
  // src/utils/auto-index.ts
2537
2537
  var import_fs7 = require("fs");
2538
- var os3 = __toESM(require("os"), 1);
2539
- var path11 = __toESM(require("path"), 1);
2538
+ var os4 = __toESM(require("os"), 1);
2539
+ var path12 = __toESM(require("path"), 1);
2540
2540
 
2541
2541
  // src/indexer/index-lock.ts
2542
2542
  var import_crypto = require("crypto");
@@ -2783,7 +2783,7 @@ function removeDeadReclaimMarker(lockPath, expectedOwner) {
2783
2783
  return true;
2784
2784
  }
2785
2785
  function reclaimDeadOwner(indexPath, lockPath, expectedOwner) {
2786
- const reclaimPath = path9.join(lockPath, RECLAIM_DIRECTORY_NAME);
2786
+ const reclaimPath2 = path9.join(lockPath, RECLAIM_DIRECTORY_NAME);
2787
2787
  const reclaimOwner = {
2788
2788
  pid: process.pid,
2789
2789
  hostname: os2.hostname(),
@@ -2792,19 +2792,19 @@ function reclaimDeadOwner(indexPath, lockPath, expectedOwner) {
2792
2792
  expectedOwnerToken: expectedOwner.token
2793
2793
  };
2794
2794
  for (let attempt = 0; attempt < 2; attempt += 1) {
2795
- if (publishJsonDirectory(reclaimPath, reclaimOwner)) break;
2795
+ if (publishJsonDirectory(reclaimPath2, reclaimOwner)) break;
2796
2796
  if (attempt === 0 && removeDeadReclaimMarker(lockPath, expectedOwner)) continue;
2797
2797
  return false;
2798
2798
  }
2799
2799
  try {
2800
- const currentReclaimer = readReclaimOwner(reclaimPath);
2800
+ const currentReclaimer = readReclaimOwner(reclaimPath2);
2801
2801
  const currentOwner = readDirectoryOwner(lockPath);
2802
2802
  if (!currentReclaimer || !sameReclaimOwner(currentReclaimer, reclaimOwner) || !currentOwner || !sameOwner(currentOwner, expectedOwner) || getOwnerLiveness(currentOwner) !== "dead") {
2803
2803
  return false;
2804
2804
  }
2805
2805
  publishRecoveryMarker(indexPath, expectedOwner);
2806
2806
  const ownerBeforeQuarantine = readDirectoryOwner(lockPath);
2807
- const reclaimerBeforeQuarantine = readReclaimOwner(reclaimPath);
2807
+ const reclaimerBeforeQuarantine = readReclaimOwner(reclaimPath2);
2808
2808
  if (!ownerBeforeQuarantine || !sameOwner(ownerBeforeQuarantine, expectedOwner) || getOwnerLiveness(ownerBeforeQuarantine) !== "dead" || !reclaimerBeforeQuarantine || !sameReclaimOwner(reclaimerBeforeQuarantine, reclaimOwner)) {
2809
2809
  return false;
2810
2810
  }
@@ -2974,10 +2974,880 @@ function completeLeaseRecovery(lease) {
2974
2974
  }
2975
2975
  }
2976
2976
 
2977
+ // src/utils/background-worker.ts
2978
+ var import_node_crypto = require("crypto");
2979
+ var import_node_fs = require("fs");
2980
+ var os3 = __toESM(require("os"), 1);
2981
+ var path10 = __toESM(require("path"), 1);
2982
+ var OWNER_FILE_NAME2 = "owner.json";
2983
+ var HEARTBEAT_FILE_PREFIX = "heartbeat.";
2984
+ var RECLAIM_DIRECTORY_NAME2 = "reclaim";
2985
+ var REFRESH_REQUEST_FILE_NAME = "refresh-request.json";
2986
+ var HEARTBEAT_INTERVAL_MS = 5e3;
2987
+ var STALE_LEASE_MS = 3e4;
2988
+ var RETRY_DELAY_MS = 5e3;
2989
+ 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;
2990
+ var BackgroundWorkerStopError = class extends Error {
2991
+ constructor(watcherError, autoIndexError) {
2992
+ super("Failed to stop background worker");
2993
+ this.watcherError = watcherError;
2994
+ this.autoIndexError = autoIndexError;
2995
+ this.name = "BackgroundWorkerStopError";
2996
+ }
2997
+ watcherError;
2998
+ autoIndexError;
2999
+ };
3000
+ var workers = /* @__PURE__ */ new Map();
3001
+ var workerKeysByProject = /* @__PURE__ */ new Map();
3002
+ var workerReplacementBarriers = /* @__PURE__ */ new Map();
3003
+ function getErrorCode2(error) {
3004
+ return typeof error === "object" && error !== null && "code" in error ? String(error.code) : void 0;
3005
+ }
3006
+ function canonicalizePath(targetPath) {
3007
+ const resolved = path10.resolve(targetPath);
3008
+ if ((0, import_node_fs.existsSync)(resolved)) {
3009
+ try {
3010
+ return import_node_fs.realpathSync.native(resolved);
3011
+ } catch {
3012
+ return resolved;
3013
+ }
3014
+ }
3015
+ const parent = path10.dirname(resolved);
3016
+ if (parent === resolved) return resolved;
3017
+ return path10.join(canonicalizePath(parent), path10.basename(resolved));
3018
+ }
3019
+ function projectLookupKey(projectRoot, host) {
3020
+ return `${host}::${canonicalizePath(projectRoot)}`;
3021
+ }
3022
+ function resolveIdentity(projectRoot, config, host) {
3023
+ const canonicalProjectRoot = canonicalizePath(projectRoot);
3024
+ const canonicalIndexPath = canonicalizePath(resolveProjectIndexPath(projectRoot, config.scope, host));
3025
+ return {
3026
+ canonicalIndexPath,
3027
+ canonicalProjectRoot,
3028
+ key: `${canonicalIndexPath}::${canonicalProjectRoot}`
3029
+ };
3030
+ }
3031
+ function controllerKey(identity, host) {
3032
+ return `${identity.key}::${host}`;
3033
+ }
3034
+ function leaseDirectoryName(identity) {
3035
+ const hash = (0, import_node_crypto.createHash)("sha256").update(identity.key).digest("hex").slice(0, 32);
3036
+ return `background-worker.${hash}.lease`;
3037
+ }
3038
+ function leasePathFor(identity) {
3039
+ return path10.join(identity.canonicalIndexPath, leaseDirectoryName(identity));
3040
+ }
3041
+ function parseOwner2(value) {
3042
+ if (typeof value !== "object" || value === null) return null;
3043
+ const candidate = value;
3044
+ if (candidate.version !== 1) return null;
3045
+ if (!Number.isInteger(candidate.pid) || (candidate.pid ?? 0) <= 0) return null;
3046
+ if (typeof candidate.hostname !== "string" || candidate.hostname.length === 0) return null;
3047
+ if (typeof candidate.startedAt !== "string" || Number.isNaN(Date.parse(candidate.startedAt))) return null;
3048
+ if (typeof candidate.heartbeatAt !== "string" || Number.isNaN(Date.parse(candidate.heartbeatAt))) return null;
3049
+ if (typeof candidate.projectRoot !== "string" || candidate.projectRoot.length === 0) return null;
3050
+ if (typeof candidate.indexPath !== "string" || candidate.indexPath.length === 0) return null;
3051
+ if (typeof candidate.token !== "string" || !UUID_PATTERN2.test(candidate.token)) return null;
3052
+ return candidate;
3053
+ }
3054
+ function parseHeartbeat(value, expectedToken) {
3055
+ if (typeof value !== "object" || value === null) return null;
3056
+ const candidate = value;
3057
+ if (candidate.version !== 1 || candidate.token !== expectedToken) return null;
3058
+ if (typeof candidate.heartbeatAt !== "string" || Number.isNaN(Date.parse(candidate.heartbeatAt))) return null;
3059
+ return candidate;
3060
+ }
3061
+ function parseReclaimOwner2(value) {
3062
+ if (typeof value !== "object" || value === null) return null;
3063
+ const candidate = value;
3064
+ if (candidate.version !== 1) return null;
3065
+ if (!Number.isInteger(candidate.pid) || (candidate.pid ?? 0) <= 0) return null;
3066
+ if (typeof candidate.hostname !== "string" || candidate.hostname.length === 0) return null;
3067
+ if (typeof candidate.startedAt !== "string" || Number.isNaN(Date.parse(candidate.startedAt))) return null;
3068
+ if (typeof candidate.token !== "string" || !UUID_PATTERN2.test(candidate.token)) return null;
3069
+ if (candidate.expectedOwnerToken !== null && (typeof candidate.expectedOwnerToken !== "string" || !UUID_PATTERN2.test(candidate.expectedOwnerToken))) return null;
3070
+ return candidate;
3071
+ }
3072
+ function heartbeatPath(leasePath, token) {
3073
+ return path10.join(leasePath, `${HEARTBEAT_FILE_PREFIX}${token}.json`);
3074
+ }
3075
+ function reclaimPath(leasePath) {
3076
+ return path10.join(leasePath, RECLAIM_DIRECTORY_NAME2);
3077
+ }
3078
+ function refreshRequestPath(leasePath) {
3079
+ return path10.join(leasePath, REFRESH_REQUEST_FILE_NAME);
3080
+ }
3081
+ function readLeaseOwner(leasePath) {
3082
+ try {
3083
+ return parseOwner2(JSON.parse((0, import_node_fs.readFileSync)(path10.join(leasePath, OWNER_FILE_NAME2), "utf-8")));
3084
+ } catch {
3085
+ return null;
3086
+ }
3087
+ }
3088
+ function readOwner(leasePath) {
3089
+ const owner = readLeaseOwner(leasePath);
3090
+ if (!owner) return null;
3091
+ try {
3092
+ const heartbeat = parseHeartbeat(
3093
+ JSON.parse((0, import_node_fs.readFileSync)(heartbeatPath(leasePath, owner.token), "utf-8")),
3094
+ owner.token
3095
+ );
3096
+ return heartbeat ? { ...owner, heartbeatAt: heartbeat.heartbeatAt } : owner;
3097
+ } catch {
3098
+ return owner;
3099
+ }
3100
+ }
3101
+ function readReclaimOwner2(leasePath) {
3102
+ try {
3103
+ return parseReclaimOwner2(JSON.parse((0, import_node_fs.readFileSync)(path10.join(reclaimPath(leasePath), OWNER_FILE_NAME2), "utf-8")));
3104
+ } catch {
3105
+ return null;
3106
+ }
3107
+ }
3108
+ function ownerLiveness(owner) {
3109
+ if (owner.hostname !== os3.hostname()) return "unknown";
3110
+ try {
3111
+ process.kill(owner.pid, 0);
3112
+ return "alive";
3113
+ } catch (error) {
3114
+ const code = getErrorCode2(error);
3115
+ if (code === "ESRCH") return "dead";
3116
+ if (code === "EPERM") return "alive";
3117
+ return "unknown";
3118
+ }
3119
+ }
3120
+ function isHeartbeatExpired(owner) {
3121
+ return Date.now() - Date.parse(owner.heartbeatAt) >= STALE_LEASE_MS;
3122
+ }
3123
+ function sameOwner2(left, right) {
3124
+ return left.pid === right.pid && left.hostname === right.hostname && left.token === right.token;
3125
+ }
3126
+ function writeHeartbeat(leasePath, owner) {
3127
+ const targetPath = heartbeatPath(leasePath, owner.token);
3128
+ const temporaryPath = `${targetPath}.tmp.${process.pid}.${owner.token}.${(0, import_node_crypto.randomUUID)()}`;
3129
+ const heartbeat = {
3130
+ version: 1,
3131
+ token: owner.token,
3132
+ heartbeatAt: owner.heartbeatAt
3133
+ };
3134
+ try {
3135
+ (0, import_node_fs.writeFileSync)(temporaryPath, JSON.stringify(heartbeat), {
3136
+ encoding: "utf-8",
3137
+ flag: "wx",
3138
+ mode: 384
3139
+ });
3140
+ (0, import_node_fs.renameSync)(temporaryPath, targetPath);
3141
+ const currentOwner = readLeaseOwner(leasePath);
3142
+ return currentOwner !== null && sameOwner2(currentOwner, owner);
3143
+ } finally {
3144
+ if ((0, import_node_fs.existsSync)(temporaryPath)) (0, import_node_fs.rmSync)(temporaryPath, { force: true });
3145
+ }
3146
+ }
3147
+ function requestRefreshFromLeader(leasePath, allowDisabledAutoIndex) {
3148
+ const requestPath = refreshRequestPath(leasePath);
3149
+ const temporaryPath = `${requestPath}.tmp.${process.pid}.${(0, import_node_crypto.randomUUID)()}`;
3150
+ try {
3151
+ const request = {
3152
+ allowDisabledAutoIndex,
3153
+ requestedAt: (/* @__PURE__ */ new Date()).toISOString(),
3154
+ version: 1
3155
+ };
3156
+ (0, import_node_fs.writeFileSync)(temporaryPath, JSON.stringify(request), {
3157
+ encoding: "utf-8",
3158
+ flag: "wx",
3159
+ mode: 384
3160
+ });
3161
+ (0, import_node_fs.renameSync)(temporaryPath, requestPath);
3162
+ } catch (error) {
3163
+ if (getErrorCode2(error) !== "ENOENT") {
3164
+ console.error("[codebase-index] Failed to request background index refresh from the project worker:", error);
3165
+ }
3166
+ } finally {
3167
+ if ((0, import_node_fs.existsSync)(temporaryPath)) (0, import_node_fs.rmSync)(temporaryPath, { force: true });
3168
+ }
3169
+ }
3170
+ function consumeRefreshRequest(leasePath) {
3171
+ const requestPath = refreshRequestPath(leasePath);
3172
+ const claimedPath = `${requestPath}.handling.${process.pid}.${(0, import_node_crypto.randomUUID)()}`;
3173
+ try {
3174
+ (0, import_node_fs.renameSync)(requestPath, claimedPath);
3175
+ } catch (error) {
3176
+ if (getErrorCode2(error) === "ENOENT") return null;
3177
+ throw error;
3178
+ }
3179
+ try {
3180
+ const value = JSON.parse((0, import_node_fs.readFileSync)(claimedPath, "utf-8"));
3181
+ return {
3182
+ allowDisabledAutoIndex: value.version === 1 && value.allowDisabledAutoIndex === true,
3183
+ requestedAt: typeof value.requestedAt === "string" ? value.requestedAt : (/* @__PURE__ */ new Date()).toISOString(),
3184
+ version: 1
3185
+ };
3186
+ } catch {
3187
+ return { allowDisabledAutoIndex: false, requestedAt: (/* @__PURE__ */ new Date()).toISOString(), version: 1 };
3188
+ } finally {
3189
+ (0, import_node_fs.rmSync)(claimedPath, { force: true });
3190
+ }
3191
+ }
3192
+ function publishLease(leasePath, owner) {
3193
+ const candidatePath = `${leasePath}.candidate.${process.pid}.${owner.token}`;
3194
+ try {
3195
+ (0, import_node_fs.mkdirSync)(candidatePath, { mode: 448 });
3196
+ } catch (error) {
3197
+ if (getErrorCode2(error) === "ENOENT") return false;
3198
+ throw error;
3199
+ }
3200
+ try {
3201
+ (0, import_node_fs.writeFileSync)(path10.join(candidatePath, OWNER_FILE_NAME2), JSON.stringify(owner), {
3202
+ encoding: "utf-8",
3203
+ flag: "wx",
3204
+ mode: 384
3205
+ });
3206
+ if ((0, import_node_fs.existsSync)(leasePath)) return false;
3207
+ try {
3208
+ (0, import_node_fs.renameSync)(candidatePath, leasePath);
3209
+ return true;
3210
+ } catch (error) {
3211
+ if ((0, import_node_fs.existsSync)(leasePath) || getErrorCode2(error) === "ENOENT") return false;
3212
+ throw error;
3213
+ }
3214
+ } finally {
3215
+ if ((0, import_node_fs.existsSync)(candidatePath)) (0, import_node_fs.rmSync)(candidatePath, { recursive: true, force: true });
3216
+ }
3217
+ }
3218
+ function sameReclaimOwner2(left, right) {
3219
+ return left.pid === right.pid && left.hostname === right.hostname && left.token === right.token && left.expectedOwnerToken === right.expectedOwnerToken;
3220
+ }
3221
+ function reclaimerLiveness(owner) {
3222
+ return ownerLiveness(owner);
3223
+ }
3224
+ function isReclaimMarkerExpired(leasePath, owner) {
3225
+ const startedAt = owner ? Date.parse(owner.startedAt) : (() => {
3226
+ try {
3227
+ return (0, import_node_fs.lstatSync)(reclaimPath(leasePath)).mtimeMs;
3228
+ } catch {
3229
+ return Date.now();
3230
+ }
3231
+ })();
3232
+ return Date.now() - startedAt >= STALE_LEASE_MS;
3233
+ }
3234
+ function hasActiveReclaimMarker(leasePath, owner) {
3235
+ const marker = readReclaimOwner2(leasePath);
3236
+ return marker !== null && marker.expectedOwnerToken === owner.token && (marker.hostname !== os3.hostname() || ownerLiveness(owner) !== "alive");
3237
+ }
3238
+ function publishReclaimMarker(leasePath, expectedOwner) {
3239
+ const markerPath = reclaimPath(leasePath);
3240
+ const owner = {
3241
+ version: 1,
3242
+ pid: process.pid,
3243
+ hostname: os3.hostname(),
3244
+ startedAt: (/* @__PURE__ */ new Date()).toISOString(),
3245
+ token: (0, import_node_crypto.randomUUID)(),
3246
+ expectedOwnerToken: expectedOwner?.token ?? null
3247
+ };
3248
+ try {
3249
+ (0, import_node_fs.mkdirSync)(markerPath, { mode: 448 });
3250
+ } catch (error) {
3251
+ if (getErrorCode2(error) === "EEXIST" || getErrorCode2(error) === "ENOENT") return null;
3252
+ throw error;
3253
+ }
3254
+ try {
3255
+ (0, import_node_fs.writeFileSync)(path10.join(markerPath, OWNER_FILE_NAME2), JSON.stringify(owner), {
3256
+ encoding: "utf-8",
3257
+ flag: "wx",
3258
+ mode: 384
3259
+ });
3260
+ return owner;
3261
+ } catch (error) {
3262
+ (0, import_node_fs.rmSync)(markerPath, { recursive: true, force: true });
3263
+ throw error;
3264
+ }
3265
+ }
3266
+ function removeExpiredReclaimMarker(leasePath, expectedOwner) {
3267
+ const marker = readReclaimOwner2(leasePath);
3268
+ const markerPath = reclaimPath(leasePath);
3269
+ if (!(0, import_node_fs.existsSync)(markerPath)) return false;
3270
+ if (marker && marker.expectedOwnerToken !== (expectedOwner?.token ?? null)) return false;
3271
+ if (marker && (reclaimerLiveness(marker) === "alive" || !isReclaimMarkerExpired(leasePath, marker))) return false;
3272
+ if (!marker && !isReclaimMarkerExpired(leasePath, null)) return false;
3273
+ const staleMarkerPath = `${markerPath}.stale.${marker?.pid ?? process.pid}.${marker?.token ?? (0, import_node_crypto.randomUUID)()}.${(0, import_node_crypto.randomUUID)()}`;
3274
+ try {
3275
+ (0, import_node_fs.renameSync)(markerPath, staleMarkerPath);
3276
+ } catch (error) {
3277
+ if (getErrorCode2(error) === "ENOENT") return false;
3278
+ throw error;
3279
+ }
3280
+ try {
3281
+ let claimedMarker = null;
3282
+ try {
3283
+ claimedMarker = parseReclaimOwner2(
3284
+ JSON.parse((0, import_node_fs.readFileSync)(path10.join(staleMarkerPath, OWNER_FILE_NAME2), "utf-8"))
3285
+ );
3286
+ } catch {
3287
+ claimedMarker = null;
3288
+ }
3289
+ const markerMatches = marker ? claimedMarker !== null && sameReclaimOwner2(claimedMarker, marker) : claimedMarker === null;
3290
+ if (!markerMatches || !canReclaimLease(leasePath, expectedOwner)) {
3291
+ if (!(0, import_node_fs.existsSync)(markerPath) && (0, import_node_fs.existsSync)(staleMarkerPath)) (0, import_node_fs.renameSync)(staleMarkerPath, markerPath);
3292
+ return false;
3293
+ }
3294
+ (0, import_node_fs.rmSync)(staleMarkerPath, { recursive: true, force: true });
3295
+ return true;
3296
+ } catch (error) {
3297
+ if (getErrorCode2(error) === "ENOENT") return false;
3298
+ throw error;
3299
+ }
3300
+ }
3301
+ function canReclaimLease(leasePath, expectedOwner) {
3302
+ if (!(0, import_node_fs.existsSync)(leasePath)) return false;
3303
+ if (!expectedOwner) return false;
3304
+ const currentOwner = readOwner(leasePath);
3305
+ if (!currentOwner || !sameOwner2(currentOwner, expectedOwner)) return false;
3306
+ if (currentOwner.hostname === os3.hostname()) {
3307
+ return ownerLiveness(currentOwner) === "dead";
3308
+ }
3309
+ return isHeartbeatExpired(currentOwner);
3310
+ }
3311
+ function reclaimLease(leasePath, expectedOwner) {
3312
+ let marker = null;
3313
+ for (let attempt = 0; attempt < 2; attempt += 1) {
3314
+ marker = publishReclaimMarker(leasePath, expectedOwner);
3315
+ if (marker) break;
3316
+ if (attempt === 0 && removeExpiredReclaimMarker(leasePath, expectedOwner)) continue;
3317
+ return false;
3318
+ }
3319
+ if (!marker) return false;
3320
+ const markerPath = reclaimPath(leasePath);
3321
+ try {
3322
+ const currentMarker = readReclaimOwner2(leasePath);
3323
+ if (!currentMarker || !sameReclaimOwner2(currentMarker, marker) || !canReclaimLease(leasePath, expectedOwner)) {
3324
+ return false;
3325
+ }
3326
+ const stalePath = `${leasePath}.stale.${process.pid}.${marker.token}`;
3327
+ (0, import_node_fs.renameSync)(leasePath, stalePath);
3328
+ const quarantinedOwner = readOwner(stalePath);
3329
+ const quarantinedMarker = readReclaimOwner2(stalePath);
3330
+ if (!quarantinedMarker || !sameReclaimOwner2(quarantinedMarker, marker) || expectedOwner !== null && (!quarantinedOwner || !sameOwner2(quarantinedOwner, expectedOwner))) {
3331
+ if (!(0, import_node_fs.existsSync)(leasePath) && (0, import_node_fs.existsSync)(stalePath)) (0, import_node_fs.renameSync)(stalePath, leasePath);
3332
+ return false;
3333
+ }
3334
+ (0, import_node_fs.rmSync)(stalePath, { recursive: true, force: true });
3335
+ return true;
3336
+ } catch (error) {
3337
+ if (getErrorCode2(error) === "ENOENT") return false;
3338
+ throw error;
3339
+ } finally {
3340
+ const currentMarker = readReclaimOwner2(leasePath);
3341
+ if (currentMarker && sameReclaimOwner2(currentMarker, marker)) {
3342
+ (0, import_node_fs.rmSync)(markerPath, { recursive: true, force: true });
3343
+ }
3344
+ }
3345
+ }
3346
+ function acquireLease(identity) {
3347
+ (0, import_node_fs.mkdirSync)(identity.canonicalIndexPath, { recursive: true, mode: 448 });
3348
+ const canonicalIndexPath = import_node_fs.realpathSync.native(identity.canonicalIndexPath);
3349
+ const leasePath = path10.join(canonicalIndexPath, leaseDirectoryName({ ...identity, canonicalIndexPath }));
3350
+ for (let attempt = 0; attempt < 4; attempt += 1) {
3351
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
3352
+ const owner = {
3353
+ version: 1,
3354
+ pid: process.pid,
3355
+ hostname: os3.hostname(),
3356
+ startedAt: timestamp,
3357
+ heartbeatAt: timestamp,
3358
+ projectRoot: identity.canonicalProjectRoot,
3359
+ indexPath: canonicalIndexPath,
3360
+ token: (0, import_node_crypto.randomUUID)()
3361
+ };
3362
+ if (publishLease(leasePath, owner)) {
3363
+ return { leasePath, owner };
3364
+ }
3365
+ const existingOwner = readOwner(leasePath);
3366
+ if (existingOwner) {
3367
+ if (canReclaimLease(leasePath, existingOwner) && reclaimLease(leasePath, existingOwner)) continue;
3368
+ return null;
3369
+ }
3370
+ return null;
3371
+ }
3372
+ return null;
3373
+ }
3374
+ function releaseLease(lease) {
3375
+ const currentOwner = readOwner(lease.leasePath);
3376
+ if (!currentOwner || !sameOwner2(currentOwner, lease.owner)) return false;
3377
+ const releasePath = `${lease.leasePath}.release.${lease.owner.pid}.${lease.owner.token}`;
3378
+ try {
3379
+ (0, import_node_fs.renameSync)(lease.leasePath, releasePath);
3380
+ } catch (error) {
3381
+ if (getErrorCode2(error) === "ENOENT") return false;
3382
+ throw error;
3383
+ }
3384
+ const claimedOwner = readOwner(releasePath);
3385
+ if (!claimedOwner || !sameOwner2(claimedOwner, lease.owner)) {
3386
+ if (!(0, import_node_fs.existsSync)(lease.leasePath) && (0, import_node_fs.existsSync)(releasePath)) {
3387
+ (0, import_node_fs.renameSync)(releasePath, lease.leasePath);
3388
+ }
3389
+ return false;
3390
+ }
3391
+ (0, import_node_fs.rmSync)(releasePath, { recursive: true, force: true });
3392
+ return true;
3393
+ }
3394
+ var BackgroundWorkerController = class {
3395
+ constructor(projectRoot, host, config, hooks, identity) {
3396
+ this.projectRoot = projectRoot;
3397
+ this.host = host;
3398
+ this.config = config;
3399
+ this.hooks = hooks;
3400
+ this.identity = identity;
3401
+ }
3402
+ projectRoot;
3403
+ host;
3404
+ config;
3405
+ hooks;
3406
+ identity;
3407
+ lease = null;
3408
+ watcher = null;
3409
+ leaderReady = Promise.resolve();
3410
+ heartbeatTimer = null;
3411
+ retryTimer = null;
3412
+ teardownRetryTimer = null;
3413
+ transition = Promise.resolve();
3414
+ stopPromise = null;
3415
+ stopped = false;
3416
+ stopping = false;
3417
+ losingLeadership = false;
3418
+ restartAfterStop = false;
3419
+ leaderWorkStopped = false;
3420
+ startingLeaderWork = false;
3421
+ stopAutoIndexOnTeardown = true;
3422
+ autoIndexStarted = false;
3423
+ reportedError = null;
3424
+ update(config, hooks, options) {
3425
+ const autoIndexWasEnabled = this.config.indexing.autoIndex;
3426
+ const shouldReplaceWatcher = this.watcher !== null && hooks.watcherFactory !== void 0 && (hooks.watcherFactory === null || hooks.replaceWatcher === true);
3427
+ this.config = config;
3428
+ this.hooks = {
3429
+ ...this.hooks,
3430
+ ...hooks,
3431
+ watcherFactory: hooks.watcherFactory === void 0 ? this.hooks.watcherFactory : hooks.watcherFactory,
3432
+ watcherFactoryForConfig: hooks.watcherFactoryForConfig === void 0 ? this.hooks.watcherFactoryForConfig : hooks.watcherFactoryForConfig
3433
+ };
3434
+ if (autoIndexWasEnabled && !config.indexing.autoIndex || options.restartAutoIndex === true && config.indexing.autoIndex && !this.startingLeaderWork) {
3435
+ this.autoIndexStarted = false;
3436
+ }
3437
+ if (!this.canRun()) {
3438
+ void this.stop().catch((error) => {
3439
+ console.error("[codebase-index] Failed to stop background worker after disabling automatic work:", error);
3440
+ });
3441
+ return;
3442
+ }
3443
+ if (shouldReplaceWatcher) {
3444
+ void this.enqueue(async () => {
3445
+ const watcher = this.watcher;
3446
+ if (watcher) {
3447
+ await watcher.stop();
3448
+ if (this.watcher === watcher) this.watcher = null;
3449
+ }
3450
+ if (this.lease && !this.stopped) this.startLeaderWork();
3451
+ }).catch((error) => {
3452
+ console.error("[codebase-index] Failed to replace background file watcher:", error);
3453
+ });
3454
+ }
3455
+ this.start();
3456
+ }
3457
+ startAfter(activation) {
3458
+ this.transition = activation.catch(() => void 0);
3459
+ this.start();
3460
+ }
3461
+ start() {
3462
+ if (!this.canRun() || this.losingLeadership) return;
3463
+ if (this.stopping) {
3464
+ this.restartAfterStop = true;
3465
+ return;
3466
+ }
3467
+ this.stopped = false;
3468
+ void this.enqueue(async () => {
3469
+ if (this.stopped || this.stopping || this.losingLeadership || !this.canRun()) return;
3470
+ if (!this.lease) {
3471
+ try {
3472
+ this.lease = acquireLease(this.identity);
3473
+ this.reportedError = null;
3474
+ } catch (error) {
3475
+ this.reportAcquireError(error);
3476
+ this.scheduleRetry();
3477
+ return;
3478
+ }
3479
+ }
3480
+ if (!this.lease) {
3481
+ this.scheduleRetry();
3482
+ return;
3483
+ }
3484
+ this.startHeartbeat();
3485
+ this.startLeaderWork();
3486
+ });
3487
+ }
3488
+ waitForStart() {
3489
+ return this.transition.catch(() => void 0).then(() => this.leaderReady);
3490
+ }
3491
+ requestRefresh(allowDisabledAutoIndex = false) {
3492
+ this.start();
3493
+ if (!this.isLeader()) {
3494
+ requestRefreshFromLeader(leasePathFor(this.identity), allowDisabledAutoIndex);
3495
+ return;
3496
+ }
3497
+ void this.enqueue(async () => {
3498
+ if (this.stopped || !this.lease) return;
3499
+ this.hooks.startAutoIndex("retrieval", allowDisabledAutoIndex);
3500
+ });
3501
+ }
3502
+ isLeader() {
3503
+ return this.lease !== null && !this.stopping && !this.losingLeadership;
3504
+ }
3505
+ isStopping() {
3506
+ return this.stopping;
3507
+ }
3508
+ getHooksForConfig(config) {
3509
+ const watcherFactoryForConfig = this.hooks.watcherFactoryForConfig;
3510
+ if (!watcherFactoryForConfig) return this.hooks;
3511
+ return {
3512
+ ...this.hooks,
3513
+ watcherFactory: watcherFactoryForConfig(config),
3514
+ replaceWatcher: true
3515
+ };
3516
+ }
3517
+ attachWatcher(watcherFactory, watcherFactoryForConfig) {
3518
+ if (this.hooks.watcherFactory !== void 0) return;
3519
+ this.hooks = {
3520
+ ...this.hooks,
3521
+ watcherFactory,
3522
+ watcherFactoryForConfig: watcherFactoryForConfig ?? this.hooks.watcherFactoryForConfig
3523
+ };
3524
+ this.start();
3525
+ }
3526
+ async stop(stopAutoIndex = true) {
3527
+ if (this.stopPromise) return this.stopPromise;
3528
+ this.stopped = true;
3529
+ this.stopping = true;
3530
+ this.stopAutoIndexOnTeardown &&= stopAutoIndex;
3531
+ this.clearRetryTimer();
3532
+ const attempt = this.enqueue(async () => {
3533
+ try {
3534
+ const lease = this.lease;
3535
+ if (this.leaderWorkStopped) {
3536
+ if (lease) {
3537
+ this.releaseStoppedLease(lease);
3538
+ } else {
3539
+ this.finishStoppedLease();
3540
+ }
3541
+ return;
3542
+ }
3543
+ const hadLeaderWork = lease !== null || this.watcher !== null || this.autoIndexStarted;
3544
+ const stopped = await this.stopLeaderWork(hadLeaderWork && this.stopAutoIndexOnTeardown);
3545
+ if (!lease) {
3546
+ this.finishStoppedLease();
3547
+ return;
3548
+ }
3549
+ if (!stopped.completed) {
3550
+ this.releaseLeaseWhenAutoIndexStops(lease, stopped.completion);
3551
+ return;
3552
+ }
3553
+ this.leaderWorkStopped = true;
3554
+ this.releaseStoppedLease(lease);
3555
+ } catch (error) {
3556
+ this.scheduleTeardownRetry();
3557
+ throw error;
3558
+ }
3559
+ });
3560
+ const completion = attempt.finally(() => {
3561
+ if (this.stopPromise === completion) this.stopPromise = null;
3562
+ });
3563
+ this.stopPromise = completion;
3564
+ return completion;
3565
+ }
3566
+ canRun() {
3567
+ return this.config.indexing.autoIndex || this.hooks.watcherFactory != null;
3568
+ }
3569
+ enqueue(operation) {
3570
+ const next = this.transition.catch(() => void 0).then(operation);
3571
+ this.transition = next;
3572
+ return next;
3573
+ }
3574
+ startLeaderWork() {
3575
+ if (this.stopped || this.stopping || this.losingLeadership) return;
3576
+ this.startingLeaderWork = true;
3577
+ try {
3578
+ if (this.config.indexing.autoIndex && !this.autoIndexStarted) {
3579
+ this.autoIndexStarted = true;
3580
+ this.hooks.startAutoIndex("startup");
3581
+ }
3582
+ if (!this.watcher && this.hooks.watcherFactory) {
3583
+ try {
3584
+ const watcher = this.hooks.watcherFactory();
3585
+ this.watcher = watcher;
3586
+ this.leaderReady = watcher.whenReady?.().catch((error) => {
3587
+ console.error("[codebase-index] Failed while waiting for background file watcher startup:", error);
3588
+ }) ?? Promise.resolve();
3589
+ } catch (error) {
3590
+ console.error("[codebase-index] Failed to start background file watcher:", error);
3591
+ this.leaderReady = Promise.resolve();
3592
+ }
3593
+ }
3594
+ } finally {
3595
+ this.startingLeaderWork = false;
3596
+ }
3597
+ }
3598
+ async stopLeaderWork(stopAutoIndex) {
3599
+ const watcher = this.watcher;
3600
+ let watcherError;
3601
+ if (watcher) {
3602
+ try {
3603
+ await watcher.stop();
3604
+ if (this.watcher === watcher) this.watcher = null;
3605
+ } catch (error) {
3606
+ watcherError = error;
3607
+ }
3608
+ }
3609
+ let autoIndexError;
3610
+ let autoIndexStop = {
3611
+ completed: true,
3612
+ completion: Promise.resolve()
3613
+ };
3614
+ if (stopAutoIndex) {
3615
+ try {
3616
+ autoIndexStop = await this.hooks.stopAutoIndex();
3617
+ this.autoIndexStarted = false;
3618
+ } catch (error) {
3619
+ autoIndexError = error;
3620
+ }
3621
+ }
3622
+ if (watcherError !== void 0 || autoIndexError !== void 0) {
3623
+ throw new BackgroundWorkerStopError(watcherError, autoIndexError);
3624
+ }
3625
+ return autoIndexStop;
3626
+ }
3627
+ releaseLeaseWhenAutoIndexStops(lease, completion) {
3628
+ void completion.then(
3629
+ () => {
3630
+ void this.enqueue(async () => {
3631
+ if (this.lease !== lease || !this.stopping) return;
3632
+ this.leaderWorkStopped = true;
3633
+ this.releaseStoppedLease(lease);
3634
+ }).catch((error) => {
3635
+ console.error("[codebase-index] Failed to release background worker lease after automatic indexing stopped:", error);
3636
+ this.scheduleTeardownRetry();
3637
+ });
3638
+ },
3639
+ (error) => {
3640
+ console.error("[codebase-index] Failed while waiting for automatic indexing to stop:", error);
3641
+ this.scheduleTeardownRetry();
3642
+ }
3643
+ );
3644
+ }
3645
+ releaseStoppedLease(lease) {
3646
+ if (this.lease !== lease) {
3647
+ this.finishStoppedLease();
3648
+ return;
3649
+ }
3650
+ releaseLease(lease);
3651
+ this.lease = null;
3652
+ this.finishStoppedLease();
3653
+ }
3654
+ finishStoppedLease() {
3655
+ this.leaderWorkStopped = false;
3656
+ this.stopAutoIndexOnTeardown = true;
3657
+ this.stopping = false;
3658
+ this.clearTimers();
3659
+ this.restartAfterTeardown();
3660
+ if (!this.stopped || this.stopping) return;
3661
+ const projectKey = projectLookupKey(this.projectRoot, this.host);
3662
+ const key = controllerKey(this.identity, this.host);
3663
+ if (workers.get(key) === this) workers.delete(key);
3664
+ if (workerKeysByProject.get(projectKey) === key) workerKeysByProject.delete(projectKey);
3665
+ }
3666
+ startHeartbeat() {
3667
+ if (this.heartbeatTimer) return;
3668
+ const heartbeat = () => {
3669
+ void this.heartbeat();
3670
+ };
3671
+ this.heartbeatTimer = setInterval(heartbeat, HEARTBEAT_INTERVAL_MS);
3672
+ this.heartbeatTimer.unref?.();
3673
+ }
3674
+ async heartbeat() {
3675
+ const lease = this.lease;
3676
+ if (!lease || this.losingLeadership || this.stopped && !this.stopping) return;
3677
+ if (hasActiveReclaimMarker(lease.leasePath, lease.owner)) {
3678
+ await this.loseLeadership();
3679
+ return;
3680
+ }
3681
+ const currentOwner = readOwner(lease.leasePath);
3682
+ if (!currentOwner || !sameOwner2(currentOwner, lease.owner)) {
3683
+ await this.loseLeadership();
3684
+ return;
3685
+ }
3686
+ try {
3687
+ const nextOwner = { ...lease.owner, heartbeatAt: (/* @__PURE__ */ new Date()).toISOString() };
3688
+ if (!writeHeartbeat(lease.leasePath, nextOwner)) {
3689
+ await this.loseLeadership();
3690
+ return;
3691
+ }
3692
+ lease.owner = nextOwner;
3693
+ const refreshRequest = !this.stopping ? consumeRefreshRequest(lease.leasePath) : null;
3694
+ if (refreshRequest) {
3695
+ this.hooks.startAutoIndex("retrieval", refreshRequest.allowDisabledAutoIndex);
3696
+ }
3697
+ } catch (error) {
3698
+ const ownerAfterError = readOwner(lease.leasePath);
3699
+ if (hasActiveReclaimMarker(lease.leasePath, lease.owner) || !ownerAfterError || !sameOwner2(ownerAfterError, lease.owner)) {
3700
+ await this.loseLeadership();
3701
+ return;
3702
+ }
3703
+ console.error("[codebase-index] Failed to renew background worker lease:", error);
3704
+ }
3705
+ }
3706
+ async loseLeadership() {
3707
+ if (this.losingLeadership) return;
3708
+ this.losingLeadership = true;
3709
+ this.clearHeartbeat();
3710
+ await this.enqueue(async () => this.stopAfterLeadershipLoss());
3711
+ }
3712
+ async stopAfterLeadershipLoss() {
3713
+ const lease = this.lease;
3714
+ if (!lease) {
3715
+ this.losingLeadership = false;
3716
+ return;
3717
+ }
3718
+ try {
3719
+ const stopped = await this.stopLeaderWork(true);
3720
+ this.lease = null;
3721
+ this.losingLeadership = false;
3722
+ if (stopped.completed) {
3723
+ this.scheduleRetry();
3724
+ } else {
3725
+ void stopped.completion.then(() => this.scheduleRetry());
3726
+ }
3727
+ } catch (error) {
3728
+ console.error("[codebase-index] Failed to stop background work after losing its lease:", error);
3729
+ this.scheduleLostLeadershipTeardownRetry();
3730
+ }
3731
+ }
3732
+ scheduleRetry() {
3733
+ if (this.stopped || !this.canRun() || this.retryTimer) return;
3734
+ this.retryTimer = setTimeout(() => {
3735
+ this.retryTimer = null;
3736
+ this.start();
3737
+ }, RETRY_DELAY_MS);
3738
+ this.retryTimer.unref?.();
3739
+ }
3740
+ scheduleTeardownRetry() {
3741
+ if (!this.stopping || this.teardownRetryTimer) return;
3742
+ this.teardownRetryTimer = setTimeout(() => {
3743
+ this.teardownRetryTimer = null;
3744
+ void this.stop(this.stopAutoIndexOnTeardown).catch((error) => {
3745
+ console.error("[codebase-index] Failed to retry background worker teardown:", error);
3746
+ });
3747
+ }, RETRY_DELAY_MS);
3748
+ this.teardownRetryTimer.unref?.();
3749
+ }
3750
+ restartAfterTeardown() {
3751
+ if (!this.restartAfterStop || !this.canRun() || this.losingLeadership) return;
3752
+ this.restartAfterStop = false;
3753
+ this.stopped = false;
3754
+ this.start();
3755
+ }
3756
+ scheduleLostLeadershipTeardownRetry() {
3757
+ if (this.stopped || !this.losingLeadership || this.retryTimer) return;
3758
+ this.retryTimer = setTimeout(() => {
3759
+ this.retryTimer = null;
3760
+ void this.enqueue(async () => this.stopAfterLeadershipLoss());
3761
+ }, RETRY_DELAY_MS);
3762
+ this.retryTimer.unref?.();
3763
+ }
3764
+ clearHeartbeat() {
3765
+ if (!this.heartbeatTimer) return;
3766
+ clearInterval(this.heartbeatTimer);
3767
+ this.heartbeatTimer = null;
3768
+ }
3769
+ clearTimers() {
3770
+ this.clearHeartbeat();
3771
+ this.clearRetryTimer();
3772
+ if (this.teardownRetryTimer) {
3773
+ clearTimeout(this.teardownRetryTimer);
3774
+ this.teardownRetryTimer = null;
3775
+ }
3776
+ }
3777
+ clearRetryTimer() {
3778
+ if (!this.retryTimer) return;
3779
+ clearTimeout(this.retryTimer);
3780
+ this.retryTimer = null;
3781
+ }
3782
+ reportAcquireError(error) {
3783
+ const message = error instanceof Error ? error.message : String(error);
3784
+ if (this.reportedError === message) return;
3785
+ this.reportedError = message;
3786
+ console.error("[codebase-index] Failed to acquire background worker lease:", error);
3787
+ }
3788
+ };
3789
+ function configureBackgroundWorker(projectRoot, host, config, hooks, options = {}) {
3790
+ const projectKey = projectLookupKey(projectRoot, host);
3791
+ const identity = resolveIdentity(projectRoot, config, host);
3792
+ const key = controllerKey(identity, host);
3793
+ const previousKey = workerKeysByProject.get(projectKey);
3794
+ if (previousKey && previousKey !== key) {
3795
+ const previous = workers.get(previousKey);
3796
+ const previousBarrier = workerReplacementBarriers.get(projectKey) ?? Promise.resolve();
3797
+ const stopPrevious = previous?.stop(options.stopPreviousAutoIndex ?? true) ?? Promise.resolve();
3798
+ const activation = Promise.all([previousBarrier, stopPrevious]).then(() => void 0);
3799
+ workerReplacementBarriers.set(projectKey, activation);
3800
+ workers.delete(previousKey);
3801
+ const worker2 = new BackgroundWorkerController(projectRoot, host, config, hooks, identity);
3802
+ worker2.startAfter(activation);
3803
+ workers.set(key, worker2);
3804
+ workerKeysByProject.set(projectKey, key);
3805
+ return;
3806
+ }
3807
+ let worker = workers.get(key);
3808
+ if (!worker) {
3809
+ worker = new BackgroundWorkerController(projectRoot, host, config, hooks, identity);
3810
+ workers.set(key, worker);
3811
+ } else {
3812
+ worker.update(config, hooks, options);
3813
+ }
3814
+ workerKeysByProject.set(projectKey, key);
3815
+ worker.start();
3816
+ }
3817
+ function updateBackgroundWorkerConfig(projectRoot, host, config) {
3818
+ const projectKey = projectLookupKey(projectRoot, host);
3819
+ const key = workerKeysByProject.get(projectKey);
3820
+ const worker = key ? workers.get(key) : void 0;
3821
+ if (!worker) return;
3822
+ configureBackgroundWorker(projectRoot, host, config, worker.getHooksForConfig(config), {
3823
+ stopPreviousAutoIndex: false,
3824
+ restartAutoIndex: true
3825
+ });
3826
+ }
3827
+ function requestBackgroundWorkerRefresh(projectRoot, host, allowDisabledAutoIndex = false) {
3828
+ const key = workerKeysByProject.get(projectLookupKey(projectRoot, host));
3829
+ workers.get(key ?? "")?.requestRefresh(allowDisabledAutoIndex);
3830
+ }
3831
+ function isBackgroundWorkerManaged(projectRoot, host) {
3832
+ const key = workerKeysByProject.get(projectLookupKey(projectRoot, host));
3833
+ return key !== void 0 && workers.has(key);
3834
+ }
3835
+ function isBackgroundWorkerLeader(projectRoot, host) {
3836
+ const key = workerKeysByProject.get(projectLookupKey(projectRoot, host));
3837
+ return key !== void 0 && workers.get(key)?.isLeader() === true;
3838
+ }
3839
+ async function stopBackgroundWorker(projectRoot, host) {
3840
+ const projectKey = projectLookupKey(projectRoot, host);
3841
+ const key = workerKeysByProject.get(projectKey);
3842
+ const worker = key ? workers.get(key) : void 0;
3843
+ if (!worker) return;
3844
+ await worker.stop();
3845
+ }
3846
+
2977
3847
  // src/utils/files.ts
2978
3848
  var import_ignore = __toESM(require_ignore(), 1);
2979
3849
  var import_fs6 = require("fs");
2980
- var path10 = __toESM(require("path"), 1);
3850
+ var path11 = __toESM(require("path"), 1);
2981
3851
  var PROJECT_MARKERS = [
2982
3852
  ".git",
2983
3853
  "package.json",
@@ -2995,7 +3865,7 @@ var PROJECT_MARKERS = [
2995
3865
  ];
2996
3866
  function hasProjectMarker(projectRoot) {
2997
3867
  for (const marker of PROJECT_MARKERS) {
2998
- if ((0, import_fs6.existsSync)(path10.join(projectRoot, marker))) {
3868
+ if ((0, import_fs6.existsSync)(path11.join(projectRoot, marker))) {
2999
3869
  return true;
3000
3870
  }
3001
3871
  }
@@ -3022,13 +3892,40 @@ function createIgnoreFilter(projectRoot) {
3022
3892
  "**/*build*/**"
3023
3893
  ];
3024
3894
  ig.add(defaultIgnores);
3025
- const gitignorePath = path10.join(projectRoot, ".gitignore");
3895
+ const gitignorePath = path11.join(projectRoot, ".gitignore");
3026
3896
  if ((0, import_fs6.existsSync)(gitignorePath)) {
3027
3897
  const gitignoreContent = (0, import_fs6.readFileSync)(gitignorePath, "utf-8");
3028
3898
  ig.add(gitignoreContent);
3029
3899
  }
3030
3900
  return ig;
3031
3901
  }
3902
+ function toPosixRelativePath(relativePath) {
3903
+ return relativePath.split(path11.sep).join("/");
3904
+ }
3905
+ function matchesAnyGlob(filePath, patterns) {
3906
+ const normalized = toPosixRelativePath(filePath);
3907
+ return patterns.some((pattern) => matchGlob(normalized, pattern));
3908
+ }
3909
+ function isExcludedByPatterns(relativePath, excludePatterns) {
3910
+ return matchesAnyGlob(relativePath, excludePatterns);
3911
+ }
3912
+ function isExcludedDirectory(relativePath, excludePatterns) {
3913
+ const normalized = toPosixRelativePath(relativePath);
3914
+ if (matchesAnyGlob(normalized, excludePatterns)) {
3915
+ return true;
3916
+ }
3917
+ for (const pattern of excludePatterns) {
3918
+ const posixPattern = toPosixRelativePath(pattern).replace(/\/+$/, "");
3919
+ if (!posixPattern.endsWith("/**")) {
3920
+ continue;
3921
+ }
3922
+ const directoryPattern = posixPattern.slice(0, -3);
3923
+ if (directoryPattern && matchesAnyGlob(normalized, [directoryPattern])) {
3924
+ return true;
3925
+ }
3926
+ }
3927
+ return false;
3928
+ }
3032
3929
  function matchGlob(filePath, pattern) {
3033
3930
  if (pattern.startsWith("**/")) {
3034
3931
  const withoutPrefix = pattern.slice(3);
@@ -3049,8 +3946,8 @@ async function* walkDirectory(dir, projectRoot, includePatterns, excludePatterns
3049
3946
  const filesInDir = [];
3050
3947
  const subdirs = [];
3051
3948
  for (const entry of entries) {
3052
- const fullPath = path10.join(dir, entry.name);
3053
- const relativePath = path10.relative(projectRoot, fullPath);
3949
+ const fullPath = path11.join(dir, entry.name);
3950
+ const relativePath = toPosixRelativePath(path11.relative(projectRoot, fullPath));
3054
3951
  if (isHiddenPathSegment(entry.name)) {
3055
3952
  if (entry.isDirectory()) {
3056
3953
  skipped.push({ path: relativePath, reason: "excluded" });
@@ -3068,6 +3965,10 @@ async function* walkDirectory(dir, projectRoot, includePatterns, excludePatterns
3068
3965
  continue;
3069
3966
  }
3070
3967
  if (entry.isDirectory()) {
3968
+ if (isExcludedDirectory(relativePath, excludePatterns)) {
3969
+ skipped.push({ path: relativePath, reason: "excluded" });
3970
+ continue;
3971
+ }
3071
3972
  subdirs.push({ fullPath, relativePath });
3072
3973
  } else if (entry.isFile()) {
3073
3974
  const stat2 = await import_fs6.promises.stat(fullPath);
@@ -3075,20 +3976,11 @@ async function* walkDirectory(dir, projectRoot, includePatterns, excludePatterns
3075
3976
  skipped.push({ path: relativePath, reason: "too_large" });
3076
3977
  continue;
3077
3978
  }
3078
- for (const pattern of excludePatterns) {
3079
- if (matchGlob(relativePath, pattern)) {
3080
- skipped.push({ path: relativePath, reason: "excluded" });
3081
- continue;
3082
- }
3083
- }
3084
- let matched = false;
3085
- for (const pattern of includePatterns) {
3086
- if (matchGlob(relativePath, pattern)) {
3087
- matched = true;
3088
- break;
3089
- }
3979
+ if (isExcludedByPatterns(relativePath, excludePatterns)) {
3980
+ skipped.push({ path: relativePath, reason: "excluded" });
3981
+ continue;
3090
3982
  }
3091
- if (matched) {
3983
+ if (matchesAnyGlob(relativePath, includePatterns)) {
3092
3984
  filesInDir.push({ path: fullPath, size: stat2.size });
3093
3985
  }
3094
3986
  }
@@ -3099,7 +3991,7 @@ async function* walkDirectory(dir, projectRoot, includePatterns, excludePatterns
3099
3991
  yield f;
3100
3992
  }
3101
3993
  for (let i = options.maxFilesPerDirectory; i < filesInDir.length; i++) {
3102
- skipped.push({ path: path10.relative(projectRoot, filesInDir[i].path), reason: "excluded" });
3994
+ skipped.push({ path: toPosixRelativePath(path11.relative(projectRoot, filesInDir[i].path)), reason: "excluded" });
3103
3995
  }
3104
3996
  const canRecurse = options.maxDepth === -1 || currentDepth < options.maxDepth;
3105
3997
  if (canRecurse) {
@@ -3139,8 +4031,8 @@ async function collectFiles(projectRoot, includePatterns, excludePatterns, maxFi
3139
4031
  if (additionalRoots && additionalRoots.length > 0) {
3140
4032
  const normalizedRoots = /* @__PURE__ */ new Set();
3141
4033
  for (const kbRoot of additionalRoots) {
3142
- const resolved = path10.normalize(
3143
- path10.isAbsolute(kbRoot) ? kbRoot : path10.resolve(projectRoot, kbRoot)
4034
+ const resolved = path11.normalize(
4035
+ path11.isAbsolute(kbRoot) ? kbRoot : path11.resolve(projectRoot, kbRoot)
3144
4036
  );
3145
4037
  normalizedRoots.add(resolved);
3146
4038
  }
@@ -3181,7 +4073,7 @@ function getErrorMessage(error) {
3181
4073
  return error instanceof Error ? error.message : String(error);
3182
4074
  }
3183
4075
  function runCommand(file, args, options) {
3184
- return new Promise((resolve19, reject) => {
4076
+ return new Promise((resolve20, reject) => {
3185
4077
  childProcess.execFile(
3186
4078
  file,
3187
4079
  args,
@@ -3191,7 +4083,7 @@ function runCommand(file, args, options) {
3191
4083
  reject(error);
3192
4084
  return;
3193
4085
  }
3194
- resolve19(stdout);
4086
+ resolve20(stdout);
3195
4087
  }
3196
4088
  );
3197
4089
  });
@@ -3283,8 +4175,8 @@ var AutoIndexCancelledError = class extends Error {
3283
4175
  function now() {
3284
4176
  return (/* @__PURE__ */ new Date()).toISOString();
3285
4177
  }
3286
- function canonicalizePath(targetPath) {
3287
- const resolved = path11.resolve(targetPath);
4178
+ function canonicalizePath2(targetPath) {
4179
+ const resolved = path12.resolve(targetPath);
3288
4180
  if ((0, import_fs7.existsSync)(resolved)) {
3289
4181
  try {
3290
4182
  return import_fs7.realpathSync.native(resolved);
@@ -3292,20 +4184,20 @@ function canonicalizePath(targetPath) {
3292
4184
  return resolved;
3293
4185
  }
3294
4186
  }
3295
- const parent = path11.dirname(resolved);
4187
+ const parent = path12.dirname(resolved);
3296
4188
  if (parent === resolved) return resolved;
3297
- return path11.join(canonicalizePath(parent), path11.basename(resolved));
4189
+ return path12.join(canonicalizePath2(parent), path12.basename(resolved));
3298
4190
  }
3299
4191
  function isHomeDirectory(projectRoot) {
3300
- return canonicalizePath(projectRoot) === canonicalizePath(os3.homedir());
4192
+ return canonicalizePath2(projectRoot) === canonicalizePath2(os4.homedir());
3301
4193
  }
3302
- function projectLookupKey(projectRoot, host) {
3303
- return `${host}::${canonicalizePath(projectRoot)}`;
4194
+ function projectLookupKey2(projectRoot, host) {
4195
+ return `${host}::${canonicalizePath2(projectRoot)}`;
3304
4196
  }
3305
4197
  function coordinatorKey(projectRoot, config, host) {
3306
- const canonicalProjectRoot = canonicalizePath(projectRoot);
4198
+ const canonicalProjectRoot = canonicalizePath2(projectRoot);
3307
4199
  const indexPath = resolveProjectIndexPath(projectRoot, config.scope, host);
3308
- return `${canonicalizePath(indexPath)}::${canonicalProjectRoot}`;
4200
+ return `${canonicalizePath2(indexPath)}::${canonicalProjectRoot}`;
3309
4201
  }
3310
4202
  function getProjectSafety(projectRoot, config) {
3311
4203
  if (isHomeDirectory(projectRoot)) {
@@ -3336,10 +4228,10 @@ function safeFailureMessage(error) {
3336
4228
  }
3337
4229
  function cancellableDelay(delayMs, signal) {
3338
4230
  if (signal.aborted) return Promise.reject(new AutoIndexCancelledError());
3339
- return new Promise((resolve19, reject) => {
4231
+ return new Promise((resolve20, reject) => {
3340
4232
  const timer = setTimeout(() => {
3341
4233
  signal.removeEventListener("abort", onAbort);
3342
- resolve19();
4234
+ resolve20();
3343
4235
  }, delayMs);
3344
4236
  timer.unref?.();
3345
4237
  const onAbort = () => {
@@ -3351,18 +4243,44 @@ function cancellableDelay(delayMs, signal) {
3351
4243
  }
3352
4244
  function withTimeout(promise, timeoutMs) {
3353
4245
  if (timeoutMs <= 0) return Promise.resolve(void 0);
3354
- return new Promise((resolve19) => {
3355
- const timer = setTimeout(() => resolve19(void 0), timeoutMs);
4246
+ return new Promise((resolve20) => {
4247
+ const timer = setTimeout(() => resolve20(void 0), timeoutMs);
3356
4248
  timer.unref?.();
3357
4249
  void promise.then((value) => {
3358
4250
  clearTimeout(timer);
3359
- resolve19(value);
4251
+ resolve20(value);
3360
4252
  }, () => {
3361
4253
  clearTimeout(timer);
3362
- resolve19(void 0);
4254
+ resolve20(void 0);
3363
4255
  });
3364
4256
  });
3365
4257
  }
4258
+ function settlesWithin(promise, timeoutMs) {
4259
+ if (timeoutMs <= 0) return Promise.resolve(false);
4260
+ return new Promise((resolve20) => {
4261
+ let settled = false;
4262
+ const timer = setTimeout(() => {
4263
+ if (settled) return;
4264
+ settled = true;
4265
+ resolve20(false);
4266
+ }, timeoutMs);
4267
+ timer.unref?.();
4268
+ void promise.then(
4269
+ () => {
4270
+ if (settled) return;
4271
+ settled = true;
4272
+ clearTimeout(timer);
4273
+ resolve20(true);
4274
+ },
4275
+ () => {
4276
+ if (settled) return;
4277
+ settled = true;
4278
+ clearTimeout(timer);
4279
+ resolve20(true);
4280
+ }
4281
+ );
4282
+ });
4283
+ }
3366
4284
  function requestPriority(request) {
3367
4285
  if (request.force) return 4;
3368
4286
  if (request.source === "manual") return 3;
@@ -3373,6 +4291,7 @@ function mergeRequests(current, next) {
3373
4291
  if (!current) return next;
3374
4292
  const preferred = requestPriority(next) > requestPriority(current) ? next : current;
3375
4293
  return {
4294
+ allowDisabledAutoIndex: current.allowDisabledAutoIndex || next.allowDisabledAutoIndex,
3376
4295
  checkFreshness: current.checkFreshness && next.checkFreshness,
3377
4296
  force: current.force || next.force,
3378
4297
  onProgress: next.onProgress ?? current.onProgress,
@@ -3434,11 +4353,11 @@ var AutoIndexCoordinator = class {
3434
4353
  progress: this.status.progress ? { ...this.status.progress } : void 0
3435
4354
  };
3436
4355
  }
3437
- start(source) {
4356
+ start(source, allowDisabledAutoIndex = false) {
3438
4357
  this.refreshSafety();
3439
- if (!this.registration.config.indexing.autoIndex || !this.registration.safeToRun) return null;
4358
+ if (!this.registration.config.indexing.autoIndex && !allowDisabledAutoIndex || !this.registration.safeToRun) return null;
3440
4359
  if (this.status.state === "failed") return this.inFlight;
3441
- return this.request({ checkFreshness: true, force: false, source });
4360
+ return this.request({ allowDisabledAutoIndex, checkFreshness: true, force: false, source });
3442
4361
  }
3443
4362
  request(request) {
3444
4363
  if (this.stopped) {
@@ -3513,13 +4432,15 @@ var AutoIndexCoordinator = class {
3513
4432
  retryAttempt: void 0
3514
4433
  });
3515
4434
  const inFlight = this.inFlight;
3516
- if (inFlight) {
3517
- if (waitForCompletion) {
3518
- await inFlight;
3519
- } else {
3520
- await withTimeout(inFlight, SHUTDOWN_WAIT_MS);
3521
- }
4435
+ const completion = inFlight ? inFlight.then(() => void 0, () => void 0) : Promise.resolve();
4436
+ if (!inFlight) {
4437
+ return { completed: true, completion };
4438
+ }
4439
+ if (waitForCompletion) {
4440
+ await completion;
4441
+ return { completed: true, completion };
3522
4442
  }
4443
+ return { completed: await settlesWithin(completion, SHUTDOWN_WAIT_MS), completion };
3523
4444
  }
3524
4445
  startRequest(request) {
3525
4446
  if (this.stopped || !this.canRun(request)) {
@@ -3708,7 +4629,7 @@ var AutoIndexCoordinator = class {
3708
4629
  if (request.source === "manual" || request.source === "watcher") {
3709
4630
  return true;
3710
4631
  }
3711
- return this.registration.safeToRun && this.registration.config.indexing.autoIndex;
4632
+ return this.registration.safeToRun && (this.registration.config.indexing.autoIndex || request.allowDisabledAutoIndex === true);
3712
4633
  }
3713
4634
  shouldDeferForBattery(request) {
3714
4635
  return this.registration.backgroundIndexingPolicy !== null && (request.source === "startup" || request.source === "watcher");
@@ -3741,17 +4662,17 @@ var AutoIndexCoordinator = class {
3741
4662
  }
3742
4663
  }
3743
4664
  waitForBatteryRetry(delayMs) {
3744
- return new Promise((resolve19) => {
4665
+ return new Promise((resolve20) => {
3745
4666
  const timer = setTimeout(() => {
3746
4667
  if (this.batteryRetryTimer === timer) {
3747
4668
  this.batteryRetryTimer = null;
3748
4669
  this.resolveBatteryRetry = null;
3749
4670
  }
3750
- resolve19();
4671
+ resolve20();
3751
4672
  }, delayMs);
3752
4673
  timer.unref?.();
3753
4674
  this.batteryRetryTimer = timer;
3754
- this.resolveBatteryRetry = resolve19;
4675
+ this.resolveBatteryRetry = resolve20;
3755
4676
  });
3756
4677
  }
3757
4678
  cancelBatteryRetry() {
@@ -3759,9 +4680,9 @@ var AutoIndexCoordinator = class {
3759
4680
  clearTimeout(this.batteryRetryTimer);
3760
4681
  this.batteryRetryTimer = null;
3761
4682
  }
3762
- const resolve19 = this.resolveBatteryRetry;
4683
+ const resolve20 = this.resolveBatteryRetry;
3763
4684
  this.resolveBatteryRetry = null;
3764
- resolve19?.();
4685
+ resolve20?.();
3765
4686
  }
3766
4687
  finishBatteryCheck(batteryCheck) {
3767
4688
  if (this.batteryCheck !== batteryCheck) return;
@@ -3774,12 +4695,25 @@ var AutoIndexCoordinator = class {
3774
4695
  }
3775
4696
  };
3776
4697
  function getCoordinator(projectRoot, host) {
3777
- const key = coordinatorKeysByProject.get(projectLookupKey(projectRoot, host));
4698
+ const key = coordinatorKeysByProject.get(projectLookupKey2(projectRoot, host));
3778
4699
  return key ? coordinators.get(key) ?? null : null;
3779
4700
  }
3780
- function configureAutoIndex(projectRoot, host, config, getIndexer) {
3781
- const projectKey = projectLookupKey(projectRoot, host);
4701
+ function synchronizeBackgroundWorker(projectRoot, host, config, safeToRun) {
4702
+ if (safeToRun) {
4703
+ updateBackgroundWorkerConfig(projectRoot, host, config);
4704
+ return;
4705
+ }
4706
+ void stopBackgroundWorker(projectRoot, host).catch((error) => {
4707
+ console.error("[codebase-index] Failed to stop background worker after project safety changed:", error);
4708
+ });
4709
+ }
4710
+ function configureAutoIndex(projectRoot, host, config, getIndexer, options = {}) {
4711
+ const projectKey = projectLookupKey2(projectRoot, host);
3782
4712
  const safety = getProjectSafety(projectRoot, config);
4713
+ const synchronizeWorker = options.synchronizeBackgroundWorker ?? true;
4714
+ if (options.preserveManagedWorker === true && isBackgroundWorkerManaged(projectRoot, host)) {
4715
+ return;
4716
+ }
3783
4717
  const registration = {
3784
4718
  backgroundIndexingPolicy: createBackgroundIndexingPolicy(
3785
4719
  config.indexing.pauseBackgroundIndexingOnBattery
@@ -3797,6 +4731,9 @@ function configureAutoIndex(projectRoot, host, config, getIndexer) {
3797
4731
  const stopPrevious = previousCoordinator?.stop(true) ?? Promise.resolve();
3798
4732
  const activation = Promise.all([previousBarrier, stopPrevious]).then(() => void 0);
3799
4733
  coordinatorReplacementBarriers.set(projectKey, activation);
4734
+ if (synchronizeWorker) {
4735
+ synchronizeBackgroundWorker(projectRoot, host, config, safety.safeToRun);
4736
+ }
3800
4737
  coordinators.delete(previousKey);
3801
4738
  const coordinator2 = new AutoIndexCoordinator(registration);
3802
4739
  coordinator2.activateAfter(activation);
@@ -3812,6 +4749,9 @@ function configureAutoIndex(projectRoot, host, config, getIndexer) {
3812
4749
  coordinator.update(registration);
3813
4750
  }
3814
4751
  coordinatorKeysByProject.set(projectKey, key);
4752
+ if (synchronizeWorker) {
4753
+ synchronizeBackgroundWorker(projectRoot, host, config, safety.safeToRun);
4754
+ }
3815
4755
  }
3816
4756
  function runCoordinatedIndex(projectRoot, host, force, onProgress) {
3817
4757
  return getCoordinator(projectRoot, host)?.request({
@@ -3846,15 +4786,23 @@ async function waitForAutoIndexForRetrieval(projectRoot, host) {
3846
4786
  };
3847
4787
  }
3848
4788
  try {
3849
- if (await hasReadableCurrentIndex(coordinator)) return { ready: true };
4789
+ const readiness = await getSearchReadiness(coordinator);
4790
+ if (readiness.searchable) {
4791
+ return { ready: true };
4792
+ }
4793
+ if (readiness.blocked) return unavailableSnapshotResult(readiness.reason);
3850
4794
  } catch {
3851
4795
  }
3852
- const job = coordinator.start("retrieval") ?? coordinator.currentJob();
4796
+ const job = startRetrievalRefresh(projectRoot, host, coordinator);
3853
4797
  if (job) {
3854
4798
  await withTimeout(job, coordinator.getWaitMs());
4799
+ } else if (isBackgroundWorkerManaged(projectRoot, host)) {
4800
+ await waitForPublishedSnapshot(coordinator, coordinator.getWaitMs());
3855
4801
  }
3856
4802
  try {
3857
- if (await hasReadableCurrentIndex(coordinator)) return { ready: true };
4803
+ const readiness = await getSearchReadiness(coordinator);
4804
+ if (readiness.searchable) return { ready: true };
4805
+ if (readiness.blocked) return unavailableSnapshotResult(readiness.reason);
3858
4806
  } catch {
3859
4807
  }
3860
4808
  const status = coordinator.snapshot();
@@ -3875,18 +4823,45 @@ async function waitForAutoIndexForRetrieval(projectRoot, host) {
3875
4823
  text: `Automatic indexing is ${status.state}. Retry shortly or call index_status for progress. You can also run index_codebase explicitly.`
3876
4824
  };
3877
4825
  }
3878
- async function hasReadableCurrentIndex(coordinator) {
4826
+ async function getSearchReadiness(coordinator) {
3879
4827
  const indexer = coordinator.getIndexer();
3880
4828
  if (indexer.getIndexFreshness) {
3881
4829
  const freshness = await indexer.getIndexFreshness();
3882
- return freshness.readable && freshness.current;
4830
+ const searchable = freshness.readable && freshness.current && freshness.reason === "current";
4831
+ return {
4832
+ blocked: freshness.reason === "unreadable" || freshness.reason === "incompatible" || freshness.reason === "failed-batches" || freshness.reason === "migration-required",
4833
+ reason: freshness.reason,
4834
+ searchable
4835
+ };
4836
+ }
4837
+ const indexed = (await indexer.getStatus()).indexed;
4838
+ return { blocked: false, searchable: indexed };
4839
+ }
4840
+ function unavailableSnapshotResult(reason) {
4841
+ 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.";
4842
+ return {
4843
+ ready: false,
4844
+ text: `${detail} Run index_codebase before retrying retrieval.`
4845
+ };
4846
+ }
4847
+ function startRetrievalRefresh(projectRoot, host, coordinator) {
4848
+ if (isBackgroundWorkerManaged(projectRoot, host)) {
4849
+ requestBackgroundWorkerRefresh(projectRoot, host, true);
4850
+ return isBackgroundWorkerLeader(projectRoot, host) ? coordinator.currentJob() : null;
4851
+ }
4852
+ return coordinator.start("retrieval") ?? coordinator.currentJob();
4853
+ }
4854
+ async function waitForPublishedSnapshot(coordinator, waitMs) {
4855
+ const deadline = Date.now() + waitMs;
4856
+ while (Date.now() < deadline) {
4857
+ if ((await getSearchReadiness(coordinator)).searchable) return;
4858
+ await new Promise((resolve20) => setTimeout(resolve20, Math.min(250, deadline - Date.now())));
3883
4859
  }
3884
- return (await indexer.getStatus()).indexed;
3885
4860
  }
3886
4861
 
3887
4862
  // src/tools/config-state.ts
3888
4863
  var import_fs8 = require("fs");
3889
- var path12 = __toESM(require("path"), 1);
4864
+ var path13 = __toESM(require("path"), 1);
3890
4865
  function normalizeKnowledgeBasePaths(config, projectRoot) {
3891
4866
  const normalized = { ...config };
3892
4867
  if (Array.isArray(normalized.knowledgeBases)) {
@@ -3908,7 +4883,7 @@ function loadRuntimeConfig(projectRoot, host) {
3908
4883
 
3909
4884
  // src/indexer/index.ts
3910
4885
  var import_fs12 = require("fs");
3911
- var path19 = __toESM(require("path"), 1);
4886
+ var path20 = __toESM(require("path"), 1);
3912
4887
  var import_perf_hooks = require("perf_hooks");
3913
4888
  var import_child_process4 = require("child_process");
3914
4889
  var import_util4 = require("util");
@@ -3935,7 +4910,7 @@ function pTimeout(promise, options) {
3935
4910
  } = options;
3936
4911
  let timer;
3937
4912
  let abortHandler;
3938
- const wrappedPromise = new Promise((resolve19, reject) => {
4913
+ const wrappedPromise = new Promise((resolve20, reject) => {
3939
4914
  if (typeof milliseconds !== "number" || Math.sign(milliseconds) !== 1) {
3940
4915
  throw new TypeError(`Expected \`milliseconds\` to be a positive number, got \`${milliseconds}\``);
3941
4916
  }
@@ -3949,7 +4924,7 @@ function pTimeout(promise, options) {
3949
4924
  };
3950
4925
  signal.addEventListener("abort", abortHandler, { once: true });
3951
4926
  }
3952
- promise.then(resolve19, reject);
4927
+ promise.then(resolve20, reject);
3953
4928
  if (milliseconds === Number.POSITIVE_INFINITY) {
3954
4929
  return;
3955
4930
  }
@@ -3957,7 +4932,7 @@ function pTimeout(promise, options) {
3957
4932
  timer = customTimers.setTimeout.call(void 0, () => {
3958
4933
  if (fallback) {
3959
4934
  try {
3960
- resolve19(fallback());
4935
+ resolve20(fallback());
3961
4936
  } catch (error) {
3962
4937
  reject(error);
3963
4938
  }
@@ -3967,7 +4942,7 @@ function pTimeout(promise, options) {
3967
4942
  promise.cancel();
3968
4943
  }
3969
4944
  if (message === false) {
3970
- resolve19();
4945
+ resolve20();
3971
4946
  } else if (message instanceof Error) {
3972
4947
  reject(message);
3973
4948
  } else {
@@ -4369,7 +5344,7 @@ var PQueue = class extends import_index.default {
4369
5344
  // Assign unique ID if not provided
4370
5345
  id: options.id ?? (this.#idAssigner++).toString()
4371
5346
  };
4372
- return new Promise((resolve19, reject) => {
5347
+ return new Promise((resolve20, reject) => {
4373
5348
  const taskSymbol = /* @__PURE__ */ Symbol(`task-${options.id}`);
4374
5349
  let cleanupQueueAbortHandler = () => void 0;
4375
5350
  const run = async () => {
@@ -4409,7 +5384,7 @@ var PQueue = class extends import_index.default {
4409
5384
  })]);
4410
5385
  }
4411
5386
  const result = await operation;
4412
- resolve19(result);
5387
+ resolve20(result);
4413
5388
  this.emit("completed", result);
4414
5389
  } catch (error) {
4415
5390
  reject(error);
@@ -4597,13 +5572,13 @@ var PQueue = class extends import_index.default {
4597
5572
  });
4598
5573
  }
4599
5574
  async #onEvent(event, filter) {
4600
- return new Promise((resolve19) => {
5575
+ return new Promise((resolve20) => {
4601
5576
  const listener = () => {
4602
5577
  if (filter && !filter()) {
4603
5578
  return;
4604
5579
  }
4605
5580
  this.off(event, listener);
4606
- resolve19();
5581
+ resolve20();
4607
5582
  };
4608
5583
  this.on(event, listener);
4609
5584
  });
@@ -4889,7 +5864,7 @@ async function onAttemptFailure({ error, attemptNumber, retriesConsumed, startTi
4889
5864
  const finalDelay = Math.min(delayTime, remainingTime);
4890
5865
  options.signal?.throwIfAborted();
4891
5866
  if (finalDelay > 0) {
4892
- await new Promise((resolve19, reject) => {
5867
+ await new Promise((resolve20, reject) => {
4893
5868
  const onAbort = () => {
4894
5869
  clearTimeout(timeoutToken);
4895
5870
  options.signal?.removeEventListener("abort", onAbort);
@@ -4897,7 +5872,7 @@ async function onAttemptFailure({ error, attemptNumber, retriesConsumed, startTi
4897
5872
  };
4898
5873
  const timeoutToken = setTimeout(() => {
4899
5874
  options.signal?.removeEventListener("abort", onAbort);
4900
- resolve19();
5875
+ resolve20();
4901
5876
  }, finalDelay);
4902
5877
  if (options.unref) {
4903
5878
  timeoutToken.unref?.();
@@ -4959,10 +5934,10 @@ async function pRetry(input, options = {}) {
4959
5934
 
4960
5935
  // src/embeddings/detector.ts
4961
5936
  var import_fs9 = require("fs");
4962
- var path13 = __toESM(require("path"), 1);
4963
- var os4 = __toESM(require("os"), 1);
5937
+ var path14 = __toESM(require("path"), 1);
5938
+ var os5 = __toESM(require("os"), 1);
4964
5939
  function getOpenCodeAuthPath() {
4965
- return path13.join(os4.homedir(), ".local", "share", "opencode", "auth.json");
5940
+ return path14.join(os5.homedir(), ".local", "share", "opencode", "auth.json");
4966
5941
  }
4967
5942
  function loadOpenCodeAuth() {
4968
5943
  const authPath = getOpenCodeAuthPath();
@@ -5259,17 +6234,17 @@ function validateExternalUrl(urlString) {
5259
6234
  if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
5260
6235
  return { valid: false, reason: `Blocked protocol: ${parsed.protocol}` };
5261
6236
  }
5262
- const hostname2 = parsed.hostname.toLowerCase();
5263
- if (BLOCKED_HOSTNAMES.has(hostname2)) {
5264
- return { valid: false, reason: `Blocked: cloud metadata service (${hostname2})` };
6237
+ const hostname3 = parsed.hostname.toLowerCase();
6238
+ if (BLOCKED_HOSTNAMES.has(hostname3)) {
6239
+ return { valid: false, reason: `Blocked: cloud metadata service (${hostname3})` };
5265
6240
  }
5266
6241
  for (const pattern of BLOCKED_METADATA_IPS) {
5267
- if (pattern.test(hostname2)) {
5268
- return { valid: false, reason: `Blocked: cloud metadata IP (${hostname2})` };
6242
+ if (pattern.test(hostname3)) {
6243
+ return { valid: false, reason: `Blocked: cloud metadata IP (${hostname3})` };
5269
6244
  }
5270
6245
  }
5271
- if (/^169\.254\./.test(hostname2)) {
5272
- return { valid: false, reason: `Blocked: link-local address (${hostname2})` };
6246
+ if (/^169\.254\./.test(hostname3)) {
6247
+ return { valid: false, reason: `Blocked: link-local address (${hostname3})` };
5273
6248
  }
5274
6249
  return { valid: true };
5275
6250
  }
@@ -6361,8 +7336,8 @@ function extractParamNames(params) {
6361
7336
  }
6362
7337
 
6363
7338
  // src/native/binding.ts
6364
- var os5 = __toESM(require("os"), 1);
6365
- var path14 = __toESM(require("path"), 1);
7339
+ var os6 = __toESM(require("os"), 1);
7340
+ var path15 = __toESM(require("path"), 1);
6366
7341
  var module2 = __toESM(require("module"), 1);
6367
7342
  var import_node_url = require("url");
6368
7343
 
@@ -6399,7 +7374,7 @@ var STABLE_NATIVE_BINARY_NAME = IDENTITY_CATALOG.native.binaryName;
6399
7374
 
6400
7375
  // src/native/binding.ts
6401
7376
  var import_meta = {};
6402
- function getNativeBindingFilename(platform2 = os5.platform(), arch2 = os5.arch()) {
7377
+ function getNativeBindingFilename(platform2 = os6.platform(), arch2 = os6.arch()) {
6403
7378
  if (platform2 === "darwin" && arch2 === "arm64") {
6404
7379
  return `${STABLE_NATIVE_BINARY_NAME}.darwin-arm64.node`;
6405
7380
  }
@@ -6417,25 +7392,25 @@ function getNativeBindingFilename(platform2 = os5.platform(), arch2 = os5.arch()
6417
7392
  }
6418
7393
  throw new Error(`Unsupported platform: ${platform2}-${arch2}`);
6419
7394
  }
6420
- function resolveNativeBindingPath(packageRoot, platform2 = os5.platform(), arch2 = os5.arch()) {
6421
- return path14.join(packageRoot, "native", getNativeBindingFilename(platform2, arch2));
7395
+ function resolveNativeBindingPath(packageRoot, platform2 = os6.platform(), arch2 = os6.arch()) {
7396
+ return path15.join(packageRoot, "native", getNativeBindingFilename(platform2, arch2));
6422
7397
  }
6423
7398
  function getNativeBinding() {
6424
7399
  let currentDir;
6425
7400
  let requireTarget;
6426
7401
  if (typeof import_meta !== "undefined" && import_meta.url) {
6427
- currentDir = path14.dirname((0, import_node_url.fileURLToPath)(import_meta.url));
7402
+ currentDir = path15.dirname((0, import_node_url.fileURLToPath)(import_meta.url));
6428
7403
  requireTarget = import_meta.url;
6429
7404
  } else if (typeof __dirname !== "undefined") {
6430
7405
  currentDir = __dirname;
6431
7406
  requireTarget = __filename;
6432
7407
  } else {
6433
7408
  currentDir = process.cwd();
6434
- requireTarget = path14.join(currentDir, "index.js");
7409
+ requireTarget = path15.join(currentDir, "index.js");
6435
7410
  }
6436
7411
  const normalizedDir = currentDir.replace(/\\/g, "/");
6437
- const isDevMode = normalizedDir.includes("/src/native") || currentDir.includes(path14.join("src", "native"));
6438
- const packageRoot = isDevMode ? path14.resolve(currentDir, "../..") : path14.resolve(currentDir, "..");
7412
+ const isDevMode = normalizedDir.includes("/src/native") || currentDir.includes(path15.join("src", "native"));
7413
+ const packageRoot = isDevMode ? path15.resolve(currentDir, "../..") : path15.resolve(currentDir, "..");
6439
7414
  const nativePath = resolveNativeBindingPath(packageRoot);
6440
7415
  const require2 = module2.createRequire(requireTarget);
6441
7416
  return require2(nativePath);
@@ -7019,8 +7994,8 @@ var Database = class _Database {
7019
7994
 
7020
7995
  // src/git/branch-materialization.ts
7021
7996
  var import_fs10 = require("fs");
7022
- var os6 = __toESM(require("os"), 1);
7023
- var path15 = __toESM(require("path"), 1);
7997
+ var os7 = __toESM(require("os"), 1);
7998
+ var path16 = __toESM(require("path"), 1);
7024
7999
 
7025
8000
  // src/git/branch-resolution.ts
7026
8001
  var import_child_process = require("child_process");
@@ -7339,13 +8314,13 @@ async function isWorktreeRegistered(projectRoot, worktreePath) {
7339
8314
  return false;
7340
8315
  }
7341
8316
  function isPathWithinRoot(filePath, rootPath) {
7342
- const relative12 = path15.relative(path15.resolve(rootPath), path15.resolve(filePath));
7343
- return relative12 === "" || !relative12.startsWith(`..${path15.sep}`) && relative12 !== ".." && !path15.isAbsolute(relative12);
8317
+ const relative12 = path16.relative(path16.resolve(rootPath), path16.resolve(filePath));
8318
+ return relative12 === "" || !relative12.startsWith(`..${path16.sep}`) && relative12 !== ".." && !path16.isAbsolute(relative12);
7344
8319
  }
7345
8320
  async function pruneExactMissingWorktreeRegistration(projectRoot, worktreePath) {
7346
8321
  if (await pathExists(worktreePath)) return false;
7347
8322
  const commonDir = await runGit(projectRoot, ["rev-parse", "--path-format=absolute", "--git-common-dir"]);
7348
- const registrationsRoot = path15.join(commonDir, "worktrees");
8323
+ const registrationsRoot = path16.join(commonDir, "worktrees");
7349
8324
  let entries;
7350
8325
  try {
7351
8326
  entries = await import_fs10.promises.readdir(registrationsRoot, { withFileTypes: true });
@@ -7356,16 +8331,16 @@ async function pruneExactMissingWorktreeRegistration(projectRoot, worktreePath)
7356
8331
  const target = canonicalizePathForComparison(worktreePath);
7357
8332
  for (const entry of entries) {
7358
8333
  if (!entry.isDirectory()) continue;
7359
- const registrationPath = path15.join(registrationsRoot, entry.name);
8334
+ const registrationPath = path16.join(registrationsRoot, entry.name);
7360
8335
  if (!isPathWithinRoot(registrationPath, registrationsRoot)) continue;
7361
8336
  let gitdirPath;
7362
8337
  try {
7363
- gitdirPath = (await import_fs10.promises.readFile(path15.join(registrationPath, "gitdir"), "utf8")).trim();
8338
+ gitdirPath = (await import_fs10.promises.readFile(path16.join(registrationPath, "gitdir"), "utf8")).trim();
7364
8339
  } catch {
7365
8340
  continue;
7366
8341
  }
7367
- const resolvedGitdirPath = path15.isAbsolute(gitdirPath) ? gitdirPath : path15.resolve(registrationPath, gitdirPath);
7368
- if (canonicalizePathForComparison(path15.dirname(resolvedGitdirPath)) !== target) continue;
8342
+ const resolvedGitdirPath = path16.isAbsolute(gitdirPath) ? gitdirPath : path16.resolve(registrationPath, gitdirPath);
8343
+ if (canonicalizePathForComparison(path16.dirname(resolvedGitdirPath)) !== target) continue;
7369
8344
  await import_fs10.promises.rm(registrationPath, { recursive: true, force: true });
7370
8345
  return true;
7371
8346
  }
@@ -7383,7 +8358,7 @@ async function removeWorktree(projectRoot, worktreePath) {
7383
8358
  registered = await isWorktreeRegistered(projectRoot, worktreePath);
7384
8359
  } catch (error) {
7385
8360
  errors.push(asError(error));
7386
- throw new AggregateError(errors, `Could not verify temporary worktree deregistration; preserved ${path15.dirname(worktreePath)}`);
8361
+ throw new AggregateError(errors, `Could not verify temporary worktree deregistration; preserved ${path16.dirname(worktreePath)}`);
7387
8362
  }
7388
8363
  if (registered) {
7389
8364
  try {
@@ -7399,7 +8374,7 @@ async function removeWorktree(projectRoot, worktreePath) {
7399
8374
  registered = await isWorktreeRegistered(projectRoot, worktreePath);
7400
8375
  } catch (error) {
7401
8376
  errors.push(asError(error));
7402
- throw new AggregateError(errors, `Could not verify temporary worktree deregistration; preserved ${path15.dirname(worktreePath)}`);
8377
+ throw new AggregateError(errors, `Could not verify temporary worktree deregistration; preserved ${path16.dirname(worktreePath)}`);
7403
8378
  }
7404
8379
  }
7405
8380
  if (registered && !await pathExists(worktreePath)) {
@@ -7412,13 +8387,13 @@ async function removeWorktree(projectRoot, worktreePath) {
7412
8387
  }
7413
8388
  if (registered) {
7414
8389
  errors.push(new Error(`Temporary worktree remains registered: ${worktreePath}`));
7415
- throw new AggregateError(errors, `Failed to deregister temporary worktree; preserved ${path15.dirname(worktreePath)}`);
8390
+ throw new AggregateError(errors, `Failed to deregister temporary worktree; preserved ${path16.dirname(worktreePath)}`);
7416
8391
  }
7417
8392
  try {
7418
- await import_fs10.promises.rm(path15.dirname(worktreePath), { recursive: true, force: true });
8393
+ await import_fs10.promises.rm(path16.dirname(worktreePath), { recursive: true, force: true });
7419
8394
  } catch (error) {
7420
8395
  errors.push(asError(error));
7421
- throw new AggregateError(errors, `Deregistered the temporary worktree but could not remove ${path15.dirname(worktreePath)}`);
8396
+ throw new AggregateError(errors, `Deregistered the temporary worktree but could not remove ${path16.dirname(worktreePath)}`);
7422
8397
  }
7423
8398
  }
7424
8399
  async function cleanupTemporaryWorktree(projectRoot, worktreePath, temporaryRoot) {
@@ -7454,9 +8429,9 @@ async function withMaterializedBranch(request, callback) {
7454
8429
  `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.`
7455
8430
  );
7456
8431
  }
7457
- const temporaryRoot = await import_fs10.promises.mkdtemp(path15.join(os6.tmpdir(), "codebase-index-branch-"));
7458
- const worktreePath = path15.join(temporaryRoot, "worktree");
7459
- const hooksPath = path15.join(temporaryRoot, "hooks");
8432
+ const temporaryRoot = await import_fs10.promises.mkdtemp(path16.join(os7.tmpdir(), "codebase-index-branch-"));
8433
+ const worktreePath = path16.join(temporaryRoot, "worktree");
8434
+ const hooksPath = path16.join(temporaryRoot, "hooks");
7460
8435
  await import_fs10.promises.mkdir(hooksPath);
7461
8436
  const info = {
7462
8437
  branch: request.branch,
@@ -7508,7 +8483,7 @@ async function withMaterializedBranch(request, callback) {
7508
8483
  // src/tools/changed-files.ts
7509
8484
  var import_child_process2 = require("child_process");
7510
8485
  var import_fs11 = require("fs");
7511
- var path16 = __toESM(require("path"), 1);
8486
+ var path17 = __toESM(require("path"), 1);
7512
8487
  var import_util2 = require("util");
7513
8488
  var execFileAsync2 = (0, import_util2.promisify)(import_child_process2.execFile);
7514
8489
  var GH_PR_VIEW_FIELDS = [
@@ -7650,7 +8625,7 @@ function getHeadRepositoryIdentity(data, host) {
7650
8625
  return `${host}/${owner.toLowerCase()}/${repository.replace(/\.git$/i, "").toLowerCase()}`;
7651
8626
  }
7652
8627
  function getLocalRepositoryIdentity(projectRoot) {
7653
- let canonicalRoot = path16.resolve(projectRoot);
8628
+ let canonicalRoot = path17.resolve(projectRoot);
7654
8629
  try {
7655
8630
  canonicalRoot = import_fs11.realpathSync.native(canonicalRoot);
7656
8631
  } catch {
@@ -7711,17 +8686,17 @@ async function getMergeBase(projectRoot, baseCommit, headCommit) {
7711
8686
  return commit;
7712
8687
  }
7713
8688
  function normalizeFiles(rawFiles, projectRoot) {
7714
- const root = path16.resolve(projectRoot);
8689
+ const root = path17.resolve(projectRoot);
7715
8690
  const seen = /* @__PURE__ */ new Set();
7716
8691
  const result = [];
7717
8692
  for (const raw of rawFiles) {
7718
8693
  if (raw.length === 0) continue;
7719
- const absolute = path16.resolve(root, raw);
7720
- const relative12 = path16.relative(root, absolute);
7721
- if (path16.isAbsolute(raw) || relative12 === ".." || relative12.startsWith(`..${path16.sep}`) || path16.isAbsolute(relative12)) {
8694
+ const absolute = path17.resolve(root, raw);
8695
+ const relative12 = path17.relative(root, absolute);
8696
+ if (path17.isAbsolute(raw) || relative12 === ".." || relative12.startsWith(`..${path17.sep}`) || path17.isAbsolute(relative12)) {
7722
8697
  throw new Error(`Changed file escapes the project root: ${JSON.stringify(raw)}`);
7723
8698
  }
7724
- const cleaned = relative12.startsWith(`.${path16.sep}`) ? relative12.slice(2) : relative12;
8699
+ const cleaned = relative12.startsWith(`.${path17.sep}`) ? relative12.slice(2) : relative12;
7725
8700
  if (!seen.has(cleaned)) {
7726
8701
  seen.add(cleaned);
7727
8702
  result.push(cleaned);
@@ -7732,7 +8707,7 @@ function normalizeFiles(rawFiles, projectRoot) {
7732
8707
 
7733
8708
  // src/indexer/git-blame.ts
7734
8709
  var import_child_process3 = require("child_process");
7735
- var path17 = __toESM(require("path"), 1);
8710
+ var path18 = __toESM(require("path"), 1);
7736
8711
  var import_util3 = require("util");
7737
8712
  var execFileAsync3 = (0, import_util3.promisify)(import_child_process3.execFile);
7738
8713
  function parseGitBlamePorcelain(output) {
@@ -7770,7 +8745,7 @@ function parseGitBlamePorcelain(output) {
7770
8745
  return Array.from(commits.values()).filter((commit) => commit.lines > 0).sort((a, b) => b.lines - a.lines || b.committedAt - a.committedAt)[0];
7771
8746
  }
7772
8747
  async function getChunkGitBlame(projectRoot, filePath, startLine, endLine) {
7773
- const relativePath = path17.relative(projectRoot, filePath);
8748
+ const relativePath = path18.relative(projectRoot, filePath);
7774
8749
  try {
7775
8750
  const { stdout } = await execFileAsync3(
7776
8751
  "git",
@@ -8349,8 +9324,8 @@ function pathSegmentsForAffinityMatch(filePath) {
8349
9324
  if (segments.length === 0) {
8350
9325
  return [];
8351
9326
  }
8352
- const basename6 = segments[segments.length - 1] ?? "";
8353
- const basenameWithoutExt = basename6.replace(/\.[^/.]+$/u, "");
9327
+ const basename7 = segments[segments.length - 1] ?? "";
9328
+ const basenameWithoutExt = basename7.replace(/\.[^/.]+$/u, "");
8354
9329
  const normalizedSegments = segments.map((segment) => segment.toLowerCase());
8355
9330
  return Array.from(/* @__PURE__ */ new Set([
8356
9331
  ...normalizedSegments,
@@ -8659,8 +9634,8 @@ function hasAllEmbeddingParts(parts, expectedPartCount) {
8659
9634
 
8660
9635
  // src/indexer/failed-state-persistence.ts
8661
9636
  var fs2 = __toESM(require("fs"), 1);
8662
- var import_node_crypto = require("crypto");
8663
- var path18 = __toESM(require("path"), 1);
9637
+ var import_node_crypto2 = require("crypto");
9638
+ var path19 = __toESM(require("path"), 1);
8664
9639
  var import_node_string_decoder = require("string_decoder");
8665
9640
  var CURRENT_FAILED_BATCH_VERSION = 1;
8666
9641
  var DEFAULT_MALFORMED_LINE_ACTION = "skip";
@@ -8678,7 +9653,7 @@ function* readFailedBatchRecords(filePath, options = {}) {
8678
9653
  function createFailedBatchWriter(targetPath) {
8679
9654
  const temporaryPath = createTemporaryPath(targetPath);
8680
9655
  let finalized = false;
8681
- fs2.mkdirSync(path18.dirname(targetPath), { recursive: true });
9656
+ fs2.mkdirSync(path19.dirname(targetPath), { recursive: true });
8682
9657
  fs2.closeSync(fs2.openSync(temporaryPath, "w"));
8683
9658
  const write = (record) => {
8684
9659
  if (finalized) {
@@ -8697,7 +9672,7 @@ function createFailedBatchWriter(targetPath) {
8697
9672
  if (lines.length === 0) {
8698
9673
  return;
8699
9674
  }
8700
- fs2.mkdirSync(path18.dirname(targetPath), { recursive: true });
9675
+ fs2.mkdirSync(path19.dirname(targetPath), { recursive: true });
8701
9676
  fs2.appendFileSync(temporaryPath, `${lines.join("\n")}
8702
9677
  `, "utf-8");
8703
9678
  };
@@ -8705,7 +9680,7 @@ function createFailedBatchWriter(targetPath) {
8705
9680
  if (finalized) {
8706
9681
  return;
8707
9682
  }
8708
- fs2.mkdirSync(path18.dirname(targetPath), { recursive: true });
9683
+ fs2.mkdirSync(path19.dirname(targetPath), { recursive: true });
8709
9684
  fs2.renameSync(temporaryPath, targetPath);
8710
9685
  finalized = true;
8711
9686
  };
@@ -8851,10 +9826,10 @@ function stripLeadingBomAndWhitespace(value) {
8851
9826
  return result;
8852
9827
  }
8853
9828
  function createTemporaryPath(targetPath) {
8854
- const randomId = (0, import_node_crypto.createHash)("sha1").update(`${Date.now()}:${(0, import_node_crypto.randomBytes)(8).toString("hex")}`).digest("hex");
8855
- const targetDir = path18.dirname(targetPath);
8856
- const baseName = path18.basename(targetPath);
8857
- return path18.join(targetDir, `.${baseName}.${randomId}.tmp`);
9829
+ const randomId = (0, import_node_crypto2.createHash)("sha1").update(`${Date.now()}:${(0, import_node_crypto2.randomBytes)(8).toString("hex")}`).digest("hex");
9830
+ const targetDir = path19.dirname(targetPath);
9831
+ const baseName = path19.basename(targetPath);
9832
+ return path19.join(targetDir, `.${baseName}.${randomId}.tmp`);
8858
9833
  }
8859
9834
  function handleMalformedLine(filePath, lineNumber, line, error, options) {
8860
9835
  const action = options.malformedLineAction ?? DEFAULT_MALFORMED_LINE_ACTION;
@@ -9100,9 +10075,9 @@ var SWIFT_PARSER_VERSION = "1";
9100
10075
  var METAL_PARSER_VERSION = "1";
9101
10076
  var SYMBOL_EXTRACTOR_VERSION = "1";
9102
10077
  function isPathWithinRoot2(filePath, rootPath) {
9103
- const normalizedFilePath = path19.resolve(filePath);
9104
- const normalizedRoot = path19.resolve(rootPath);
9105
- return normalizedFilePath === normalizedRoot || normalizedFilePath.startsWith(`${normalizedRoot}${path19.sep}`);
10078
+ const normalizedFilePath = path20.resolve(filePath);
10079
+ const normalizedRoot = path20.resolve(rootPath);
10080
+ return normalizedFilePath === normalizedRoot || normalizedFilePath.startsWith(`${normalizedRoot}${path20.sep}`);
9106
10081
  }
9107
10082
  function promoteIdentifierMatches(query, combined, semanticCandidates, keywordCandidates, database, branchChunkIds, prioritizeSourcePaths = classifyQueryIntentRaw(query) === "source") {
9108
10083
  if (combined.length === 0) {
@@ -9433,10 +10408,10 @@ function matchesHardSearchFilters(candidate, options, projectRoot) {
9433
10408
  }
9434
10409
  if (options?.directory) {
9435
10410
  const candidatePath = canonicalizePathForComparison(
9436
- path19.resolve(projectRoot, candidate.metadata.filePath.replace(/\\/g, path19.sep))
10411
+ path20.resolve(projectRoot, candidate.metadata.filePath.replace(/\\/g, path20.sep))
9437
10412
  );
9438
10413
  const directoryPath = canonicalizePathForComparison(
9439
- path19.resolve(projectRoot, options.directory.trim().replace(/\\/g, path19.sep))
10414
+ path20.resolve(projectRoot, options.directory.trim().replace(/\\/g, path20.sep))
9440
10415
  );
9441
10416
  if (!isPathWithinRoot2(candidatePath, directoryPath)) return false;
9442
10417
  }
@@ -9549,26 +10524,37 @@ var Indexer = class _Indexer {
9549
10524
  return this.indexPathOverride ?? resolveProjectIndexPath(this.projectRoot, this.config.scope, this.host);
9550
10525
  }
9551
10526
  toCanonicalFilePath(filePath) {
9552
- if (!path19.isAbsolute(filePath)) {
10527
+ if (!path20.isAbsolute(filePath)) {
9553
10528
  return this.resolveStoredFilePath(filePath, this.projectRoot);
9554
10529
  }
9555
- if (path19.resolve(this.materializedProjectRoot) === path19.resolve(this.projectRoot) || !isPathWithinRoot2(filePath, this.materializedProjectRoot)) {
10530
+ if (path20.resolve(this.materializedProjectRoot) === path20.resolve(this.projectRoot) || !isPathWithinRoot2(filePath, this.materializedProjectRoot)) {
9556
10531
  return filePath;
9557
10532
  }
9558
- return path19.resolve(this.projectRoot, path19.relative(this.materializedProjectRoot, filePath));
10533
+ return path20.resolve(this.projectRoot, path20.relative(this.materializedProjectRoot, filePath));
9559
10534
  }
9560
10535
  toStoredFilePath(filePath) {
9561
10536
  const canonicalFilePath = this.toCanonicalFilePath(filePath);
9562
10537
  if (this.config.scope !== "project" || !isPathWithinRoot2(canonicalFilePath, this.projectRoot)) {
9563
10538
  return canonicalFilePath;
9564
10539
  }
9565
- return path19.relative(this.projectRoot, canonicalFilePath).split(path19.sep).join("/");
10540
+ return path20.relative(this.projectRoot, canonicalFilePath).split(path20.sep).join("/");
10541
+ }
10542
+ isStoredPathExcluded(storedPath) {
10543
+ let matchPath = storedPath.split(path20.sep).join("/");
10544
+ if (path20.isAbsolute(storedPath)) {
10545
+ const relativePath = path20.relative(this.projectRoot, storedPath).split(path20.sep).join("/");
10546
+ if (relativePath.startsWith("..") || path20.isAbsolute(relativePath)) {
10547
+ return false;
10548
+ }
10549
+ matchPath = relativePath;
10550
+ }
10551
+ return isExcludedByPatterns(matchPath, this.config.exclude);
9566
10552
  }
9567
10553
  resolveStoredFilePath(filePath, rootPath = this.projectRoot) {
9568
- if (path19.isAbsolute(filePath)) {
10554
+ if (path20.isAbsolute(filePath)) {
9569
10555
  return filePath;
9570
10556
  }
9571
- const resolvedPath = path19.resolve(rootPath, ...filePath.split("/"));
10557
+ const resolvedPath = path20.resolve(rootPath, ...filePath.split("/"));
9572
10558
  if (!isPathWithinRoot2(resolvedPath, rootPath)) {
9573
10559
  throw new Error(`Stored project path escapes project root: ${JSON.stringify(filePath)}`);
9574
10560
  }
@@ -9592,7 +10578,7 @@ var Indexer = class _Indexer {
9592
10578
  }
9593
10579
  toMaterializedFilePath(filePath) {
9594
10580
  const storedFilePath = this.toStoredFilePath(filePath);
9595
- if (path19.isAbsolute(storedFilePath)) {
10581
+ if (path20.isAbsolute(storedFilePath)) {
9596
10582
  return storedFilePath;
9597
10583
  }
9598
10584
  return this.resolveStoredFilePath(storedFilePath, this.materializedProjectRoot);
@@ -9609,10 +10595,10 @@ var Indexer = class _Indexer {
9609
10595
  }
9610
10596
  getRuntimeArtifactPath(fileName) {
9611
10597
  const namespace = this.getRuntimeArtifactNamespace();
9612
- if (!namespace) return path19.join(this.indexPath, fileName);
9613
- const extension = path19.extname(fileName);
10598
+ if (!namespace) return path20.join(this.indexPath, fileName);
10599
+ const extension = path20.extname(fileName);
9614
10600
  const baseName = fileName.slice(0, fileName.length - extension.length);
9615
- return path19.join(this.indexPath, `${baseName}.${namespace}${extension}`);
10601
+ return path20.join(this.indexPath, `${baseName}.${namespace}${extension}`);
9616
10602
  }
9617
10603
  refreshRuntimeArtifactPaths() {
9618
10604
  this.fileHashCachePath = this.getRuntimeArtifactPath("file-hashes.json");
@@ -9625,14 +10611,14 @@ var Indexer = class _Indexer {
9625
10611
  getMaterializedKnowledgeBases() {
9626
10612
  const canonicalProjectRoot = this.getCanonicalPath(this.projectRoot);
9627
10613
  return this.config.knowledgeBases.map((knowledgeBase) => {
9628
- const configuredPath = path19.isAbsolute(knowledgeBase) ? knowledgeBase : path19.resolve(this.projectRoot, knowledgeBase);
10614
+ const configuredPath = path20.isAbsolute(knowledgeBase) ? knowledgeBase : path20.resolve(this.projectRoot, knowledgeBase);
9629
10615
  const canonicalPath = this.getCanonicalPath(configuredPath);
9630
10616
  if (!isPathWithinRoot2(canonicalPath, canonicalProjectRoot)) {
9631
10617
  return canonicalPath;
9632
10618
  }
9633
- return path19.resolve(
10619
+ return path20.resolve(
9634
10620
  this.materializedProjectRoot,
9635
- path19.relative(canonicalProjectRoot, canonicalPath)
10621
+ path20.relative(canonicalProjectRoot, canonicalPath)
9636
10622
  );
9637
10623
  });
9638
10624
  }
@@ -9640,7 +10626,7 @@ var Indexer = class _Indexer {
9640
10626
  try {
9641
10627
  return canonicalizePathForComparison(targetPath);
9642
10628
  } catch {
9643
- return path19.resolve(targetPath);
10629
+ return path20.resolve(targetPath);
9644
10630
  }
9645
10631
  }
9646
10632
  getProjectIdentityHash(projectRoot) {
@@ -9766,7 +10752,7 @@ var Indexer = class _Indexer {
9766
10752
  atomicWriteSync(targetPath, data) {
9767
10753
  const lease = this.requireActiveLease();
9768
10754
  const tempPath = createLeaseTemporaryPath(targetPath, lease.owner, "tmp");
9769
- (0, import_fs12.mkdirSync)(path19.dirname(targetPath), { recursive: true });
10755
+ (0, import_fs12.mkdirSync)(path20.dirname(targetPath), { recursive: true });
9770
10756
  try {
9771
10757
  (0, import_fs12.writeFileSync)(tempPath, data);
9772
10758
  (0, import_fs12.renameSync)(tempPath, targetPath);
@@ -9776,14 +10762,14 @@ var Indexer = class _Indexer {
9776
10762
  }
9777
10763
  saveInvertedIndex(invertedIndex) {
9778
10764
  this.atomicWriteSync(
9779
- path19.join(this.indexPath, "inverted-index.json"),
10765
+ path20.join(this.indexPath, "inverted-index.json"),
9780
10766
  invertedIndex.serialize()
9781
10767
  );
9782
10768
  }
9783
10769
  getScopedRoots(projectRoot = this.projectRoot) {
9784
10770
  const roots = /* @__PURE__ */ new Set([this.getCanonicalPath(projectRoot)]);
9785
10771
  for (const kbRoot of this.config.knowledgeBases) {
9786
- roots.add(this.getCanonicalPath(path19.resolve(projectRoot, kbRoot)));
10772
+ roots.add(this.getCanonicalPath(path20.resolve(projectRoot, kbRoot)));
9787
10773
  }
9788
10774
  return Array.from(roots);
9789
10775
  }
@@ -10200,7 +11186,7 @@ var Indexer = class _Indexer {
10200
11186
  return configuredProviderInfo !== null && recovery.embeddingProvider === configuredProviderInfo.provider && recovery.embeddingModel === configuredProviderInfo.modelInfo.model && recovery.embeddingDimensions === configuredProviderInfo.modelInfo.dimensions && recovery.embeddingStrategyVersion === EMBEDDING_STRATEGY_VERSION;
10201
11187
  }
10202
11188
  hasUnknownLegacyForceIndexClear(owner) {
10203
- return owner.operation === "force-index" && owner.clearRecovery === void 0 && owner.recoveryProtocolVersion !== 1 && (0, import_fs12.existsSync)(path19.join(this.indexPath, "force-index-phase"));
11189
+ return owner.operation === "force-index" && owner.clearRecovery === void 0 && owner.recoveryProtocolVersion !== 1 && (0, import_fs12.existsSync)(path20.join(this.indexPath, "force-index-phase"));
10204
11190
  }
10205
11191
  async recoverFromInterruptedIndexingUnlocked(owners) {
10206
11192
  for (const owner of owners) {
@@ -10588,7 +11574,7 @@ var Indexer = class _Indexer {
10588
11574
  await options.queue.onSizeLessThan(Math.max(1, options.providerRateLimits.concurrency));
10589
11575
  const task = options.queue.add(async () => {
10590
11576
  if (options.rateLimitState.backoffMs > 0) {
10591
- await new Promise((resolve19) => setTimeout(resolve19, options.rateLimitState.backoffMs));
11577
+ await new Promise((resolve20) => setTimeout(resolve20, options.rateLimitState.backoffMs));
10592
11578
  }
10593
11579
  try {
10594
11580
  const embeddingResult = await pRetry(
@@ -11007,12 +11993,12 @@ var Indexer = class _Indexer {
11007
11993
  }
11008
11994
  }
11009
11995
  captureReaderArtifactFingerprint() {
11010
- const storePath = path19.join(this.indexPath, "vectors");
11996
+ const storePath = path20.join(this.indexPath, "vectors");
11011
11997
  return {
11012
11998
  vectors: `${this.getReaderFileFingerprint(storePath)}|${this.getReaderFileFingerprint(`${storePath}.meta.json`)}`,
11013
- keyword: this.getReaderFileFingerprint(path19.join(this.indexPath, "inverted-index.json")),
11014
- database: this.getReaderFileFingerprint(path19.join(this.indexPath, "codebase.db")),
11015
- databaseIdentity: this.getReaderFileFingerprint(path19.join(this.indexPath, "codebase.db"), true)
11999
+ keyword: this.getReaderFileFingerprint(path20.join(this.indexPath, "inverted-index.json")),
12000
+ database: this.getReaderFileFingerprint(path20.join(this.indexPath, "codebase.db")),
12001
+ databaseIdentity: this.getReaderFileFingerprint(path20.join(this.indexPath, "codebase.db"), true)
11016
12002
  };
11017
12003
  }
11018
12004
  refreshReaderArtifacts() {
@@ -11037,10 +12023,10 @@ var Indexer = class _Indexer {
11037
12023
  issues.set(component, this.createReadIssue(component, message));
11038
12024
  this.readerArtifactRetryAfter.set(component, Date.now() + READER_ARTIFACT_RETRY_INTERVAL_MS);
11039
12025
  };
11040
- const storePath = path19.join(this.indexPath, "vectors");
12026
+ const storePath = path20.join(this.indexPath, "vectors");
11041
12027
  const vectorMetadataPath = `${storePath}.meta.json`;
11042
- const invertedIndexPath = path19.join(this.indexPath, "inverted-index.json");
11043
- const dbPath = path19.join(this.indexPath, "codebase.db");
12028
+ const invertedIndexPath = path20.join(this.indexPath, "inverted-index.json");
12029
+ const dbPath = path20.join(this.indexPath, "codebase.db");
11044
12030
  if (vectorsChanged || retryDue("vectors")) {
11045
12031
  const vectorStoreExists = (0, import_fs12.existsSync)(storePath);
11046
12032
  const vectorMetadataExists = (0, import_fs12.existsSync)(vectorMetadataPath);
@@ -11156,10 +12142,10 @@ var Indexer = class _Indexer {
11156
12142
  });
11157
12143
  this.provider = createEmbeddingProvider(this.configuredProviderInfo);
11158
12144
  const dimensions = this.configuredProviderInfo.modelInfo.dimensions;
11159
- const storePath = path19.join(this.indexPath, "vectors");
12145
+ const storePath = path20.join(this.indexPath, "vectors");
11160
12146
  const vectorMetadataPath = `${storePath}.meta.json`;
11161
- const invertedIndexPath = path19.join(this.indexPath, "inverted-index.json");
11162
- const dbPath = path19.join(this.indexPath, "codebase.db");
12147
+ const invertedIndexPath = path20.join(this.indexPath, "inverted-index.json");
12148
+ const dbPath = path20.join(this.indexPath, "codebase.db");
11163
12149
  let dbIsNew = !(0, import_fs12.existsSync)(dbPath);
11164
12150
  const readerArtifactFingerprint = mode === "reader" ? this.captureReaderArtifactFingerprint() : null;
11165
12151
  if (mode === "writer") {
@@ -11337,7 +12323,7 @@ var Indexer = class _Indexer {
11337
12323
  if (await this.tryResetCorruptedIndex("running automatic orphan garbage collection", error)) {
11338
12324
  return {
11339
12325
  resetCorruptedIndex: true,
11340
- warning: this.getCorruptedIndexWarning(path19.join(this.indexPath, "codebase.db"))
12326
+ warning: this.getCorruptedIndexWarning(path20.join(this.indexPath, "codebase.db"))
11341
12327
  };
11342
12328
  }
11343
12329
  throw error;
@@ -11352,7 +12338,7 @@ var Indexer = class _Indexer {
11352
12338
  return;
11353
12339
  }
11354
12340
  const retainedEntries = store.getAllMetadata().filter(({ key }) => !excludedSet.has(key));
11355
- const storeBasePath = path19.join(this.indexPath, "vectors");
12341
+ const storeBasePath = path20.join(this.indexPath, "vectors");
11356
12342
  const storeIndexPath = storeBasePath;
11357
12343
  const storeMetadataPath = `${storeBasePath}.meta.json`;
11358
12344
  const lease = this.requireActiveLease();
@@ -11439,7 +12425,7 @@ var Indexer = class _Indexer {
11439
12425
  const names = await import_fs12.promises.readdir(this.indexPath);
11440
12426
  const runtimeStatePattern = /^(?:file-hashes|failed-batches)(?:\.[a-f0-9]{16})?\.json$/;
11441
12427
  await Promise.all(
11442
- names.filter((name) => runtimeStatePattern.test(name)).map((name) => import_fs12.promises.rm(path19.join(this.indexPath, name), { force: true }))
12428
+ names.filter((name) => runtimeStatePattern.test(name)).map((name) => import_fs12.promises.rm(path20.join(this.indexPath, name), { force: true }))
11443
12429
  );
11444
12430
  }
11445
12431
  async resetLocalIndexArtifacts() {
@@ -11455,13 +12441,13 @@ var Indexer = class _Indexer {
11455
12441
  this.readerArtifactRetryAfter.clear();
11456
12442
  this.fileHashCache.clear();
11457
12443
  const resetPaths = [
11458
- path19.join(this.indexPath, "codebase.db"),
11459
- path19.join(this.indexPath, "codebase.db-shm"),
11460
- path19.join(this.indexPath, "codebase.db-wal"),
11461
- path19.join(this.indexPath, "vectors"),
11462
- path19.join(this.indexPath, "vectors.usearch"),
11463
- path19.join(this.indexPath, "vectors.meta.json"),
11464
- path19.join(this.indexPath, "inverted-index.json")
12444
+ path20.join(this.indexPath, "codebase.db"),
12445
+ path20.join(this.indexPath, "codebase.db-shm"),
12446
+ path20.join(this.indexPath, "codebase.db-wal"),
12447
+ path20.join(this.indexPath, "vectors"),
12448
+ path20.join(this.indexPath, "vectors.usearch"),
12449
+ path20.join(this.indexPath, "vectors.meta.json"),
12450
+ path20.join(this.indexPath, "inverted-index.json")
11465
12451
  ];
11466
12452
  await Promise.all(resetPaths.map((targetPath) => import_fs12.promises.rm(targetPath, { recursive: true, force: true })));
11467
12453
  await this.removeProjectRuntimeStateArtifacts();
@@ -11471,7 +12457,7 @@ var Indexer = class _Indexer {
11471
12457
  if (!isSqliteCorruptionError(error)) {
11472
12458
  return false;
11473
12459
  }
11474
- const dbPath = path19.join(this.indexPath, "codebase.db");
12460
+ const dbPath = path20.join(this.indexPath, "codebase.db");
11475
12461
  const warning = this.getCorruptedIndexWarning(dbPath);
11476
12462
  const errorMessage = getErrorMessage4(error);
11477
12463
  if (this.config.scope === "global") {
@@ -11858,10 +12844,10 @@ var Indexer = class _Indexer {
11858
12844
  const reparseCachedMetalFiles = database.getMetadata(metalParserMetadataKey) !== METAL_PARSER_VERSION;
11859
12845
  const symbolExtractorMetadataKey = this.getSymbolExtractorVersionMetadataKey();
11860
12846
  const refreshCachedSymbols = database.getMetadata(symbolExtractorMetadataKey) !== SYMBOL_EXTRACTOR_VERSION;
11861
- if (reparseCachedSwiftFiles && Array.from(this.fileHashCache.keys()).some((filePath) => path19.extname(filePath).toLowerCase() === ".swift")) {
12847
+ if (reparseCachedSwiftFiles && Array.from(this.fileHashCache.keys()).some((filePath) => path20.extname(filePath).toLowerCase() === ".swift")) {
11862
12848
  this.logger.info("Reindexing cached Swift files for parser support");
11863
12849
  }
11864
- if (reparseCachedMetalFiles && Array.from(this.fileHashCache.keys()).some((filePath) => path19.extname(filePath).toLowerCase() === ".metal")) {
12850
+ if (reparseCachedMetalFiles && Array.from(this.fileHashCache.keys()).some((filePath) => path20.extname(filePath).toLowerCase() === ".metal")) {
11865
12851
  this.logger.info("Reindexing cached Metal files for parser support");
11866
12852
  }
11867
12853
  const includePatterns = [...this.config.include, ...this.config.additionalInclude];
@@ -11905,8 +12891,8 @@ var Indexer = class _Indexer {
11905
12891
  const needsCallGraphRefresh = cachedHashMatches && needsCallGraphResolutionMigration && database.getChunksByFile(storedPath).some(
11906
12892
  (chunk) => chunk.language === "php" || chunk.language === "c" || chunk.language === "cpp"
11907
12893
  );
11908
- const requiresSwiftParserUpgrade = reparseCachedSwiftFiles && path19.extname(storedPath).toLowerCase() === ".swift";
11909
- const requiresMetalParserUpgrade = reparseCachedMetalFiles && path19.extname(storedPath).toLowerCase() === ".metal";
12894
+ const requiresSwiftParserUpgrade = reparseCachedSwiftFiles && path20.extname(storedPath).toLowerCase() === ".swift";
12895
+ const requiresMetalParserUpgrade = reparseCachedMetalFiles && path20.extname(storedPath).toLowerCase() === ".metal";
11910
12896
  const inMigrationScope = forceScopedReembed && scopedRoots !== null && this.isFileInCurrentScope(storedPath, scopedRoots);
11911
12897
  if (cachedHashMatches && !inMigrationScope && !needsCallGraphRefresh && !requiresSwiftParserUpgrade && !requiresMetalParserUpgrade && !refreshCachedSymbols) {
11912
12898
  unchangedFilePaths.add(storedPath);
@@ -11965,7 +12951,7 @@ var Indexer = class _Indexer {
11965
12951
  }
11966
12952
  }
11967
12953
  }
11968
- const shouldRetryFailedPath = (filePath) => filePath !== null && currentFileHashes.has(filePath) && unchangedFilePaths.has(filePath);
12954
+ const shouldRetryFailedPath = (filePath) => filePath !== null && !this.isStoredPathExcluded(filePath) && currentFileHashes.has(filePath) && unchangedFilePaths.has(filePath);
11969
12955
  const failedProcessing = this.prepareFailedBatchProcessing(scopedRoots, shouldRetryFailedPath);
11970
12956
  const maxChunkTokens = getSafeEmbeddingChunkTokenLimit(configuredProviderInfo);
11971
12957
  const providerRateLimits = this.getProviderRateLimits(configuredProviderInfo.provider);
@@ -12069,7 +13055,7 @@ var Indexer = class _Indexer {
12069
13055
  throw new Error(`Parsed file was not present in its source batch: ${parsed.path}`);
12070
13056
  }
12071
13057
  if (parsed.chunks.length === 0) {
12072
- stats.parseFailures.push(path19.isAbsolute(parsed.path) ? path19.relative(this.projectRoot, parsed.path) : parsed.path);
13058
+ stats.parseFailures.push(path20.isAbsolute(parsed.path) ? path20.relative(this.projectRoot, parsed.path) : parsed.path);
12073
13059
  }
12074
13060
  let chunksToProcess = parsed.chunks;
12075
13061
  if (this.config.indexing.fallbackToTextOnMaxChunks && chunksToProcess.length > this.config.indexing.maxChunksPerFile) {
@@ -12404,7 +13390,7 @@ var Indexer = class _Indexer {
12404
13390
  previousBranchSymbolIds,
12405
13391
  Array.from(allSymbolIds)
12406
13392
  );
12407
- const vectorPath = path19.join(this.indexPath, "vectors");
13393
+ const vectorPath = path20.join(this.indexPath, "vectors");
12408
13394
  const shouldFingerprintLegacyPair = !store.hasFingerprint() && (0, import_fs12.existsSync)(vectorPath) && (0, import_fs12.existsSync)(`${vectorPath}.meta.json`);
12409
13395
  if (backfilledBlameMetadata || shouldFingerprintLegacyPair || removedStoredChunks) {
12410
13396
  store.save();
@@ -13263,7 +14249,7 @@ var Indexer = class _Indexer {
13263
14249
  gcOrphanSymbols: 0,
13264
14250
  gcOrphanCallEdges: 0,
13265
14251
  resetCorruptedIndex: true,
13266
- warning: this.getCorruptedIndexWarning(path19.join(this.indexPath, "codebase.db"))
14252
+ warning: this.getCorruptedIndexWarning(path20.join(this.indexPath, "codebase.db"))
13267
14253
  };
13268
14254
  }
13269
14255
  this.logger.recordGc(removedCount, gcOrphanChunks, gcOrphanEmbeddings);
@@ -13293,7 +14279,8 @@ var Indexer = class _Indexer {
13293
14279
  const maxChunkTokens = getSafeEmbeddingChunkTokenLimit(configuredProviderInfo);
13294
14280
  const providerRateLimits = this.getProviderRateLimits(configuredProviderInfo.provider);
13295
14281
  const roots = this.config.scope === "global" ? this.getScopedRoots() : null;
13296
- const failedProcessing = this.prepareFailedBatchProcessing(roots, () => true);
14282
+ const shouldProcessFailedPath = (filePath) => filePath === null || !this.isStoredPathExcluded(filePath);
14283
+ const failedProcessing = this.prepareFailedBatchProcessing(roots, shouldProcessFailedPath);
13297
14284
  if (failedProcessing.latestById.size === 0) {
13298
14285
  this.finalizeFailedBatchWriteState(failedProcessing.state);
13299
14286
  return { succeeded: 0, failed: 0, remaining: 0 };
@@ -13306,7 +14293,7 @@ var Indexer = class _Indexer {
13306
14293
  const retryableChunks = this.iterateLatestFailedChunks(
13307
14294
  failedProcessing.latestById,
13308
14295
  roots,
13309
- () => true,
14296
+ shouldProcessFailedPath,
13310
14297
  maxChunkTokens
13311
14298
  );
13312
14299
  for (const retryBatch of iterateOrderedFileBatches(
@@ -13576,9 +14563,9 @@ var Indexer = class _Indexer {
13576
14563
  this.requireReadableComponents(readIssues, "database");
13577
14564
  let shortest = [];
13578
14565
  for (const branchKey of this.getBranchCatalogKeys()) {
13579
- const path34 = database.findShortestPath(fromName, toName, branchKey, maxDepth);
13580
- if (path34.length > 0 && (shortest.length === 0 || path34.length < shortest.length)) {
13581
- shortest = path34;
14566
+ const path35 = database.findShortestPath(fromName, toName, branchKey, maxDepth);
14567
+ if (path35.length > 0 && (shortest.length === 0 || path35.length < shortest.length)) {
14568
+ shortest = path35;
13582
14569
  }
13583
14570
  }
13584
14571
  return shortest.map((hop) => this.resolveFilePathRecord(hop));
@@ -13626,13 +14613,13 @@ var Indexer = class _Indexer {
13626
14613
  }
13627
14614
  }
13628
14615
  if (!found) continue;
13629
- const path34 = [];
14616
+ const path35 = [];
13630
14617
  let currentSymbolId = toSymbolId;
13631
14618
  while (true) {
13632
14619
  const symbol = symbolsById.get(currentSymbolId);
13633
14620
  if (!symbol) break;
13634
14621
  const parent = parentBySymbolId.get(currentSymbolId);
13635
- path34.push({
14622
+ path35.push({
13636
14623
  symbolId: symbol.id,
13637
14624
  symbolName: symbol.name,
13638
14625
  filePath: symbol.filePath,
@@ -13642,9 +14629,9 @@ var Indexer = class _Indexer {
13642
14629
  if (!parent) break;
13643
14630
  currentSymbolId = parent.parentId;
13644
14631
  }
13645
- path34.reverse();
13646
- if (path34.length > 0 && (shortest.length === 0 || path34.length < shortest.length)) {
13647
- shortest = path34;
14632
+ path35.reverse();
14633
+ if (path35.length > 0 && (shortest.length === 0 || path35.length < shortest.length)) {
14634
+ shortest = path35;
13648
14635
  }
13649
14636
  }
13650
14637
  return shortest.map((hop) => this.resolveFilePathRecord(hop));
@@ -13795,7 +14782,7 @@ var Indexer = class _Indexer {
13795
14782
  );
13796
14783
  }
13797
14784
  }
13798
- const toStoredChangedFiles = (filePaths) => filePaths.map((filePath) => this.toStoredFilePath(path19.resolve(this.projectRoot, filePath)));
14785
+ const toStoredChangedFiles = (filePaths) => filePaths.map((filePath) => this.toStoredFilePath(path20.resolve(this.projectRoot, filePath)));
13799
14786
  const storedChangedFiles = toStoredChangedFiles(changedFiles);
13800
14787
  const directSymbols = database.getSymbolsForFiles(storedChangedFiles, branchKey);
13801
14788
  const directIds = directSymbols.map((s) => s.id);
@@ -13944,12 +14931,12 @@ var Indexer = class _Indexer {
13944
14931
  if (meta.filePath) filePaths.add(meta.filePath);
13945
14932
  }
13946
14933
  const directory = options?.directory?.replace(/\/$/, "");
13947
- const absoluteDirectoryFilter = directory ? path19.resolve(this.projectRoot, directory) : void 0;
14934
+ const absoluteDirectoryFilter = directory ? path20.resolve(this.projectRoot, directory) : void 0;
13948
14935
  for (const filePath of filePaths) {
13949
14936
  if (directory) {
13950
14937
  const absoluteFilePath = this.resolveStoredFilePath(filePath);
13951
14938
  const matchesRelative = filePath === directory || filePath.startsWith(directory + "/");
13952
- const matchesProjectRelative = absoluteDirectoryFilter !== void 0 && (absoluteFilePath === absoluteDirectoryFilter || absoluteFilePath.startsWith(absoluteDirectoryFilter + path19.sep));
14939
+ const matchesProjectRelative = absoluteDirectoryFilter !== void 0 && (absoluteFilePath === absoluteDirectoryFilter || absoluteFilePath.startsWith(absoluteDirectoryFilter + path20.sep));
13953
14940
  if (!matchesRelative && !matchesProjectRelative) {
13954
14941
  continue;
13955
14942
  }
@@ -14038,15 +15025,24 @@ function getOrCreateIndexer(projectRoot, host) {
14038
15025
  }
14039
15026
  const indexer = new Indexer(projectRoot, config, host);
14040
15027
  indexerCache.set(key, indexer);
14041
- configureAutoIndex(projectRoot, host, config, () => getOrCreateIndexer(projectRoot, host));
15028
+ configureAutoIndex(projectRoot, host, config, () => getOrCreateIndexer(projectRoot, host), {
15029
+ preserveManagedWorker: true,
15030
+ synchronizeBackgroundWorker: false
15031
+ });
14042
15032
  return indexer;
14043
15033
  }
14044
- function initializeTools(projectRoot, config, host) {
15034
+ function initializeTools(projectRoot, config, host, options = {}) {
14045
15035
  defaultProjectRoots.set(host, projectRoot);
14046
15036
  const key = getIndexerCacheKey(projectRoot, host);
15037
+ if (options.preserveManagedWorker === true && isBackgroundWorkerManaged(projectRoot, host) && indexerCache.has(key)) {
15038
+ return;
15039
+ }
14047
15040
  configCache.set(key, config);
14048
15041
  indexerCache.set(key, new Indexer(projectRoot, config, host));
14049
- configureAutoIndex(projectRoot, host, config, () => getOrCreateIndexer(projectRoot, host));
15042
+ configureAutoIndex(projectRoot, host, config, () => getOrCreateIndexer(projectRoot, host), {
15043
+ preserveManagedWorker: options.preserveManagedWorker,
15044
+ synchronizeBackgroundWorker: false
15045
+ });
14050
15046
  }
14051
15047
  function getIndexerForProject(projectRoot, host) {
14052
15048
  const root = getProjectRoot(projectRoot, host);
@@ -14076,7 +15072,7 @@ function trimOrUndefined(value) {
14076
15072
  return normalized || void 0;
14077
15073
  }
14078
15074
  function normalizeCallGraphPath(value) {
14079
- let normalized = path20.posix.normalize(value.trim().replaceAll("\\", "/"));
15075
+ let normalized = path21.posix.normalize(value.trim().replaceAll("\\", "/"));
14080
15076
  if (normalized.startsWith("./")) {
14081
15077
  normalized = normalized.slice(2);
14082
15078
  }
@@ -14292,32 +15288,32 @@ async function executeCallGraph(projectRoot, host, args) {
14292
15288
  // src/adapters/mcp/cli.ts
14293
15289
  var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
14294
15290
  var import_fs20 = require("fs");
14295
- var os8 = __toESM(require("os"), 1);
14296
- var path32 = __toESM(require("path"), 1);
15291
+ var os9 = __toESM(require("os"), 1);
15292
+ var path33 = __toESM(require("path"), 1);
14297
15293
  var import_url = require("url");
14298
15294
 
14299
15295
  // src/eval/reports.ts
14300
15296
  var import_fs14 = require("fs");
14301
- var path21 = __toESM(require("path"), 1);
15297
+ var path22 = __toESM(require("path"), 1);
14302
15298
 
14303
15299
  // src/eval/runner.ts
14304
15300
  var crypto2 = __toESM(require("crypto"), 1);
14305
15301
  var import_fs17 = require("fs");
14306
- var path23 = __toESM(require("path"), 1);
15302
+ var path24 = __toESM(require("path"), 1);
14307
15303
 
14308
15304
  // src/eval/runner-config.ts
14309
15305
  var import_fs15 = require("fs");
14310
- var os7 = __toESM(require("os"), 1);
14311
- var path22 = __toESM(require("path"), 1);
15306
+ var os8 = __toESM(require("os"), 1);
15307
+ var path23 = __toESM(require("path"), 1);
14312
15308
 
14313
15309
  // src/eval/schema.ts
14314
15310
  var import_fs16 = require("fs");
14315
15311
 
14316
15312
  // src/eval/cli.ts
14317
- var path25 = __toESM(require("path"), 1);
15313
+ var path26 = __toESM(require("path"), 1);
14318
15314
 
14319
15315
  // src/eval/cli-parser.ts
14320
- var path24 = __toESM(require("path"), 1);
15316
+ var path25 = __toESM(require("path"), 1);
14321
15317
 
14322
15318
  // src/adapters/mcp/server.ts
14323
15319
  var import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
@@ -14423,25 +15419,25 @@ var MCP_TOOL_NAMES = [
14423
15419
 
14424
15420
  // src/watcher/file-watcher.ts
14425
15421
  var import_fs19 = require("fs");
14426
- var path28 = __toESM(require("path"), 1);
15422
+ var path29 = __toESM(require("path"), 1);
14427
15423
 
14428
15424
  // src/watcher/native-recursive-watcher.ts
14429
- var import_node_fs = require("fs");
14430
- var path26 = __toESM(require("path"), 1);
15425
+ var import_node_fs2 = require("fs");
15426
+ var path27 = __toESM(require("path"), 1);
14431
15427
 
14432
15428
  // src/watcher/snapshot.ts
14433
15429
  var fsPromises4 = __toESM(require("fs/promises"), 1);
14434
- var path27 = __toESM(require("path"), 1);
15430
+ var path28 = __toESM(require("path"), 1);
14435
15431
 
14436
15432
  // src/watcher/git-head-watcher.ts
14437
- var path29 = __toESM(require("path"), 1);
15433
+ var path30 = __toESM(require("path"), 1);
14438
15434
 
14439
15435
  // src/tools/visualize/activity.ts
14440
15436
  var import_child_process5 = require("child_process");
14441
- var path30 = __toESM(require("path"), 1);
15437
+ var path31 = __toESM(require("path"), 1);
14442
15438
 
14443
15439
  // src/tools/visualize/transform.ts
14444
- var path31 = __toESM(require("path"), 1);
15440
+ var path32 = __toESM(require("path"), 1);
14445
15441
 
14446
15442
  // src/adapters/mcp/cli.ts
14447
15443
  function parseIndexArgs(argv, cwd) {
@@ -14463,7 +15459,7 @@ function parseIndexArgs(argv, cwd) {
14463
15459
  if (!arg.startsWith("--project=")) {
14464
15460
  i += 1;
14465
15461
  }
14466
- project = path32.resolve(cwd, value);
15462
+ project = path33.resolve(cwd, value);
14467
15463
  continue;
14468
15464
  }
14469
15465
  if (arg === "--config" || arg.startsWith("--config=")) {
@@ -14474,7 +15470,7 @@ function parseIndexArgs(argv, cwd) {
14474
15470
  if (!arg.startsWith("--config=")) {
14475
15471
  i += 1;
14476
15472
  }
14477
- config = path32.resolve(cwd, value);
15473
+ config = path33.resolve(cwd, value);
14478
15474
  continue;
14479
15475
  }
14480
15476
  if (arg === "--host" || arg.startsWith("--host=")) {
@@ -14638,7 +15634,7 @@ function parseCbiCommandArgs(command, args, cwd) {
14638
15634
  if (arg === "--help" || arg === "-h") throw new Error("help-requested");
14639
15635
  if (arg === "--project" || arg.startsWith("--project=")) {
14640
15636
  const parsed = optionValue(args, index, "project");
14641
- project = path33.resolve(cwd, parsed.value);
15637
+ project = path34.resolve(cwd, parsed.value);
14642
15638
  index += parsed.consumed;
14643
15639
  continue;
14644
15640
  }
@@ -14650,7 +15646,7 @@ function parseCbiCommandArgs(command, args, cwd) {
14650
15646
  }
14651
15647
  if (arg === "--config" || arg.startsWith("--config=")) {
14652
15648
  const parsed = optionValue(args, index, "config");
14653
- config = path33.resolve(cwd, parsed.value);
15649
+ config = path34.resolve(cwd, parsed.value);
14654
15650
  index += parsed.consumed;
14655
15651
  continue;
14656
15652
  }
@@ -14663,7 +15659,7 @@ function parseCbiCommandArgs(command, args, cwd) {
14663
15659
  }
14664
15660
  if (command === "graph" && (arg === "--file" || arg.startsWith("--file="))) {
14665
15661
  const parsed = optionValue(args, index, "file");
14666
- filePath = path33.resolve(cwd, parsed.value);
15662
+ filePath = path34.resolve(cwd, parsed.value);
14667
15663
  index += parsed.consumed;
14668
15664
  continue;
14669
15665
  }
@@ -14762,7 +15758,7 @@ async function runCbiCli(argv, cwd, deps = {}) {
14762
15758
  }
14763
15759
  }
14764
15760
  function isCbiEntrypoint(moduleUrl, argvPath) {
14765
- return argvPath !== void 0 && (0, import_node_fs2.realpathSync)((0, import_node_url2.fileURLToPath)(moduleUrl)) === (0, import_node_fs2.realpathSync)(argvPath);
15761
+ return argvPath !== void 0 && (0, import_node_fs3.realpathSync)((0, import_node_url2.fileURLToPath)(moduleUrl)) === (0, import_node_fs3.realpathSync)(argvPath);
14766
15762
  }
14767
15763
 
14768
15764
  // src/cbi.ts