opencode-codebase-index 0.11.0 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -328,7 +328,7 @@ var require_ignore = __commonJS({
328
328
  // path matching.
329
329
  // - check `string` either `MODE_IGNORE` or `MODE_CHECK_IGNORE`
330
330
  // @returns {TestResult} true if a file is ignored
331
- test(path18, checkUnignored, mode) {
331
+ test(path19, checkUnignored, mode) {
332
332
  let ignored = false;
333
333
  let unignored = false;
334
334
  let matchedRule;
@@ -337,7 +337,7 @@ var require_ignore = __commonJS({
337
337
  if (unignored === negative && ignored !== unignored || negative && !ignored && !unignored && !checkUnignored) {
338
338
  return;
339
339
  }
340
- const matched = rule[mode].test(path18);
340
+ const matched = rule[mode].test(path19);
341
341
  if (!matched) {
342
342
  return;
343
343
  }
@@ -358,17 +358,17 @@ var require_ignore = __commonJS({
358
358
  var throwError = (message, Ctor) => {
359
359
  throw new Ctor(message);
360
360
  };
361
- var checkPath = (path18, originalPath, doThrow) => {
362
- if (!isString(path18)) {
361
+ var checkPath = (path19, originalPath, doThrow) => {
362
+ if (!isString(path19)) {
363
363
  return doThrow(
364
364
  `path must be a string, but got \`${originalPath}\``,
365
365
  TypeError
366
366
  );
367
367
  }
368
- if (!path18) {
368
+ if (!path19) {
369
369
  return doThrow(`path must not be empty`, TypeError);
370
370
  }
371
- if (checkPath.isNotRelative(path18)) {
371
+ if (checkPath.isNotRelative(path19)) {
372
372
  const r = "`path.relative()`d";
373
373
  return doThrow(
374
374
  `path should be a ${r} string, but got "${originalPath}"`,
@@ -377,7 +377,7 @@ var require_ignore = __commonJS({
377
377
  }
378
378
  return true;
379
379
  };
380
- var isNotRelative = (path18) => REGEX_TEST_INVALID_PATH.test(path18);
380
+ var isNotRelative = (path19) => REGEX_TEST_INVALID_PATH.test(path19);
381
381
  checkPath.isNotRelative = isNotRelative;
382
382
  checkPath.convert = (p) => p;
383
383
  var Ignore2 = class {
@@ -407,19 +407,19 @@ var require_ignore = __commonJS({
407
407
  }
408
408
  // @returns {TestResult}
409
409
  _test(originalPath, cache, checkUnignored, slices) {
410
- const path18 = originalPath && checkPath.convert(originalPath);
410
+ const path19 = originalPath && checkPath.convert(originalPath);
411
411
  checkPath(
412
- path18,
412
+ path19,
413
413
  originalPath,
414
414
  this._strictPathCheck ? throwError : RETURN_FALSE
415
415
  );
416
- return this._t(path18, cache, checkUnignored, slices);
416
+ return this._t(path19, cache, checkUnignored, slices);
417
417
  }
418
- checkIgnore(path18) {
419
- if (!REGEX_TEST_TRAILING_SLASH.test(path18)) {
420
- return this.test(path18);
418
+ checkIgnore(path19) {
419
+ if (!REGEX_TEST_TRAILING_SLASH.test(path19)) {
420
+ return this.test(path19);
421
421
  }
422
- const slices = path18.split(SLASH2).filter(Boolean);
422
+ const slices = path19.split(SLASH2).filter(Boolean);
423
423
  slices.pop();
424
424
  if (slices.length) {
425
425
  const parent = this._t(
@@ -432,18 +432,18 @@ var require_ignore = __commonJS({
432
432
  return parent;
433
433
  }
434
434
  }
435
- return this._rules.test(path18, false, MODE_CHECK_IGNORE);
435
+ return this._rules.test(path19, false, MODE_CHECK_IGNORE);
436
436
  }
437
- _t(path18, cache, checkUnignored, slices) {
438
- if (path18 in cache) {
439
- return cache[path18];
437
+ _t(path19, cache, checkUnignored, slices) {
438
+ if (path19 in cache) {
439
+ return cache[path19];
440
440
  }
441
441
  if (!slices) {
442
- slices = path18.split(SLASH2).filter(Boolean);
442
+ slices = path19.split(SLASH2).filter(Boolean);
443
443
  }
444
444
  slices.pop();
445
445
  if (!slices.length) {
446
- return cache[path18] = this._rules.test(path18, checkUnignored, MODE_IGNORE);
446
+ return cache[path19] = this._rules.test(path19, checkUnignored, MODE_IGNORE);
447
447
  }
448
448
  const parent = this._t(
449
449
  slices.join(SLASH2) + SLASH2,
@@ -451,29 +451,29 @@ var require_ignore = __commonJS({
451
451
  checkUnignored,
452
452
  slices
453
453
  );
454
- return cache[path18] = parent.ignored ? parent : this._rules.test(path18, checkUnignored, MODE_IGNORE);
454
+ return cache[path19] = parent.ignored ? parent : this._rules.test(path19, checkUnignored, MODE_IGNORE);
455
455
  }
456
- ignores(path18) {
457
- return this._test(path18, this._ignoreCache, false).ignored;
456
+ ignores(path19) {
457
+ return this._test(path19, this._ignoreCache, false).ignored;
458
458
  }
459
459
  createFilter() {
460
- return (path18) => !this.ignores(path18);
460
+ return (path19) => !this.ignores(path19);
461
461
  }
462
462
  filter(paths) {
463
463
  return makeArray(paths).filter(this.createFilter());
464
464
  }
465
465
  // @returns {TestResult}
466
- test(path18) {
467
- return this._test(path18, this._testCache, true);
466
+ test(path19) {
467
+ return this._test(path19, this._testCache, true);
468
468
  }
469
469
  };
470
470
  var factory = (options) => new Ignore2(options);
471
- var isPathValid = (path18) => checkPath(path18 && checkPath.convert(path18), path18, RETURN_FALSE);
471
+ var isPathValid = (path19) => checkPath(path19 && checkPath.convert(path19), path19, RETURN_FALSE);
472
472
  var setupWindows = () => {
473
473
  const makePosix = (str) => /^\\\\\?\\/.test(str) || /["<>|\u0000-\u001F]+/u.test(str) ? str : str.replace(/\\/g, "/");
474
474
  checkPath.convert = makePosix;
475
475
  const REGEX_TEST_WINDOWS_PATH_ABSOLUTE = /^[a-z]:\//i;
476
- checkPath.isNotRelative = (path18) => REGEX_TEST_WINDOWS_PATH_ABSOLUTE.test(path18) || isNotRelative(path18);
476
+ checkPath.isNotRelative = (path19) => REGEX_TEST_WINDOWS_PATH_ABSOLUTE.test(path19) || isNotRelative(path19);
477
477
  };
478
478
  if (
479
479
  // Detect `process` so that it can run in browsers.
@@ -652,7 +652,7 @@ var require_eventemitter3 = __commonJS({
652
652
 
653
653
  // src/index.ts
654
654
  import * as os6 from "os";
655
- import * as path17 from "path";
655
+ import * as path18 from "path";
656
656
  import { fileURLToPath as fileURLToPath2 } from "url";
657
657
 
658
658
  // src/config/constants.ts
@@ -799,6 +799,7 @@ function getDefaultSearchConfig() {
799
799
  rerankTopN: 20,
800
800
  contextLines: 0,
801
801
  routingHints: true,
802
+ routingGraphHandoffHints: false,
802
803
  routingHintRole: "system"
803
804
  };
804
805
  }
@@ -921,6 +922,7 @@ function parseConfig(raw) {
921
922
  rerankTopN: typeof rawSearch.rerankTopN === "number" ? Math.min(200, Math.max(0, Math.floor(rawSearch.rerankTopN))) : defaultSearch.rerankTopN,
922
923
  contextLines: typeof rawSearch.contextLines === "number" ? Math.min(50, Math.max(0, rawSearch.contextLines)) : defaultSearch.contextLines,
923
924
  routingHints: typeof rawSearch.routingHints === "boolean" ? rawSearch.routingHints : defaultSearch.routingHints,
925
+ routingGraphHandoffHints: typeof rawSearch.routingGraphHandoffHints === "boolean" ? rawSearch.routingGraphHandoffHints : defaultSearch.routingGraphHandoffHints,
924
926
  routingHintRole: rawSearch.routingHintRole === "developer" || rawSearch.routingHintRole === "system" ? rawSearch.routingHintRole : defaultSearch.routingHintRole
925
927
  };
926
928
  const rawDebug = input.debug && typeof input.debug === "object" ? input.debug : {};
@@ -1547,7 +1549,7 @@ var ReaddirpStream = class extends Readable {
1547
1549
  this._directoryFilter = normalizeFilter(opts.directoryFilter);
1548
1550
  const statMethod = opts.lstat ? lstat : stat;
1549
1551
  if (wantBigintFsStats) {
1550
- this._stat = (path18) => statMethod(path18, { bigint: true });
1552
+ this._stat = (path19) => statMethod(path19, { bigint: true });
1551
1553
  } else {
1552
1554
  this._stat = statMethod;
1553
1555
  }
@@ -1572,8 +1574,8 @@ var ReaddirpStream = class extends Readable {
1572
1574
  const par = this.parent;
1573
1575
  const fil = par && par.files;
1574
1576
  if (fil && fil.length > 0) {
1575
- const { path: path18, depth } = par;
1576
- const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path18));
1577
+ const { path: path19, depth } = par;
1578
+ const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path19));
1577
1579
  const awaited = await Promise.all(slice);
1578
1580
  for (const entry of awaited) {
1579
1581
  if (!entry)
@@ -1613,20 +1615,20 @@ var ReaddirpStream = class extends Readable {
1613
1615
  this.reading = false;
1614
1616
  }
1615
1617
  }
1616
- async _exploreDir(path18, depth) {
1618
+ async _exploreDir(path19, depth) {
1617
1619
  let files;
1618
1620
  try {
1619
- files = await readdir(path18, this._rdOptions);
1621
+ files = await readdir(path19, this._rdOptions);
1620
1622
  } catch (error) {
1621
1623
  this._onError(error);
1622
1624
  }
1623
- return { files, depth, path: path18 };
1625
+ return { files, depth, path: path19 };
1624
1626
  }
1625
- async _formatEntry(dirent, path18) {
1627
+ async _formatEntry(dirent, path19) {
1626
1628
  let entry;
1627
1629
  const basename5 = this._isDirent ? dirent.name : dirent;
1628
1630
  try {
1629
- const fullPath = presolve(pjoin(path18, basename5));
1631
+ const fullPath = presolve(pjoin(path19, basename5));
1630
1632
  entry = { path: prelative(this._root, fullPath), fullPath, basename: basename5 };
1631
1633
  entry[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath);
1632
1634
  } catch (err) {
@@ -2026,16 +2028,16 @@ var delFromSet = (main, prop, item) => {
2026
2028
  };
2027
2029
  var isEmptySet = (val) => val instanceof Set ? val.size === 0 : !val;
2028
2030
  var FsWatchInstances = /* @__PURE__ */ new Map();
2029
- function createFsWatchInstance(path18, options, listener, errHandler, emitRaw) {
2031
+ function createFsWatchInstance(path19, options, listener, errHandler, emitRaw) {
2030
2032
  const handleEvent = (rawEvent, evPath) => {
2031
- listener(path18);
2032
- emitRaw(rawEvent, evPath, { watchedPath: path18 });
2033
- if (evPath && path18 !== evPath) {
2034
- fsWatchBroadcast(sp.resolve(path18, evPath), KEY_LISTENERS, sp.join(path18, evPath));
2033
+ listener(path19);
2034
+ emitRaw(rawEvent, evPath, { watchedPath: path19 });
2035
+ if (evPath && path19 !== evPath) {
2036
+ fsWatchBroadcast(sp.resolve(path19, evPath), KEY_LISTENERS, sp.join(path19, evPath));
2035
2037
  }
2036
2038
  };
2037
2039
  try {
2038
- return fs_watch(path18, {
2040
+ return fs_watch(path19, {
2039
2041
  persistent: options.persistent
2040
2042
  }, handleEvent);
2041
2043
  } catch (error) {
@@ -2051,12 +2053,12 @@ var fsWatchBroadcast = (fullPath, listenerType, val1, val2, val3) => {
2051
2053
  listener(val1, val2, val3);
2052
2054
  });
2053
2055
  };
2054
- var setFsWatchListener = (path18, fullPath, options, handlers) => {
2056
+ var setFsWatchListener = (path19, fullPath, options, handlers) => {
2055
2057
  const { listener, errHandler, rawEmitter } = handlers;
2056
2058
  let cont = FsWatchInstances.get(fullPath);
2057
2059
  let watcher;
2058
2060
  if (!options.persistent) {
2059
- watcher = createFsWatchInstance(path18, options, listener, errHandler, rawEmitter);
2061
+ watcher = createFsWatchInstance(path19, options, listener, errHandler, rawEmitter);
2060
2062
  if (!watcher)
2061
2063
  return;
2062
2064
  return watcher.close.bind(watcher);
@@ -2067,7 +2069,7 @@ var setFsWatchListener = (path18, fullPath, options, handlers) => {
2067
2069
  addAndConvert(cont, KEY_RAW, rawEmitter);
2068
2070
  } else {
2069
2071
  watcher = createFsWatchInstance(
2070
- path18,
2072
+ path19,
2071
2073
  options,
2072
2074
  fsWatchBroadcast.bind(null, fullPath, KEY_LISTENERS),
2073
2075
  errHandler,
@@ -2082,7 +2084,7 @@ var setFsWatchListener = (path18, fullPath, options, handlers) => {
2082
2084
  cont.watcherUnusable = true;
2083
2085
  if (isWindows && error.code === "EPERM") {
2084
2086
  try {
2085
- const fd = await open(path18, "r");
2087
+ const fd = await open(path19, "r");
2086
2088
  await fd.close();
2087
2089
  broadcastErr(error);
2088
2090
  } catch (err) {
@@ -2113,7 +2115,7 @@ var setFsWatchListener = (path18, fullPath, options, handlers) => {
2113
2115
  };
2114
2116
  };
2115
2117
  var FsWatchFileInstances = /* @__PURE__ */ new Map();
2116
- var setFsWatchFileListener = (path18, fullPath, options, handlers) => {
2118
+ var setFsWatchFileListener = (path19, fullPath, options, handlers) => {
2117
2119
  const { listener, rawEmitter } = handlers;
2118
2120
  let cont = FsWatchFileInstances.get(fullPath);
2119
2121
  const copts = cont && cont.options;
@@ -2135,7 +2137,7 @@ var setFsWatchFileListener = (path18, fullPath, options, handlers) => {
2135
2137
  });
2136
2138
  const currmtime = curr.mtimeMs;
2137
2139
  if (curr.size !== prev.size || currmtime > prev.mtimeMs || currmtime === 0) {
2138
- foreach(cont.listeners, (listener2) => listener2(path18, curr));
2140
+ foreach(cont.listeners, (listener2) => listener2(path19, curr));
2139
2141
  }
2140
2142
  })
2141
2143
  };
@@ -2165,13 +2167,13 @@ var NodeFsHandler = class {
2165
2167
  * @param listener on fs change
2166
2168
  * @returns closer for the watcher instance
2167
2169
  */
2168
- _watchWithNodeFs(path18, listener) {
2170
+ _watchWithNodeFs(path19, listener) {
2169
2171
  const opts = this.fsw.options;
2170
- const directory = sp.dirname(path18);
2171
- const basename5 = sp.basename(path18);
2172
+ const directory = sp.dirname(path19);
2173
+ const basename5 = sp.basename(path19);
2172
2174
  const parent = this.fsw._getWatchedDir(directory);
2173
2175
  parent.add(basename5);
2174
- const absolutePath = sp.resolve(path18);
2176
+ const absolutePath = sp.resolve(path19);
2175
2177
  const options = {
2176
2178
  persistent: opts.persistent
2177
2179
  };
@@ -2181,12 +2183,12 @@ var NodeFsHandler = class {
2181
2183
  if (opts.usePolling) {
2182
2184
  const enableBin = opts.interval !== opts.binaryInterval;
2183
2185
  options.interval = enableBin && isBinaryPath(basename5) ? opts.binaryInterval : opts.interval;
2184
- closer = setFsWatchFileListener(path18, absolutePath, options, {
2186
+ closer = setFsWatchFileListener(path19, absolutePath, options, {
2185
2187
  listener,
2186
2188
  rawEmitter: this.fsw._emitRaw
2187
2189
  });
2188
2190
  } else {
2189
- closer = setFsWatchListener(path18, absolutePath, options, {
2191
+ closer = setFsWatchListener(path19, absolutePath, options, {
2190
2192
  listener,
2191
2193
  errHandler: this._boundHandleError,
2192
2194
  rawEmitter: this.fsw._emitRaw
@@ -2208,7 +2210,7 @@ var NodeFsHandler = class {
2208
2210
  let prevStats = stats;
2209
2211
  if (parent.has(basename5))
2210
2212
  return;
2211
- const listener = async (path18, newStats) => {
2213
+ const listener = async (path19, newStats) => {
2212
2214
  if (!this.fsw._throttle(THROTTLE_MODE_WATCH, file, 5))
2213
2215
  return;
2214
2216
  if (!newStats || newStats.mtimeMs === 0) {
@@ -2222,11 +2224,11 @@ var NodeFsHandler = class {
2222
2224
  this.fsw._emit(EV.CHANGE, file, newStats2);
2223
2225
  }
2224
2226
  if ((isMacos || isLinux || isFreeBSD) && prevStats.ino !== newStats2.ino) {
2225
- this.fsw._closeFile(path18);
2227
+ this.fsw._closeFile(path19);
2226
2228
  prevStats = newStats2;
2227
2229
  const closer2 = this._watchWithNodeFs(file, listener);
2228
2230
  if (closer2)
2229
- this.fsw._addPathCloser(path18, closer2);
2231
+ this.fsw._addPathCloser(path19, closer2);
2230
2232
  } else {
2231
2233
  prevStats = newStats2;
2232
2234
  }
@@ -2258,7 +2260,7 @@ var NodeFsHandler = class {
2258
2260
  * @param item basename of this item
2259
2261
  * @returns true if no more processing is needed for this entry.
2260
2262
  */
2261
- async _handleSymlink(entry, directory, path18, item) {
2263
+ async _handleSymlink(entry, directory, path19, item) {
2262
2264
  if (this.fsw.closed) {
2263
2265
  return;
2264
2266
  }
@@ -2268,7 +2270,7 @@ var NodeFsHandler = class {
2268
2270
  this.fsw._incrReadyCount();
2269
2271
  let linkPath;
2270
2272
  try {
2271
- linkPath = await fsrealpath(path18);
2273
+ linkPath = await fsrealpath(path19);
2272
2274
  } catch (e) {
2273
2275
  this.fsw._emitReady();
2274
2276
  return true;
@@ -2278,12 +2280,12 @@ var NodeFsHandler = class {
2278
2280
  if (dir.has(item)) {
2279
2281
  if (this.fsw._symlinkPaths.get(full) !== linkPath) {
2280
2282
  this.fsw._symlinkPaths.set(full, linkPath);
2281
- this.fsw._emit(EV.CHANGE, path18, entry.stats);
2283
+ this.fsw._emit(EV.CHANGE, path19, entry.stats);
2282
2284
  }
2283
2285
  } else {
2284
2286
  dir.add(item);
2285
2287
  this.fsw._symlinkPaths.set(full, linkPath);
2286
- this.fsw._emit(EV.ADD, path18, entry.stats);
2288
+ this.fsw._emit(EV.ADD, path19, entry.stats);
2287
2289
  }
2288
2290
  this.fsw._emitReady();
2289
2291
  return true;
@@ -2313,9 +2315,9 @@ var NodeFsHandler = class {
2313
2315
  return;
2314
2316
  }
2315
2317
  const item = entry.path;
2316
- let path18 = sp.join(directory, item);
2318
+ let path19 = sp.join(directory, item);
2317
2319
  current.add(item);
2318
- if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path18, item)) {
2320
+ if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path19, item)) {
2319
2321
  return;
2320
2322
  }
2321
2323
  if (this.fsw.closed) {
@@ -2324,11 +2326,11 @@ var NodeFsHandler = class {
2324
2326
  }
2325
2327
  if (item === target || !target && !previous.has(item)) {
2326
2328
  this.fsw._incrReadyCount();
2327
- path18 = sp.join(dir, sp.relative(dir, path18));
2328
- this._addToNodeFs(path18, initialAdd, wh, depth + 1);
2329
+ path19 = sp.join(dir, sp.relative(dir, path19));
2330
+ this._addToNodeFs(path19, initialAdd, wh, depth + 1);
2329
2331
  }
2330
2332
  }).on(EV.ERROR, this._boundHandleError);
2331
- return new Promise((resolve12, reject) => {
2333
+ return new Promise((resolve13, reject) => {
2332
2334
  if (!stream)
2333
2335
  return reject();
2334
2336
  stream.once(STR_END, () => {
@@ -2337,7 +2339,7 @@ var NodeFsHandler = class {
2337
2339
  return;
2338
2340
  }
2339
2341
  const wasThrottled = throttler ? throttler.clear() : false;
2340
- resolve12(void 0);
2342
+ resolve13(void 0);
2341
2343
  previous.getChildren().filter((item) => {
2342
2344
  return item !== directory && !current.has(item);
2343
2345
  }).forEach((item) => {
@@ -2394,13 +2396,13 @@ var NodeFsHandler = class {
2394
2396
  * @param depth Child path actually targeted for watch
2395
2397
  * @param target Child path actually targeted for watch
2396
2398
  */
2397
- async _addToNodeFs(path18, initialAdd, priorWh, depth, target) {
2399
+ async _addToNodeFs(path19, initialAdd, priorWh, depth, target) {
2398
2400
  const ready = this.fsw._emitReady;
2399
- if (this.fsw._isIgnored(path18) || this.fsw.closed) {
2401
+ if (this.fsw._isIgnored(path19) || this.fsw.closed) {
2400
2402
  ready();
2401
2403
  return false;
2402
2404
  }
2403
- const wh = this.fsw._getWatchHelpers(path18);
2405
+ const wh = this.fsw._getWatchHelpers(path19);
2404
2406
  if (priorWh) {
2405
2407
  wh.filterPath = (entry) => priorWh.filterPath(entry);
2406
2408
  wh.filterDir = (entry) => priorWh.filterDir(entry);
@@ -2416,8 +2418,8 @@ var NodeFsHandler = class {
2416
2418
  const follow = this.fsw.options.followSymlinks;
2417
2419
  let closer;
2418
2420
  if (stats.isDirectory()) {
2419
- const absPath = sp.resolve(path18);
2420
- const targetPath = follow ? await fsrealpath(path18) : path18;
2421
+ const absPath = sp.resolve(path19);
2422
+ const targetPath = follow ? await fsrealpath(path19) : path19;
2421
2423
  if (this.fsw.closed)
2422
2424
  return;
2423
2425
  closer = await this._handleDir(wh.watchPath, stats, initialAdd, depth, target, wh, targetPath);
@@ -2427,29 +2429,29 @@ var NodeFsHandler = class {
2427
2429
  this.fsw._symlinkPaths.set(absPath, targetPath);
2428
2430
  }
2429
2431
  } else if (stats.isSymbolicLink()) {
2430
- const targetPath = follow ? await fsrealpath(path18) : path18;
2432
+ const targetPath = follow ? await fsrealpath(path19) : path19;
2431
2433
  if (this.fsw.closed)
2432
2434
  return;
2433
2435
  const parent = sp.dirname(wh.watchPath);
2434
2436
  this.fsw._getWatchedDir(parent).add(wh.watchPath);
2435
2437
  this.fsw._emit(EV.ADD, wh.watchPath, stats);
2436
- closer = await this._handleDir(parent, stats, initialAdd, depth, path18, wh, targetPath);
2438
+ closer = await this._handleDir(parent, stats, initialAdd, depth, path19, wh, targetPath);
2437
2439
  if (this.fsw.closed)
2438
2440
  return;
2439
2441
  if (targetPath !== void 0) {
2440
- this.fsw._symlinkPaths.set(sp.resolve(path18), targetPath);
2442
+ this.fsw._symlinkPaths.set(sp.resolve(path19), targetPath);
2441
2443
  }
2442
2444
  } else {
2443
2445
  closer = this._handleFile(wh.watchPath, stats, initialAdd);
2444
2446
  }
2445
2447
  ready();
2446
2448
  if (closer)
2447
- this.fsw._addPathCloser(path18, closer);
2449
+ this.fsw._addPathCloser(path19, closer);
2448
2450
  return false;
2449
2451
  } catch (error) {
2450
2452
  if (this.fsw._handleError(error)) {
2451
2453
  ready();
2452
- return path18;
2454
+ return path19;
2453
2455
  }
2454
2456
  }
2455
2457
  }
@@ -2481,35 +2483,35 @@ function createPattern(matcher) {
2481
2483
  if (matcher.path === string)
2482
2484
  return true;
2483
2485
  if (matcher.recursive) {
2484
- const relative8 = sp2.relative(matcher.path, string);
2485
- if (!relative8) {
2486
+ const relative9 = sp2.relative(matcher.path, string);
2487
+ if (!relative9) {
2486
2488
  return false;
2487
2489
  }
2488
- return !relative8.startsWith("..") && !sp2.isAbsolute(relative8);
2490
+ return !relative9.startsWith("..") && !sp2.isAbsolute(relative9);
2489
2491
  }
2490
2492
  return false;
2491
2493
  };
2492
2494
  }
2493
2495
  return () => false;
2494
2496
  }
2495
- function normalizePath(path18) {
2496
- if (typeof path18 !== "string")
2497
+ function normalizePath(path19) {
2498
+ if (typeof path19 !== "string")
2497
2499
  throw new Error("string expected");
2498
- path18 = sp2.normalize(path18);
2499
- path18 = path18.replace(/\\/g, "/");
2500
+ path19 = sp2.normalize(path19);
2501
+ path19 = path19.replace(/\\/g, "/");
2500
2502
  let prepend = false;
2501
- if (path18.startsWith("//"))
2503
+ if (path19.startsWith("//"))
2502
2504
  prepend = true;
2503
- path18 = path18.replace(DOUBLE_SLASH_RE, "/");
2505
+ path19 = path19.replace(DOUBLE_SLASH_RE, "/");
2504
2506
  if (prepend)
2505
- path18 = "/" + path18;
2506
- return path18;
2507
+ path19 = "/" + path19;
2508
+ return path19;
2507
2509
  }
2508
2510
  function matchPatterns(patterns, testString, stats) {
2509
- const path18 = normalizePath(testString);
2511
+ const path19 = normalizePath(testString);
2510
2512
  for (let index = 0; index < patterns.length; index++) {
2511
2513
  const pattern = patterns[index];
2512
- if (pattern(path18, stats)) {
2514
+ if (pattern(path19, stats)) {
2513
2515
  return true;
2514
2516
  }
2515
2517
  }
@@ -2547,19 +2549,19 @@ var toUnix = (string) => {
2547
2549
  }
2548
2550
  return str;
2549
2551
  };
2550
- var normalizePathToUnix = (path18) => toUnix(sp2.normalize(toUnix(path18)));
2551
- var normalizeIgnored = (cwd = "") => (path18) => {
2552
- if (typeof path18 === "string") {
2553
- return normalizePathToUnix(sp2.isAbsolute(path18) ? path18 : sp2.join(cwd, path18));
2552
+ var normalizePathToUnix = (path19) => toUnix(sp2.normalize(toUnix(path19)));
2553
+ var normalizeIgnored = (cwd = "") => (path19) => {
2554
+ if (typeof path19 === "string") {
2555
+ return normalizePathToUnix(sp2.isAbsolute(path19) ? path19 : sp2.join(cwd, path19));
2554
2556
  } else {
2555
- return path18;
2557
+ return path19;
2556
2558
  }
2557
2559
  };
2558
- var getAbsolutePath = (path18, cwd) => {
2559
- if (sp2.isAbsolute(path18)) {
2560
- return path18;
2560
+ var getAbsolutePath = (path19, cwd) => {
2561
+ if (sp2.isAbsolute(path19)) {
2562
+ return path19;
2561
2563
  }
2562
- return sp2.join(cwd, path18);
2564
+ return sp2.join(cwd, path19);
2563
2565
  };
2564
2566
  var EMPTY_SET = Object.freeze(/* @__PURE__ */ new Set());
2565
2567
  var DirEntry = class {
@@ -2624,10 +2626,10 @@ var WatchHelper = class {
2624
2626
  dirParts;
2625
2627
  followSymlinks;
2626
2628
  statMethod;
2627
- constructor(path18, follow, fsw) {
2629
+ constructor(path19, follow, fsw) {
2628
2630
  this.fsw = fsw;
2629
- const watchPath = path18;
2630
- this.path = path18 = path18.replace(REPLACER_RE, "");
2631
+ const watchPath = path19;
2632
+ this.path = path19 = path19.replace(REPLACER_RE, "");
2631
2633
  this.watchPath = watchPath;
2632
2634
  this.fullWatchPath = sp2.resolve(watchPath);
2633
2635
  this.dirParts = [];
@@ -2767,20 +2769,20 @@ var FSWatcher = class extends EventEmitter {
2767
2769
  this._closePromise = void 0;
2768
2770
  let paths = unifyPaths(paths_);
2769
2771
  if (cwd) {
2770
- paths = paths.map((path18) => {
2771
- const absPath = getAbsolutePath(path18, cwd);
2772
+ paths = paths.map((path19) => {
2773
+ const absPath = getAbsolutePath(path19, cwd);
2772
2774
  return absPath;
2773
2775
  });
2774
2776
  }
2775
- paths.forEach((path18) => {
2776
- this._removeIgnoredPath(path18);
2777
+ paths.forEach((path19) => {
2778
+ this._removeIgnoredPath(path19);
2777
2779
  });
2778
2780
  this._userIgnored = void 0;
2779
2781
  if (!this._readyCount)
2780
2782
  this._readyCount = 0;
2781
2783
  this._readyCount += paths.length;
2782
- Promise.all(paths.map(async (path18) => {
2783
- const res = await this._nodeFsHandler._addToNodeFs(path18, !_internal, void 0, 0, _origAdd);
2784
+ Promise.all(paths.map(async (path19) => {
2785
+ const res = await this._nodeFsHandler._addToNodeFs(path19, !_internal, void 0, 0, _origAdd);
2784
2786
  if (res)
2785
2787
  this._emitReady();
2786
2788
  return res;
@@ -2802,17 +2804,17 @@ var FSWatcher = class extends EventEmitter {
2802
2804
  return this;
2803
2805
  const paths = unifyPaths(paths_);
2804
2806
  const { cwd } = this.options;
2805
- paths.forEach((path18) => {
2806
- if (!sp2.isAbsolute(path18) && !this._closers.has(path18)) {
2807
+ paths.forEach((path19) => {
2808
+ if (!sp2.isAbsolute(path19) && !this._closers.has(path19)) {
2807
2809
  if (cwd)
2808
- path18 = sp2.join(cwd, path18);
2809
- path18 = sp2.resolve(path18);
2810
+ path19 = sp2.join(cwd, path19);
2811
+ path19 = sp2.resolve(path19);
2810
2812
  }
2811
- this._closePath(path18);
2812
- this._addIgnoredPath(path18);
2813
- if (this._watched.has(path18)) {
2813
+ this._closePath(path19);
2814
+ this._addIgnoredPath(path19);
2815
+ if (this._watched.has(path19)) {
2814
2816
  this._addIgnoredPath({
2815
- path: path18,
2817
+ path: path19,
2816
2818
  recursive: true
2817
2819
  });
2818
2820
  }
@@ -2876,38 +2878,38 @@ var FSWatcher = class extends EventEmitter {
2876
2878
  * @param stats arguments to be passed with event
2877
2879
  * @returns the error if defined, otherwise the value of the FSWatcher instance's `closed` flag
2878
2880
  */
2879
- async _emit(event, path18, stats) {
2881
+ async _emit(event, path19, stats) {
2880
2882
  if (this.closed)
2881
2883
  return;
2882
2884
  const opts = this.options;
2883
2885
  if (isWindows)
2884
- path18 = sp2.normalize(path18);
2886
+ path19 = sp2.normalize(path19);
2885
2887
  if (opts.cwd)
2886
- path18 = sp2.relative(opts.cwd, path18);
2887
- const args = [path18];
2888
+ path19 = sp2.relative(opts.cwd, path19);
2889
+ const args = [path19];
2888
2890
  if (stats != null)
2889
2891
  args.push(stats);
2890
2892
  const awf = opts.awaitWriteFinish;
2891
2893
  let pw;
2892
- if (awf && (pw = this._pendingWrites.get(path18))) {
2894
+ if (awf && (pw = this._pendingWrites.get(path19))) {
2893
2895
  pw.lastChange = /* @__PURE__ */ new Date();
2894
2896
  return this;
2895
2897
  }
2896
2898
  if (opts.atomic) {
2897
2899
  if (event === EVENTS.UNLINK) {
2898
- this._pendingUnlinks.set(path18, [event, ...args]);
2900
+ this._pendingUnlinks.set(path19, [event, ...args]);
2899
2901
  setTimeout(() => {
2900
- this._pendingUnlinks.forEach((entry, path19) => {
2902
+ this._pendingUnlinks.forEach((entry, path20) => {
2901
2903
  this.emit(...entry);
2902
2904
  this.emit(EVENTS.ALL, ...entry);
2903
- this._pendingUnlinks.delete(path19);
2905
+ this._pendingUnlinks.delete(path20);
2904
2906
  });
2905
2907
  }, typeof opts.atomic === "number" ? opts.atomic : 100);
2906
2908
  return this;
2907
2909
  }
2908
- if (event === EVENTS.ADD && this._pendingUnlinks.has(path18)) {
2910
+ if (event === EVENTS.ADD && this._pendingUnlinks.has(path19)) {
2909
2911
  event = EVENTS.CHANGE;
2910
- this._pendingUnlinks.delete(path18);
2912
+ this._pendingUnlinks.delete(path19);
2911
2913
  }
2912
2914
  }
2913
2915
  if (awf && (event === EVENTS.ADD || event === EVENTS.CHANGE) && this._readyEmitted) {
@@ -2925,16 +2927,16 @@ var FSWatcher = class extends EventEmitter {
2925
2927
  this.emitWithAll(event, args);
2926
2928
  }
2927
2929
  };
2928
- this._awaitWriteFinish(path18, awf.stabilityThreshold, event, awfEmit);
2930
+ this._awaitWriteFinish(path19, awf.stabilityThreshold, event, awfEmit);
2929
2931
  return this;
2930
2932
  }
2931
2933
  if (event === EVENTS.CHANGE) {
2932
- const isThrottled = !this._throttle(EVENTS.CHANGE, path18, 50);
2934
+ const isThrottled = !this._throttle(EVENTS.CHANGE, path19, 50);
2933
2935
  if (isThrottled)
2934
2936
  return this;
2935
2937
  }
2936
2938
  if (opts.alwaysStat && stats === void 0 && (event === EVENTS.ADD || event === EVENTS.ADD_DIR || event === EVENTS.CHANGE)) {
2937
- const fullPath = opts.cwd ? sp2.join(opts.cwd, path18) : path18;
2939
+ const fullPath = opts.cwd ? sp2.join(opts.cwd, path19) : path19;
2938
2940
  let stats2;
2939
2941
  try {
2940
2942
  stats2 = await stat3(fullPath);
@@ -2965,23 +2967,23 @@ var FSWatcher = class extends EventEmitter {
2965
2967
  * @param timeout duration of time to suppress duplicate actions
2966
2968
  * @returns tracking object or false if action should be suppressed
2967
2969
  */
2968
- _throttle(actionType, path18, timeout) {
2970
+ _throttle(actionType, path19, timeout) {
2969
2971
  if (!this._throttled.has(actionType)) {
2970
2972
  this._throttled.set(actionType, /* @__PURE__ */ new Map());
2971
2973
  }
2972
2974
  const action = this._throttled.get(actionType);
2973
2975
  if (!action)
2974
2976
  throw new Error("invalid throttle");
2975
- const actionPath = action.get(path18);
2977
+ const actionPath = action.get(path19);
2976
2978
  if (actionPath) {
2977
2979
  actionPath.count++;
2978
2980
  return false;
2979
2981
  }
2980
2982
  let timeoutObject;
2981
2983
  const clear = () => {
2982
- const item = action.get(path18);
2984
+ const item = action.get(path19);
2983
2985
  const count = item ? item.count : 0;
2984
- action.delete(path18);
2986
+ action.delete(path19);
2985
2987
  clearTimeout(timeoutObject);
2986
2988
  if (item)
2987
2989
  clearTimeout(item.timeoutObject);
@@ -2989,7 +2991,7 @@ var FSWatcher = class extends EventEmitter {
2989
2991
  };
2990
2992
  timeoutObject = setTimeout(clear, timeout);
2991
2993
  const thr = { timeoutObject, clear, count: 0 };
2992
- action.set(path18, thr);
2994
+ action.set(path19, thr);
2993
2995
  return thr;
2994
2996
  }
2995
2997
  _incrReadyCount() {
@@ -3003,44 +3005,44 @@ var FSWatcher = class extends EventEmitter {
3003
3005
  * @param event
3004
3006
  * @param awfEmit Callback to be called when ready for event to be emitted.
3005
3007
  */
3006
- _awaitWriteFinish(path18, threshold, event, awfEmit) {
3008
+ _awaitWriteFinish(path19, threshold, event, awfEmit) {
3007
3009
  const awf = this.options.awaitWriteFinish;
3008
3010
  if (typeof awf !== "object")
3009
3011
  return;
3010
3012
  const pollInterval = awf.pollInterval;
3011
3013
  let timeoutHandler;
3012
- let fullPath = path18;
3013
- if (this.options.cwd && !sp2.isAbsolute(path18)) {
3014
- fullPath = sp2.join(this.options.cwd, path18);
3014
+ let fullPath = path19;
3015
+ if (this.options.cwd && !sp2.isAbsolute(path19)) {
3016
+ fullPath = sp2.join(this.options.cwd, path19);
3015
3017
  }
3016
3018
  const now = /* @__PURE__ */ new Date();
3017
3019
  const writes = this._pendingWrites;
3018
3020
  function awaitWriteFinishFn(prevStat) {
3019
3021
  statcb(fullPath, (err, curStat) => {
3020
- if (err || !writes.has(path18)) {
3022
+ if (err || !writes.has(path19)) {
3021
3023
  if (err && err.code !== "ENOENT")
3022
3024
  awfEmit(err);
3023
3025
  return;
3024
3026
  }
3025
3027
  const now2 = Number(/* @__PURE__ */ new Date());
3026
3028
  if (prevStat && curStat.size !== prevStat.size) {
3027
- writes.get(path18).lastChange = now2;
3029
+ writes.get(path19).lastChange = now2;
3028
3030
  }
3029
- const pw = writes.get(path18);
3031
+ const pw = writes.get(path19);
3030
3032
  const df = now2 - pw.lastChange;
3031
3033
  if (df >= threshold) {
3032
- writes.delete(path18);
3034
+ writes.delete(path19);
3033
3035
  awfEmit(void 0, curStat);
3034
3036
  } else {
3035
3037
  timeoutHandler = setTimeout(awaitWriteFinishFn, pollInterval, curStat);
3036
3038
  }
3037
3039
  });
3038
3040
  }
3039
- if (!writes.has(path18)) {
3040
- writes.set(path18, {
3041
+ if (!writes.has(path19)) {
3042
+ writes.set(path19, {
3041
3043
  lastChange: now,
3042
3044
  cancelWait: () => {
3043
- writes.delete(path18);
3045
+ writes.delete(path19);
3044
3046
  clearTimeout(timeoutHandler);
3045
3047
  return event;
3046
3048
  }
@@ -3051,8 +3053,8 @@ var FSWatcher = class extends EventEmitter {
3051
3053
  /**
3052
3054
  * Determines whether user has asked to ignore this path.
3053
3055
  */
3054
- _isIgnored(path18, stats) {
3055
- if (this.options.atomic && DOT_RE.test(path18))
3056
+ _isIgnored(path19, stats) {
3057
+ if (this.options.atomic && DOT_RE.test(path19))
3056
3058
  return true;
3057
3059
  if (!this._userIgnored) {
3058
3060
  const { cwd } = this.options;
@@ -3062,17 +3064,17 @@ var FSWatcher = class extends EventEmitter {
3062
3064
  const list = [...ignoredPaths.map(normalizeIgnored(cwd)), ...ignored];
3063
3065
  this._userIgnored = anymatch(list, void 0);
3064
3066
  }
3065
- return this._userIgnored(path18, stats);
3067
+ return this._userIgnored(path19, stats);
3066
3068
  }
3067
- _isntIgnored(path18, stat4) {
3068
- return !this._isIgnored(path18, stat4);
3069
+ _isntIgnored(path19, stat4) {
3070
+ return !this._isIgnored(path19, stat4);
3069
3071
  }
3070
3072
  /**
3071
3073
  * Provides a set of common helpers and properties relating to symlink handling.
3072
3074
  * @param path file or directory pattern being watched
3073
3075
  */
3074
- _getWatchHelpers(path18) {
3075
- return new WatchHelper(path18, this.options.followSymlinks, this);
3076
+ _getWatchHelpers(path19) {
3077
+ return new WatchHelper(path19, this.options.followSymlinks, this);
3076
3078
  }
3077
3079
  // Directory helpers
3078
3080
  // -----------------
@@ -3104,63 +3106,63 @@ var FSWatcher = class extends EventEmitter {
3104
3106
  * @param item base path of item/directory
3105
3107
  */
3106
3108
  _remove(directory, item, isDirectory) {
3107
- const path18 = sp2.join(directory, item);
3108
- const fullPath = sp2.resolve(path18);
3109
- isDirectory = isDirectory != null ? isDirectory : this._watched.has(path18) || this._watched.has(fullPath);
3110
- if (!this._throttle("remove", path18, 100))
3109
+ const path19 = sp2.join(directory, item);
3110
+ const fullPath = sp2.resolve(path19);
3111
+ isDirectory = isDirectory != null ? isDirectory : this._watched.has(path19) || this._watched.has(fullPath);
3112
+ if (!this._throttle("remove", path19, 100))
3111
3113
  return;
3112
3114
  if (!isDirectory && this._watched.size === 1) {
3113
3115
  this.add(directory, item, true);
3114
3116
  }
3115
- const wp = this._getWatchedDir(path18);
3117
+ const wp = this._getWatchedDir(path19);
3116
3118
  const nestedDirectoryChildren = wp.getChildren();
3117
- nestedDirectoryChildren.forEach((nested) => this._remove(path18, nested));
3119
+ nestedDirectoryChildren.forEach((nested) => this._remove(path19, nested));
3118
3120
  const parent = this._getWatchedDir(directory);
3119
3121
  const wasTracked = parent.has(item);
3120
3122
  parent.remove(item);
3121
3123
  if (this._symlinkPaths.has(fullPath)) {
3122
3124
  this._symlinkPaths.delete(fullPath);
3123
3125
  }
3124
- let relPath = path18;
3126
+ let relPath = path19;
3125
3127
  if (this.options.cwd)
3126
- relPath = sp2.relative(this.options.cwd, path18);
3128
+ relPath = sp2.relative(this.options.cwd, path19);
3127
3129
  if (this.options.awaitWriteFinish && this._pendingWrites.has(relPath)) {
3128
3130
  const event = this._pendingWrites.get(relPath).cancelWait();
3129
3131
  if (event === EVENTS.ADD)
3130
3132
  return;
3131
3133
  }
3132
- this._watched.delete(path18);
3134
+ this._watched.delete(path19);
3133
3135
  this._watched.delete(fullPath);
3134
3136
  const eventName = isDirectory ? EVENTS.UNLINK_DIR : EVENTS.UNLINK;
3135
- if (wasTracked && !this._isIgnored(path18))
3136
- this._emit(eventName, path18);
3137
- this._closePath(path18);
3137
+ if (wasTracked && !this._isIgnored(path19))
3138
+ this._emit(eventName, path19);
3139
+ this._closePath(path19);
3138
3140
  }
3139
3141
  /**
3140
3142
  * Closes all watchers for a path
3141
3143
  */
3142
- _closePath(path18) {
3143
- this._closeFile(path18);
3144
- const dir = sp2.dirname(path18);
3145
- this._getWatchedDir(dir).remove(sp2.basename(path18));
3144
+ _closePath(path19) {
3145
+ this._closeFile(path19);
3146
+ const dir = sp2.dirname(path19);
3147
+ this._getWatchedDir(dir).remove(sp2.basename(path19));
3146
3148
  }
3147
3149
  /**
3148
3150
  * Closes only file-specific watchers
3149
3151
  */
3150
- _closeFile(path18) {
3151
- const closers = this._closers.get(path18);
3152
+ _closeFile(path19) {
3153
+ const closers = this._closers.get(path19);
3152
3154
  if (!closers)
3153
3155
  return;
3154
3156
  closers.forEach((closer) => closer());
3155
- this._closers.delete(path18);
3157
+ this._closers.delete(path19);
3156
3158
  }
3157
- _addPathCloser(path18, closer) {
3159
+ _addPathCloser(path19, closer) {
3158
3160
  if (!closer)
3159
3161
  return;
3160
- let list = this._closers.get(path18);
3162
+ let list = this._closers.get(path19);
3161
3163
  if (!list) {
3162
3164
  list = [];
3163
- this._closers.set(path18, list);
3165
+ this._closers.set(path19, list);
3164
3166
  }
3165
3167
  list.push(closer);
3166
3168
  }
@@ -3490,7 +3492,7 @@ var FileWatcher = class {
3490
3492
  return;
3491
3493
  }
3492
3494
  const changes = Array.from(this.pendingChanges.entries()).map(
3493
- ([path18, type]) => ({ path: path18, type })
3495
+ ([path19, type]) => ({ path: path19, type })
3494
3496
  );
3495
3497
  this.pendingChanges.clear();
3496
3498
  try {
@@ -3623,12 +3625,14 @@ function createWatcherWithIndexer(getIndexer, projectRoot, config) {
3623
3625
  }
3624
3626
 
3625
3627
  // src/tools/index.ts
3626
- import { tool } from "@opencode-ai/plugin";
3628
+ import { tool as tool2 } from "@opencode-ai/plugin";
3627
3629
 
3628
3630
  // src/indexer/index.ts
3629
3631
  import { existsSync as existsSync7, readFileSync as readFileSync6, writeFileSync as writeFileSync2, renameSync, unlinkSync, mkdirSync as mkdirSync2, promises as fsPromises2 } from "fs";
3630
- import * as path12 from "path";
3632
+ import * as path13 from "path";
3631
3633
  import { performance as performance2 } from "perf_hooks";
3634
+ import { execFile as execFile2 } from "child_process";
3635
+ import { promisify as promisify2 } from "util";
3632
3636
 
3633
3637
  // node_modules/eventemitter3/index.mjs
3634
3638
  var import_index = __toESM(require_eventemitter3(), 1);
@@ -3652,7 +3656,7 @@ function pTimeout(promise, options) {
3652
3656
  } = options;
3653
3657
  let timer;
3654
3658
  let abortHandler;
3655
- const wrappedPromise = new Promise((resolve12, reject) => {
3659
+ const wrappedPromise = new Promise((resolve13, reject) => {
3656
3660
  if (typeof milliseconds !== "number" || Math.sign(milliseconds) !== 1) {
3657
3661
  throw new TypeError(`Expected \`milliseconds\` to be a positive number, got \`${milliseconds}\``);
3658
3662
  }
@@ -3666,7 +3670,7 @@ function pTimeout(promise, options) {
3666
3670
  };
3667
3671
  signal.addEventListener("abort", abortHandler, { once: true });
3668
3672
  }
3669
- promise.then(resolve12, reject);
3673
+ promise.then(resolve13, reject);
3670
3674
  if (milliseconds === Number.POSITIVE_INFINITY) {
3671
3675
  return;
3672
3676
  }
@@ -3674,7 +3678,7 @@ function pTimeout(promise, options) {
3674
3678
  timer = customTimers.setTimeout.call(void 0, () => {
3675
3679
  if (fallback) {
3676
3680
  try {
3677
- resolve12(fallback());
3681
+ resolve13(fallback());
3678
3682
  } catch (error) {
3679
3683
  reject(error);
3680
3684
  }
@@ -3684,7 +3688,7 @@ function pTimeout(promise, options) {
3684
3688
  promise.cancel();
3685
3689
  }
3686
3690
  if (message === false) {
3687
- resolve12();
3691
+ resolve13();
3688
3692
  } else if (message instanceof Error) {
3689
3693
  reject(message);
3690
3694
  } else {
@@ -4086,7 +4090,7 @@ var PQueue = class extends import_index.default {
4086
4090
  // Assign unique ID if not provided
4087
4091
  id: options.id ?? (this.#idAssigner++).toString()
4088
4092
  };
4089
- return new Promise((resolve12, reject) => {
4093
+ return new Promise((resolve13, reject) => {
4090
4094
  const taskSymbol = /* @__PURE__ */ Symbol(`task-${options.id}`);
4091
4095
  let cleanupQueueAbortHandler = () => void 0;
4092
4096
  const run = async () => {
@@ -4126,7 +4130,7 @@ var PQueue = class extends import_index.default {
4126
4130
  })]);
4127
4131
  }
4128
4132
  const result = await operation;
4129
- resolve12(result);
4133
+ resolve13(result);
4130
4134
  this.emit("completed", result);
4131
4135
  } catch (error) {
4132
4136
  reject(error);
@@ -4314,13 +4318,13 @@ var PQueue = class extends import_index.default {
4314
4318
  });
4315
4319
  }
4316
4320
  async #onEvent(event, filter) {
4317
- return new Promise((resolve12) => {
4321
+ return new Promise((resolve13) => {
4318
4322
  const listener = () => {
4319
4323
  if (filter && !filter()) {
4320
4324
  return;
4321
4325
  }
4322
4326
  this.off(event, listener);
4323
- resolve12();
4327
+ resolve13();
4324
4328
  };
4325
4329
  this.on(event, listener);
4326
4330
  });
@@ -4606,7 +4610,7 @@ async function onAttemptFailure({ error, attemptNumber, retriesConsumed, startTi
4606
4610
  const finalDelay = Math.min(delayTime, remainingTime);
4607
4611
  options.signal?.throwIfAborted();
4608
4612
  if (finalDelay > 0) {
4609
- await new Promise((resolve12, reject) => {
4613
+ await new Promise((resolve13, reject) => {
4610
4614
  const onAbort = () => {
4611
4615
  clearTimeout(timeoutToken);
4612
4616
  options.signal?.removeEventListener("abort", onAbort);
@@ -4614,7 +4618,7 @@ async function onAttemptFailure({ error, attemptNumber, retriesConsumed, startTi
4614
4618
  };
4615
4619
  const timeoutToken = setTimeout(() => {
4616
4620
  options.signal?.removeEventListener("abort", onAbort);
4617
- resolve12();
4621
+ resolve13();
4618
4622
  }, finalDelay);
4619
4623
  if (options.unref) {
4620
4624
  timeoutToken.unref?.();
@@ -5841,6 +5845,15 @@ function createMockNativeBinding() {
5841
5845
  close() {
5842
5846
  throw error;
5843
5847
  }
5848
+ getTransitiveReachability() {
5849
+ throw error;
5850
+ }
5851
+ detectCommunities() {
5852
+ throw error;
5853
+ }
5854
+ computeCentrality() {
5855
+ throw error;
5856
+ }
5844
5857
  }
5845
5858
  };
5846
5859
  }
@@ -6391,6 +6404,14 @@ var Database = class {
6391
6404
  this.throwIfClosed();
6392
6405
  return this.inner.getSymbolsByNameCi(name);
6393
6406
  }
6407
+ getSymbolsForBranch(branch) {
6408
+ this.throwIfClosed();
6409
+ return this.inner.getSymbolsForBranch(branch);
6410
+ }
6411
+ getSymbolsForFiles(filePaths, branch) {
6412
+ this.throwIfClosed();
6413
+ return this.inner.getSymbolsForFiles(filePaths, branch);
6414
+ }
6394
6415
  deleteSymbolsByFile(filePath) {
6395
6416
  this.throwIfClosed();
6396
6417
  return this.inner.deleteSymbolsByFile(filePath);
@@ -6468,8 +6489,125 @@ var Database = class {
6468
6489
  this.throwIfClosed();
6469
6490
  return this.inner.gcOrphanCallEdges();
6470
6491
  }
6492
+ getTransitiveReachability(rootSymbolIds, branch, direction, maxDepth) {
6493
+ this.throwIfClosed();
6494
+ return this.inner.getTransitiveReachability(
6495
+ rootSymbolIds,
6496
+ branch,
6497
+ direction,
6498
+ maxDepth ?? null
6499
+ );
6500
+ }
6501
+ detectCommunities(branch, symbolIds) {
6502
+ this.throwIfClosed();
6503
+ return this.inner.detectCommunities(branch, symbolIds ?? null);
6504
+ }
6505
+ computeCentrality(branch) {
6506
+ this.throwIfClosed();
6507
+ return this.inner.computeCentrality(branch);
6508
+ }
6471
6509
  };
6472
6510
 
6511
+ // src/tools/changed-files.ts
6512
+ import * as path12 from "path";
6513
+ import { execFile } from "child_process";
6514
+ import { promisify } from "util";
6515
+ var execFileAsync = promisify(execFile);
6516
+ function getErrorMessage(error) {
6517
+ if (error instanceof Error) return error.message;
6518
+ return String(error);
6519
+ }
6520
+ async function getChangedFiles(opts) {
6521
+ const { pr, branch, projectRoot, baseBranch = "main" } = opts;
6522
+ if (pr !== void 0) {
6523
+ return getChangedFilesForPr(pr, projectRoot, baseBranch);
6524
+ }
6525
+ return getChangedFilesForBranch(branch, projectRoot, baseBranch);
6526
+ }
6527
+ async function getChangedFilesForPr(pr, projectRoot, baseBranch) {
6528
+ let headRefName;
6529
+ let actualBaseBranch = baseBranch;
6530
+ try {
6531
+ const { stdout } = await execFileAsync(
6532
+ "gh",
6533
+ ["pr", "view", String(pr), "--json", "headRefName,baseRefName,files"],
6534
+ { cwd: projectRoot, timeout: 3e4 }
6535
+ );
6536
+ const data = JSON.parse(stdout);
6537
+ headRefName = data.headRefName;
6538
+ actualBaseBranch = data.baseRefName || baseBranch;
6539
+ if (data.files && data.files.length > 0) {
6540
+ return {
6541
+ files: normalizeFiles(
6542
+ data.files.map((f) => f.path),
6543
+ projectRoot
6544
+ ),
6545
+ baseBranch: actualBaseBranch,
6546
+ source: "gh",
6547
+ headRefName
6548
+ };
6549
+ }
6550
+ } catch (error) {
6551
+ throw new Error(
6552
+ `Failed to retrieve PR #${pr} via gh CLI: ${getErrorMessage(error)}`
6553
+ );
6554
+ }
6555
+ if (headRefName === void 0) {
6556
+ throw new Error(
6557
+ `PR #${pr} returned no usable branch or file information.`
6558
+ );
6559
+ }
6560
+ const result = await getChangedFilesForBranch(headRefName, projectRoot, actualBaseBranch);
6561
+ return { ...result, headRefName };
6562
+ }
6563
+ async function getChangedFilesForBranch(branch, projectRoot, baseBranch) {
6564
+ const targetBranch = branch || await getCurrentBranch2(projectRoot);
6565
+ const mergeBase = await getMergeBase(projectRoot, baseBranch, targetBranch);
6566
+ const { stdout } = await execFileAsync(
6567
+ "git",
6568
+ ["diff", "--name-only", `${mergeBase}...${targetBranch}`],
6569
+ { cwd: projectRoot, timeout: 3e4 }
6570
+ );
6571
+ return {
6572
+ files: normalizeFiles(stdout.split("\n"), projectRoot),
6573
+ baseBranch,
6574
+ source: "git",
6575
+ headRefName: targetBranch
6576
+ };
6577
+ }
6578
+ async function getCurrentBranch2(projectRoot) {
6579
+ const { stdout } = await execFileAsync(
6580
+ "git",
6581
+ ["branch", "--show-current"],
6582
+ { cwd: projectRoot, timeout: 3e4 }
6583
+ );
6584
+ return stdout.trim() || "HEAD";
6585
+ }
6586
+ async function getMergeBase(projectRoot, baseBranch, branch) {
6587
+ const { stdout } = await execFileAsync(
6588
+ "git",
6589
+ ["merge-base", baseBranch, branch],
6590
+ { cwd: projectRoot, timeout: 3e4 }
6591
+ );
6592
+ return stdout.trim();
6593
+ }
6594
+ function normalizeFiles(rawFiles, projectRoot) {
6595
+ const seen = /* @__PURE__ */ new Set();
6596
+ const result = [];
6597
+ for (const raw of rawFiles) {
6598
+ const trimmed = raw.trim();
6599
+ if (!trimmed) continue;
6600
+ const absolute = path12.resolve(projectRoot, trimmed);
6601
+ const relative9 = path12.relative(projectRoot, absolute);
6602
+ const cleaned = relative9.startsWith("./") ? relative9.slice(2) : relative9;
6603
+ if (!seen.has(cleaned)) {
6604
+ seen.add(cleaned);
6605
+ result.push(cleaned);
6606
+ }
6607
+ }
6608
+ return result;
6609
+ }
6610
+
6473
6611
  // src/indexer/index.ts
6474
6612
  var CALL_GRAPH_LANGUAGES = /* @__PURE__ */ new Set(["typescript", "tsx", "javascript", "jsx", "python", "go", "rust", "php", "apex", "zig", "gdscript", "matlab"]);
6475
6613
  var CASE_INSENSITIVE_LANGUAGES = /* @__PURE__ */ new Set(["apex"]);
@@ -6477,6 +6615,7 @@ var CALL_GRAPH_SYMBOL_CHUNK_TYPES = /* @__PURE__ */ new Set([
6477
6615
  "function_declaration",
6478
6616
  "function",
6479
6617
  "arrow_function",
6618
+ "export_statement",
6480
6619
  "method_definition",
6481
6620
  "class_declaration",
6482
6621
  "interface_declaration",
@@ -6515,7 +6654,7 @@ function float32ArrayToBuffer(arr) {
6515
6654
  function bufferToFloat32Array(buf) {
6516
6655
  return new Float32Array(buf.buffer, buf.byteOffset, buf.byteLength / 4);
6517
6656
  }
6518
- function getErrorMessage(error) {
6657
+ function getErrorMessage2(error) {
6519
6658
  if (error instanceof Error) {
6520
6659
  return error.message;
6521
6660
  }
@@ -6528,7 +6667,7 @@ function getErrorMessage(error) {
6528
6667
  return String(error);
6529
6668
  }
6530
6669
  function isRateLimitError(error) {
6531
- const message = getErrorMessage(error);
6670
+ const message = getErrorMessage2(error);
6532
6671
  return message.includes("429") || message.toLowerCase().includes("rate limit") || message.toLowerCase().includes("too many requests");
6533
6672
  }
6534
6673
  function getSafeEmbeddingChunkTokenLimit(provider) {
@@ -6546,7 +6685,7 @@ function getDynamicBatchOptions(provider) {
6546
6685
  return {};
6547
6686
  }
6548
6687
  function isSqliteCorruptionError(error) {
6549
- const message = getErrorMessage(error).toLowerCase();
6688
+ const message = getErrorMessage2(error).toLowerCase();
6550
6689
  return message.includes("database disk image is malformed") || message.includes("file is not a database") || message.includes("database schema is corrupt") || message.includes("sqlite_corrupt");
6551
6690
  }
6552
6691
  var STARTUP_WARNING_METADATA_KEY = "index.startupWarning";
@@ -6708,9 +6847,9 @@ function hasAllEmbeddingParts(parts, expectedPartCount) {
6708
6847
  return true;
6709
6848
  }
6710
6849
  function isPathWithinRoot(filePath, rootPath) {
6711
- const normalizedFilePath = path12.resolve(filePath);
6712
- const normalizedRoot = path12.resolve(rootPath);
6713
- return normalizedFilePath === normalizedRoot || normalizedFilePath.startsWith(`${normalizedRoot}${path12.sep}`);
6850
+ const normalizedFilePath = path13.resolve(filePath);
6851
+ const normalizedRoot = path13.resolve(rootPath);
6852
+ return normalizedFilePath === normalizedRoot || normalizedFilePath.startsWith(`${normalizedRoot}${path13.sep}`);
6714
6853
  }
6715
6854
  var rankingQueryTokenCache = /* @__PURE__ */ new Map();
6716
6855
  var rankingNameTokenCache = /* @__PURE__ */ new Map();
@@ -7763,9 +7902,9 @@ var Indexer = class {
7763
7902
  this.projectRoot = projectRoot;
7764
7903
  this.config = config;
7765
7904
  this.indexPath = this.getIndexPath();
7766
- this.fileHashCachePath = path12.join(this.indexPath, "file-hashes.json");
7767
- this.failedBatchesPath = path12.join(this.indexPath, "failed-batches.json");
7768
- this.indexingLockPath = path12.join(this.indexPath, "indexing.lock");
7905
+ this.fileHashCachePath = path13.join(this.indexPath, "file-hashes.json");
7906
+ this.failedBatchesPath = path13.join(this.indexPath, "failed-batches.json");
7907
+ this.indexingLockPath = path13.join(this.indexPath, "indexing.lock");
7769
7908
  this.logger = initializeLogger(config.debug);
7770
7909
  }
7771
7910
  getIndexPath() {
@@ -7797,14 +7936,14 @@ var Indexer = class {
7797
7936
  }
7798
7937
  atomicWriteSync(targetPath, data) {
7799
7938
  const tempPath = `${targetPath}.tmp`;
7800
- mkdirSync2(path12.dirname(targetPath), { recursive: true });
7939
+ mkdirSync2(path13.dirname(targetPath), { recursive: true });
7801
7940
  writeFileSync2(tempPath, data);
7802
7941
  renameSync(tempPath, targetPath);
7803
7942
  }
7804
7943
  getScopedRoots() {
7805
- const roots = /* @__PURE__ */ new Set([path12.resolve(this.projectRoot)]);
7944
+ const roots = /* @__PURE__ */ new Set([path13.resolve(this.projectRoot)]);
7806
7945
  for (const kbRoot of this.config.knowledgeBases) {
7807
- roots.add(path12.resolve(this.projectRoot, kbRoot));
7946
+ roots.add(path13.resolve(this.projectRoot, kbRoot));
7808
7947
  }
7809
7948
  return Array.from(roots);
7810
7949
  }
@@ -7813,22 +7952,29 @@ var Indexer = class {
7813
7952
  if (this.config.scope !== "global") {
7814
7953
  return branchName;
7815
7954
  }
7816
- const projectHash = hashContent(path12.resolve(this.projectRoot)).slice(0, 16);
7955
+ const projectHash = hashContent(path13.resolve(this.projectRoot)).slice(0, 16);
7956
+ return `${projectHash}:${branchName}`;
7957
+ }
7958
+ getBranchCatalogKeyFor(branchName) {
7959
+ if (this.config.scope !== "global") {
7960
+ return branchName;
7961
+ }
7962
+ const projectHash = hashContent(path13.resolve(this.projectRoot)).slice(0, 16);
7817
7963
  return `${projectHash}:${branchName}`;
7818
7964
  }
7819
7965
  getLegacyBranchCatalogKey() {
7820
7966
  return this.currentBranch || "default";
7821
7967
  }
7822
7968
  getLegacyMigrationMetadataKey() {
7823
- const projectHash = hashContent(path12.resolve(this.projectRoot)).slice(0, 16);
7969
+ const projectHash = hashContent(path13.resolve(this.projectRoot)).slice(0, 16);
7824
7970
  return `index.globalBranchMigration.${projectHash}`;
7825
7971
  }
7826
7972
  getProjectEmbeddingStrategyMetadataKey() {
7827
- const projectHash = hashContent(path12.resolve(this.projectRoot)).slice(0, 16);
7973
+ const projectHash = hashContent(path13.resolve(this.projectRoot)).slice(0, 16);
7828
7974
  return `index.embeddingStrategyVersion.${projectHash}`;
7829
7975
  }
7830
7976
  getProjectForceReembedMetadataKey() {
7831
- const projectHash = hashContent(path12.resolve(this.projectRoot)).slice(0, 16);
7977
+ const projectHash = hashContent(path13.resolve(this.projectRoot)).slice(0, 16);
7832
7978
  return `index.forceReembed.${projectHash}`;
7833
7979
  }
7834
7980
  hasProjectForceReembedPending() {
@@ -7922,7 +8068,7 @@ var Indexer = class {
7922
8068
  if (!this.database) {
7923
8069
  return { chunkIds, symbolIds };
7924
8070
  }
7925
- const projectRootPath = path12.resolve(this.projectRoot);
8071
+ const projectRootPath = path13.resolve(this.projectRoot);
7926
8072
  const projectLocalFilePaths = /* @__PURE__ */ new Set([
7927
8073
  ...Array.from(this.fileHashCache.keys()).filter(
7928
8074
  (filePath) => this.isFileInCurrentScope(filePath, roots) && isPathWithinRoot(filePath, projectRootPath)
@@ -7945,7 +8091,7 @@ var Indexer = class {
7945
8091
  if (this.config.scope !== "global") {
7946
8092
  return this.getBranchCatalogCleanupKeys();
7947
8093
  }
7948
- const projectHash = hashContent(path12.resolve(this.projectRoot)).slice(0, 16);
8094
+ const projectHash = hashContent(path13.resolve(this.projectRoot)).slice(0, 16);
7949
8095
  const keys = /* @__PURE__ */ new Set();
7950
8096
  const projectChunkIdSet = new Set(projectChunkIds);
7951
8097
  const projectSymbolIdSet = new Set(projectSymbolIds);
@@ -8029,7 +8175,7 @@ var Indexer = class {
8029
8175
  if (!this.database || this.config.scope !== "global") {
8030
8176
  return false;
8031
8177
  }
8032
- const projectHash = hashContent(path12.resolve(this.projectRoot)).slice(0, 16);
8178
+ const projectHash = hashContent(path13.resolve(this.projectRoot)).slice(0, 16);
8033
8179
  const roots = this.getScopedRoots();
8034
8180
  const { chunkIds: projectLocalChunkIds, symbolIds: projectLocalSymbolIds } = this.getProjectLocalScopedOwnershipIds(roots);
8035
8181
  return this.database.getAllBranches().some(
@@ -8063,7 +8209,7 @@ var Indexer = class {
8063
8209
  ...Array.from(this.fileHashCache.keys()).filter((filePath) => this.isFileInCurrentScope(filePath, roots)),
8064
8210
  ...scopedEntries.map(({ metadata }) => metadata.filePath)
8065
8211
  ]);
8066
- const projectRootPath = path12.resolve(this.projectRoot);
8212
+ const projectRootPath = path13.resolve(this.projectRoot);
8067
8213
  const projectLocalFilePaths = new Set(
8068
8214
  Array.from(filePaths).filter((filePath) => isPathWithinRoot(filePath, projectRootPath))
8069
8215
  );
@@ -8316,7 +8462,7 @@ var Indexer = class {
8316
8462
  this.logger.search("warn", "External reranker failed; using deterministic order", {
8317
8463
  provider: reranker.provider,
8318
8464
  model: reranker.model,
8319
- error: getErrorMessage(error)
8465
+ error: getErrorMessage2(error)
8320
8466
  });
8321
8467
  return candidates;
8322
8468
  }
@@ -8423,13 +8569,13 @@ var Indexer = class {
8423
8569
  }
8424
8570
  await fsPromises2.mkdir(this.indexPath, { recursive: true });
8425
8571
  const dimensions = this.configuredProviderInfo.modelInfo.dimensions;
8426
- const storePath = path12.join(this.indexPath, "vectors");
8572
+ const storePath = path13.join(this.indexPath, "vectors");
8427
8573
  this.store = new VectorStore(storePath, dimensions);
8428
- const indexFilePath = path12.join(this.indexPath, "vectors.usearch");
8574
+ const indexFilePath = path13.join(this.indexPath, "vectors.usearch");
8429
8575
  if (existsSync7(indexFilePath)) {
8430
8576
  this.store.load();
8431
8577
  }
8432
- const invertedIndexPath = path12.join(this.indexPath, "inverted-index.json");
8578
+ const invertedIndexPath = path13.join(this.indexPath, "inverted-index.json");
8433
8579
  this.invertedIndex = new InvertedIndex(invertedIndexPath);
8434
8580
  try {
8435
8581
  this.invertedIndex.load();
@@ -8439,7 +8585,7 @@ var Indexer = class {
8439
8585
  }
8440
8586
  this.invertedIndex = new InvertedIndex(invertedIndexPath);
8441
8587
  }
8442
- const dbPath = path12.join(this.indexPath, "codebase.db");
8588
+ const dbPath = path13.join(this.indexPath, "codebase.db");
8443
8589
  let dbIsNew = !existsSync7(dbPath);
8444
8590
  try {
8445
8591
  this.database = new Database(dbPath);
@@ -8520,7 +8666,7 @@ var Indexer = class {
8520
8666
  if (await this.tryResetCorruptedIndex("running automatic orphan garbage collection", error)) {
8521
8667
  return {
8522
8668
  resetCorruptedIndex: true,
8523
- warning: this.getCorruptedIndexWarning(path12.join(this.indexPath, "codebase.db"))
8669
+ warning: this.getCorruptedIndexWarning(path13.join(this.indexPath, "codebase.db"))
8524
8670
  };
8525
8671
  }
8526
8672
  throw error;
@@ -8535,7 +8681,7 @@ var Indexer = class {
8535
8681
  return;
8536
8682
  }
8537
8683
  const retainedEntries = store.getAllMetadata().filter(({ key }) => !excludedSet.has(key));
8538
- const storeBasePath = path12.join(this.indexPath, "vectors");
8684
+ const storeBasePath = path13.join(this.indexPath, "vectors");
8539
8685
  const storeIndexPath = `${storeBasePath}.usearch`;
8540
8686
  const storeMetadataPath = `${storeBasePath}.meta.json`;
8541
8687
  const backupIndexPath = `${storeIndexPath}.bak`;
@@ -8620,9 +8766,9 @@ var Indexer = class {
8620
8766
  if (!isSqliteCorruptionError(error)) {
8621
8767
  return false;
8622
8768
  }
8623
- const dbPath = path12.join(this.indexPath, "codebase.db");
8769
+ const dbPath = path13.join(this.indexPath, "codebase.db");
8624
8770
  const warning = this.getCorruptedIndexWarning(dbPath);
8625
- const errorMessage = getErrorMessage(error);
8771
+ const errorMessage = getErrorMessage2(error);
8626
8772
  if (this.config.scope === "global") {
8627
8773
  this.logger.error("Detected corrupted shared global index database", {
8628
8774
  stage,
@@ -8643,15 +8789,15 @@ var Indexer = class {
8643
8789
  this.indexCompatibility = null;
8644
8790
  this.fileHashCache.clear();
8645
8791
  const resetPaths = [
8646
- path12.join(this.indexPath, "codebase.db"),
8647
- path12.join(this.indexPath, "codebase.db-shm"),
8648
- path12.join(this.indexPath, "codebase.db-wal"),
8649
- path12.join(this.indexPath, "vectors.usearch"),
8650
- path12.join(this.indexPath, "inverted-index.json"),
8651
- path12.join(this.indexPath, "file-hashes.json"),
8652
- path12.join(this.indexPath, "failed-batches.json"),
8653
- path12.join(this.indexPath, "indexing.lock"),
8654
- path12.join(this.indexPath, "vectors")
8792
+ path13.join(this.indexPath, "codebase.db"),
8793
+ path13.join(this.indexPath, "codebase.db-shm"),
8794
+ path13.join(this.indexPath, "codebase.db-wal"),
8795
+ path13.join(this.indexPath, "vectors.usearch"),
8796
+ path13.join(this.indexPath, "inverted-index.json"),
8797
+ path13.join(this.indexPath, "file-hashes.json"),
8798
+ path13.join(this.indexPath, "failed-batches.json"),
8799
+ path13.join(this.indexPath, "indexing.lock"),
8800
+ path13.join(this.indexPath, "vectors")
8655
8801
  ];
8656
8802
  await Promise.all(resetPaths.map(async (targetPath) => {
8657
8803
  try {
@@ -8916,7 +9062,7 @@ var Indexer = class {
8916
9062
  for (const parsed of parsedFiles) {
8917
9063
  currentFilePaths.add(parsed.path);
8918
9064
  if (parsed.chunks.length === 0) {
8919
- const relativePath = path12.relative(this.projectRoot, parsed.path);
9065
+ const relativePath = path13.relative(this.projectRoot, parsed.path);
8920
9066
  stats.parseFailures.push(relativePath);
8921
9067
  }
8922
9068
  let fileChunkCount = 0;
@@ -9200,7 +9346,7 @@ var Indexer = class {
9200
9346
  for (const requestBatch of requestBatches) {
9201
9347
  queue.add(async () => {
9202
9348
  if (rateLimitBackoffMs > 0) {
9203
- await new Promise((resolve12) => setTimeout(resolve12, rateLimitBackoffMs));
9349
+ await new Promise((resolve13) => setTimeout(resolve13, rateLimitBackoffMs));
9204
9350
  }
9205
9351
  try {
9206
9352
  const result = await pRetry(
@@ -9215,7 +9361,7 @@ var Indexer = class {
9215
9361
  factor: 2,
9216
9362
  shouldRetry: (error) => !(error.error instanceof CustomProviderNonRetryableError),
9217
9363
  onFailedAttempt: (error) => {
9218
- const message = getErrorMessage(error);
9364
+ const message = getErrorMessage2(error);
9219
9365
  if (isRateLimitError(error)) {
9220
9366
  rateLimitBackoffMs = Math.min(providerRateLimits.maxRetryMs, (rateLimitBackoffMs || providerRateLimits.minRetryMs) * 2);
9221
9367
  this.logger.embedding("warn", `Rate limited, backing off`, {
@@ -9322,7 +9468,7 @@ var Indexer = class {
9322
9468
  });
9323
9469
  } catch (error) {
9324
9470
  const failedChunks = getUniquePendingChunksFromRequests(requestBatch).filter((chunk) => !completedChunkIds.has(chunk.id));
9325
- const failureMessage = getErrorMessage(error);
9471
+ const failureMessage = getErrorMessage2(error);
9326
9472
  const failureTimestamp = (/* @__PURE__ */ new Date()).toISOString();
9327
9473
  for (const chunk of failedChunks) {
9328
9474
  if (!failedChunkIds.has(chunk.id)) {
@@ -9761,8 +9907,8 @@ var Indexer = class {
9761
9907
  this.indexCompatibility = compatibility;
9762
9908
  return;
9763
9909
  }
9764
- const localProjectIndexPath = path12.join(this.projectRoot, ".opencode", "index");
9765
- if (path12.resolve(this.indexPath) !== path12.resolve(localProjectIndexPath)) {
9910
+ const localProjectIndexPath = path13.join(this.projectRoot, ".opencode", "index");
9911
+ if (path13.resolve(this.indexPath) !== path13.resolve(localProjectIndexPath)) {
9766
9912
  throw new Error(
9767
9913
  "Project-scoped force rebuild is unsafe while using an inherited worktree index. Create a local project config boundary before clearing the index."
9768
9914
  );
@@ -9850,7 +9996,7 @@ var Indexer = class {
9850
9996
  gcOrphanSymbols: 0,
9851
9997
  gcOrphanCallEdges: 0,
9852
9998
  resetCorruptedIndex: true,
9853
- warning: this.getCorruptedIndexWarning(path12.join(this.indexPath, "codebase.db"))
9999
+ warning: this.getCorruptedIndexWarning(path13.join(this.indexPath, "codebase.db"))
9854
10000
  };
9855
10001
  }
9856
10002
  this.logger.recordGc(removedCount, gcOrphanChunks, gcOrphanEmbeddings);
@@ -10001,7 +10147,7 @@ var Indexer = class {
10001
10147
  succeeded += successfulResults.length;
10002
10148
  stillFailing.push(...failedChunksForBatch.values());
10003
10149
  } catch (error) {
10004
- const failureMessage = getErrorMessage(error);
10150
+ const failureMessage = getErrorMessage2(error);
10005
10151
  const failureTimestamp = (/* @__PURE__ */ new Date()).toISOString();
10006
10152
  const unaccountedChunks = batch.chunks.filter(
10007
10153
  (chunk) => !failedChunksForBatch.has(chunk.id) && !completedChunkIds.has(chunk.id)
@@ -10207,13 +10353,194 @@ var Indexer = class {
10207
10353
  const { database } = await this.ensureInitialized();
10208
10354
  let shortest = [];
10209
10355
  for (const branchKey of this.getBranchCatalogKeys()) {
10210
- const path18 = database.findShortestPath(fromName, toName, branchKey, maxDepth);
10211
- if (path18.length > 0 && (shortest.length === 0 || path18.length < shortest.length)) {
10212
- shortest = path18;
10356
+ const path19 = database.findShortestPath(fromName, toName, branchKey, maxDepth);
10357
+ if (path19.length > 0 && (shortest.length === 0 || path19.length < shortest.length)) {
10358
+ shortest = path19;
10213
10359
  }
10214
10360
  }
10215
10361
  return shortest;
10216
10362
  }
10363
+ async getSymbolsForBranch(branch) {
10364
+ const { database } = await this.ensureInitialized();
10365
+ const resolvedBranch = branch ?? this.getBranchCatalogKey();
10366
+ return database.getSymbolsForBranch(resolvedBranch);
10367
+ }
10368
+ async getSymbolsForFiles(filePaths, branch) {
10369
+ const { database } = await this.ensureInitialized();
10370
+ const resolvedBranch = branch ?? this.getBranchCatalogKey();
10371
+ return database.getSymbolsForFiles(filePaths, resolvedBranch);
10372
+ }
10373
+ async getTransitiveReachability(rootSymbolIds, direction, maxDepth) {
10374
+ const { database } = await this.ensureInitialized();
10375
+ const branch = this.getBranchCatalogKey();
10376
+ return database.getTransitiveReachability(rootSymbolIds, branch, direction, maxDepth);
10377
+ }
10378
+ async detectCommunities(branch, symbolIds) {
10379
+ const { database } = await this.ensureInitialized();
10380
+ const resolvedBranch = branch ?? this.getBranchCatalogKey();
10381
+ return database.detectCommunities(resolvedBranch, symbolIds);
10382
+ }
10383
+ async computeCentrality(branch) {
10384
+ const { database } = await this.ensureInitialized();
10385
+ const resolvedBranch = branch ?? this.getBranchCatalogKey();
10386
+ return database.computeCentrality(resolvedBranch);
10387
+ }
10388
+ async getPrImpact(opts) {
10389
+ const { database } = await this.ensureInitialized();
10390
+ const execFileAsync2 = promisify2(execFile2);
10391
+ const changedFilesResult = await getChangedFiles({
10392
+ pr: opts.pr,
10393
+ branch: opts.branch,
10394
+ projectRoot: this.projectRoot,
10395
+ baseBranch: this.baseBranch
10396
+ });
10397
+ const changedFiles = changedFilesResult.files;
10398
+ const headRefName = changedFilesResult.headRefName;
10399
+ if (opts.pr !== void 0 && headRefName === void 0) {
10400
+ throw new Error(
10401
+ `Could not resolve head branch for PR #${opts.pr}. Run index_codebase on the PR branch first.`
10402
+ );
10403
+ }
10404
+ const resolvedBranch = opts.pr !== void 0 ? headRefName : opts.branch || this.currentBranch;
10405
+ const branchKey = this.getBranchCatalogKeyFor(resolvedBranch || "default");
10406
+ const branchSymbols = database.getSymbolsForBranch(branchKey);
10407
+ if (branchSymbols.length === 0) {
10408
+ throw new Error(
10409
+ "Run index_codebase first to build the call graph and symbol index for this branch."
10410
+ );
10411
+ }
10412
+ const absoluteChangedFiles = changedFiles.map((f) => path13.resolve(this.projectRoot, f));
10413
+ const directSymbols = database.getSymbolsForFiles(absoluteChangedFiles, branchKey);
10414
+ const directIds = directSymbols.map((s) => s.id);
10415
+ const direction = opts.direction ?? "both";
10416
+ const maxDepth = opts.maxDepth ?? 5;
10417
+ const transitiveCallers = database.getTransitiveReachability(
10418
+ directIds,
10419
+ branchKey,
10420
+ direction,
10421
+ maxDepth
10422
+ );
10423
+ const affectedIdsSet = new Set(directIds);
10424
+ for (const caller of transitiveCallers) {
10425
+ affectedIdsSet.add(caller.symbolId);
10426
+ }
10427
+ const allAffectedIds = Array.from(affectedIdsSet);
10428
+ const communitiesData = database.detectCommunities(branchKey, allAffectedIds);
10429
+ const communityMap = /* @__PURE__ */ new Map();
10430
+ for (const c of communitiesData) {
10431
+ if (!communityMap.has(c.communityLabel)) {
10432
+ communityMap.set(c.communityLabel, {
10433
+ label: c.communityLabel,
10434
+ symbolCount: 0,
10435
+ directSymbols: /* @__PURE__ */ new Set()
10436
+ });
10437
+ }
10438
+ const entry = communityMap.get(c.communityLabel);
10439
+ entry.symbolCount++;
10440
+ if (directIds.includes(c.symbolId)) {
10441
+ entry.directSymbols.add(c.symbolId);
10442
+ }
10443
+ }
10444
+ const communities = Array.from(communityMap.values()).map((c) => ({
10445
+ label: c.label,
10446
+ symbolCount: c.symbolCount,
10447
+ directSymbols: Array.from(c.directSymbols)
10448
+ }));
10449
+ const centralityData = database.computeCentrality(branchKey);
10450
+ const hubThreshold = opts.hubThreshold ?? 10;
10451
+ const hubNodes = centralityData.filter((c) => directIds.includes(c.symbolId) && c.callerCount >= hubThreshold).map((c) => ({
10452
+ id: c.symbolId,
10453
+ name: c.symbolName,
10454
+ callerCount: c.callerCount,
10455
+ filePath: c.filePath
10456
+ }));
10457
+ const totalAffected = allAffectedIds.length;
10458
+ let riskLevel;
10459
+ let riskReason;
10460
+ if (totalAffected < 5 && hubNodes.length === 0) {
10461
+ riskLevel = "LOW";
10462
+ riskReason = `Small impact: ${totalAffected} affected symbols, no hub nodes touched.`;
10463
+ } else if (totalAffected > 20 || hubNodes.length > 1) {
10464
+ riskLevel = "HIGH";
10465
+ riskReason = `Large impact: ${totalAffected} affected symbols${hubNodes.length > 0 ? `, ${hubNodes.length} hub nodes touched` : ""}.`;
10466
+ } else {
10467
+ riskLevel = "MEDIUM";
10468
+ riskReason = `Moderate impact: ${totalAffected} affected symbols${hubNodes.length === 1 ? ", 1 hub node touched" : ""}.`;
10469
+ }
10470
+ let conflictingPRs;
10471
+ if (opts.checkConflicts) {
10472
+ conflictingPRs = [];
10473
+ try {
10474
+ const { stdout } = await execFileAsync2(
10475
+ "gh",
10476
+ ["pr", "list", "--state", "open", "--json", "number,headRefName", "--limit", "10000"],
10477
+ { cwd: this.projectRoot, timeout: 3e4 }
10478
+ );
10479
+ const openPRs = JSON.parse(stdout);
10480
+ const currentCommunityLabels = new Set(communities.map((c) => c.label));
10481
+ const allCommunitiesData = database.detectCommunities(branchKey);
10482
+ const symbolToCommunity = /* @__PURE__ */ new Map();
10483
+ const structuralKey = (filePath, name) => `${filePath.toLowerCase()}:${name.toLowerCase()}`;
10484
+ for (const c of allCommunitiesData) {
10485
+ symbolToCommunity.set(structuralKey(c.filePath, c.symbolName), c.communityLabel);
10486
+ }
10487
+ for (const openPr of openPRs) {
10488
+ if (openPr.number === opts.pr) continue;
10489
+ try {
10490
+ const otherChanged = await getChangedFiles({
10491
+ pr: openPr.number,
10492
+ projectRoot: this.projectRoot,
10493
+ baseBranch: this.baseBranch
10494
+ });
10495
+ const otherAbsolute = otherChanged.files.map((f) => path13.resolve(this.projectRoot, f));
10496
+ const otherBranchKey = this.getBranchCatalogKeyFor(openPr.headRefName);
10497
+ const otherSymbols = database.getSymbolsForFiles(otherAbsolute, otherBranchKey);
10498
+ const otherLabels = /* @__PURE__ */ new Set();
10499
+ for (const sym of otherSymbols) {
10500
+ const label = symbolToCommunity.get(structuralKey(sym.filePath, sym.name));
10501
+ if (label) {
10502
+ otherLabels.add(label);
10503
+ }
10504
+ }
10505
+ const overlapping = Array.from(otherLabels).filter(
10506
+ (l) => currentCommunityLabels.has(l)
10507
+ );
10508
+ if (overlapping.length > 0) {
10509
+ conflictingPRs.push({
10510
+ pr: openPr.number,
10511
+ branch: openPr.headRefName,
10512
+ overlappingCommunities: overlapping
10513
+ });
10514
+ }
10515
+ } catch {
10516
+ }
10517
+ }
10518
+ } catch {
10519
+ }
10520
+ }
10521
+ return {
10522
+ changedFiles,
10523
+ directSymbols: directSymbols.map((s) => ({
10524
+ id: s.id,
10525
+ name: s.name,
10526
+ kind: s.kind,
10527
+ filePath: s.filePath
10528
+ })),
10529
+ transitiveCallers: transitiveCallers.map((c) => ({
10530
+ id: c.symbolId,
10531
+ name: c.symbolName,
10532
+ filePath: c.filePath,
10533
+ depth: c.depth
10534
+ })),
10535
+ totalAffected,
10536
+ communities,
10537
+ hubNodes,
10538
+ riskLevel,
10539
+ riskReason,
10540
+ direction,
10541
+ conflictingPRs
10542
+ };
10543
+ }
10217
10544
  async close() {
10218
10545
  await this.database?.close();
10219
10546
  this.database = null;
@@ -10441,54 +10768,139 @@ ${truncateContent(r.content)}
10441
10768
  return formatted.join("\n\n");
10442
10769
  }
10443
10770
 
10771
+ // src/tools/pr-impact.ts
10772
+ import { tool } from "@opencode-ai/plugin";
10773
+
10774
+ // src/tools/format-pr-impact.ts
10775
+ function formatPrImpact(result) {
10776
+ const lines = [];
10777
+ lines.push(`\u2192 Files changed: ${result.changedFiles.length}`);
10778
+ for (const file of result.changedFiles) {
10779
+ lines.push(` - ${file}`);
10780
+ }
10781
+ const directCount = result.directSymbols.length;
10782
+ const transitiveCount = result.transitiveCallers.length;
10783
+ const directionLabel = result.direction === "callees" ? "callees" : result.direction === "both" ? "reachable (callers + callees)" : "callers";
10784
+ lines.push(
10785
+ `\u2192 Symbols affected: ${result.totalAffected} (${directCount} direct, ${transitiveCount} transitive ${directionLabel})`
10786
+ );
10787
+ if (directCount > 0) {
10788
+ lines.push(
10789
+ ` Direct: ${result.directSymbols.map((s) => `${s.name} (${s.kind})`).join(", ")}`
10790
+ );
10791
+ }
10792
+ if (transitiveCount > 0) {
10793
+ lines.push(
10794
+ ` Transitive ${directionLabel}: ${result.transitiveCallers.map((s) => s.name).join(", ")}`
10795
+ );
10796
+ }
10797
+ if (result.communities.length > 0) {
10798
+ lines.push(
10799
+ `\u2192 Communities touched: ${result.communities.map((c) => c.label).join(", ")}`
10800
+ );
10801
+ for (const community of result.communities) {
10802
+ lines.push(
10803
+ ` - ${community.label}: ${community.symbolCount} symbols`
10804
+ );
10805
+ }
10806
+ } else {
10807
+ lines.push("\u2192 Communities touched: none");
10808
+ }
10809
+ lines.push(`\u2192 Risk: ${result.riskLevel} \u2014 ${result.riskReason}`);
10810
+ if (result.hubNodes.length > 0) {
10811
+ lines.push(" Hub nodes in change scope:");
10812
+ for (const hub of result.hubNodes) {
10813
+ lines.push(` - ${hub.name} (${hub.callerCount} callers) at ${hub.filePath}`);
10814
+ }
10815
+ }
10816
+ if (result.conflictingPRs && result.conflictingPRs.length > 0) {
10817
+ const conflictList = result.conflictingPRs.map(
10818
+ (c) => `PR #${c.pr} (also touches ${c.overlappingCommunities.join(", ")} community)`
10819
+ );
10820
+ lines.push(`\u2192 Potential conflicts with: ${conflictList.join(", ")}`);
10821
+ }
10822
+ return lines.join("\n");
10823
+ }
10824
+
10825
+ // src/tools/pr-impact.ts
10826
+ var z = tool.schema;
10827
+ var pr_impact = tool({
10828
+ description: "Analyze the impact of a pull request or branch by examining changed files, affected symbols, transitive callers, communities touched, hub nodes, and risk level. Use to understand blast radius before merging.",
10829
+ args: {
10830
+ pr: z.number().optional().describe("Pull request number to analyze"),
10831
+ branch: z.string().optional().describe("Branch name to analyze (defaults to current branch)"),
10832
+ maxDepth: z.number().optional().default(5).describe("Maximum traversal depth for transitive callers (default: 5)"),
10833
+ hubThreshold: z.number().optional().default(10).describe("Minimum caller count to flag a symbol as a hub node (default: 10)"),
10834
+ checkConflicts: z.boolean().optional().default(false).describe("Check for conflicting open PRs touching the same communities (default: false)"),
10835
+ direction: z.enum(["callers", "callees", "both"]).optional().default("both").describe("Call-graph traversal direction: 'callers' for upstream, 'callees' for downstream, 'both' for union (default: both)")
10836
+ },
10837
+ async execute(args, context) {
10838
+ const indexer = getIndexerForProject(context?.worktree);
10839
+ try {
10840
+ const result = await indexer.getPrImpact({
10841
+ pr: args.pr,
10842
+ branch: args.branch,
10843
+ maxDepth: args.maxDepth,
10844
+ hubThreshold: args.hubThreshold,
10845
+ checkConflicts: args.checkConflicts,
10846
+ direction: args.direction
10847
+ });
10848
+ return formatPrImpact(result);
10849
+ } catch (error) {
10850
+ const message = error instanceof Error ? error.message : String(error);
10851
+ return `Error analyzing PR impact: ${message}`;
10852
+ }
10853
+ }
10854
+ });
10855
+
10444
10856
  // src/tools/knowledge-base-paths.ts
10445
- import * as path13 from "path";
10857
+ import * as path14 from "path";
10446
10858
  function resolveConfigPathValue(value, baseDir) {
10447
10859
  const trimmed = value.trim();
10448
10860
  if (!trimmed) {
10449
10861
  return trimmed;
10450
10862
  }
10451
- const absolutePath = path13.isAbsolute(trimmed) ? trimmed : path13.resolve(baseDir, trimmed);
10452
- return path13.normalize(absolutePath);
10863
+ const absolutePath = path14.isAbsolute(trimmed) ? trimmed : path14.resolve(baseDir, trimmed);
10864
+ return path14.normalize(absolutePath);
10453
10865
  }
10454
10866
  function serializeConfigPathValue(value, baseDir) {
10455
10867
  const trimmed = value.trim();
10456
10868
  if (!trimmed) {
10457
10869
  return trimmed;
10458
10870
  }
10459
- if (!path13.isAbsolute(trimmed)) {
10460
- return normalizePathSeparators(path13.normalize(trimmed));
10871
+ if (!path14.isAbsolute(trimmed)) {
10872
+ return normalizePathSeparators(path14.normalize(trimmed));
10461
10873
  }
10462
- const relativePath = path13.relative(baseDir, trimmed);
10463
- if (!relativePath || !relativePath.startsWith("..") && !path13.isAbsolute(relativePath)) {
10464
- return normalizePathSeparators(path13.normalize(relativePath || "."));
10874
+ const relativePath = path14.relative(baseDir, trimmed);
10875
+ if (!relativePath || !relativePath.startsWith("..") && !path14.isAbsolute(relativePath)) {
10876
+ return normalizePathSeparators(path14.normalize(relativePath || "."));
10465
10877
  }
10466
- return path13.normalize(trimmed);
10878
+ return path14.normalize(trimmed);
10467
10879
  }
10468
10880
  function resolveKnowledgeBasePath(value, projectRoot) {
10469
- return path13.isAbsolute(value) ? value : path13.resolve(projectRoot, value);
10881
+ return path14.isAbsolute(value) ? value : path14.resolve(projectRoot, value);
10470
10882
  }
10471
10883
  function normalizeKnowledgeBasePath2(value, projectRoot) {
10472
- return path13.normalize(resolveKnowledgeBasePath(value, projectRoot));
10884
+ return path14.normalize(resolveKnowledgeBasePath(value, projectRoot));
10473
10885
  }
10474
10886
  function hasMatchingKnowledgeBasePath(knowledgeBases, inputPath, projectRoot) {
10475
- const normalizedInput = path13.normalize(inputPath);
10887
+ const normalizedInput = path14.normalize(inputPath);
10476
10888
  return knowledgeBases.some((kb) => normalizeKnowledgeBasePath2(kb, projectRoot) === normalizedInput);
10477
10889
  }
10478
10890
  function findKnowledgeBasePathIndex(knowledgeBases, inputPath, projectRoot) {
10479
- const normalizedInput = path13.normalize(inputPath);
10891
+ const normalizedInput = path14.normalize(inputPath);
10480
10892
  return knowledgeBases.findIndex(
10481
- (kb) => path13.normalize(kb) === normalizedInput || normalizeKnowledgeBasePath2(kb, projectRoot) === normalizedInput
10893
+ (kb) => path14.normalize(kb) === normalizedInput || normalizeKnowledgeBasePath2(kb, projectRoot) === normalizedInput
10482
10894
  );
10483
10895
  }
10484
10896
 
10485
10897
  // src/tools/index.ts
10486
10898
  import { existsSync as existsSync9, realpathSync, statSync as statSync3 } from "fs";
10487
- import * as path15 from "path";
10899
+ import * as path16 from "path";
10488
10900
 
10489
10901
  // src/tools/config-state.ts
10490
10902
  import { existsSync as existsSync8, mkdirSync as mkdirSync3, writeFileSync as writeFileSync3 } from "fs";
10491
- import * as path14 from "path";
10903
+ import * as path15 from "path";
10492
10904
  function normalizeKnowledgeBasePaths(config, projectRoot) {
10493
10905
  const normalized = { ...config };
10494
10906
  if (Array.isArray(normalized.knowledgeBases)) {
@@ -10515,8 +10927,8 @@ function loadEditableConfig(projectRoot) {
10515
10927
  }
10516
10928
  function saveConfig(projectRoot, config) {
10517
10929
  const configPath = getConfigPath(projectRoot);
10518
- const configDir = path14.dirname(configPath);
10519
- const configBaseDir = path14.dirname(configDir);
10930
+ const configDir = path15.dirname(configPath);
10931
+ const configBaseDir = path15.dirname(configDir);
10520
10932
  if (!existsSync8(configDir)) {
10521
10933
  mkdirSync3(configDir, { recursive: true });
10522
10934
  }
@@ -10534,7 +10946,7 @@ import * as os5 from "os";
10534
10946
  function ensureStringArray(value) {
10535
10947
  return Array.isArray(value) ? value : [];
10536
10948
  }
10537
- var z = tool.schema;
10949
+ var z2 = tool2.schema;
10538
10950
  var indexerMap = /* @__PURE__ */ new Map();
10539
10951
  var defaultProjectRoot = "";
10540
10952
  function initializeTools(projectRoot, config) {
@@ -10554,12 +10966,12 @@ function shouldForceLocalizeProjectIndex(projectRoot) {
10554
10966
  if (currentConfig.scope !== "project") {
10555
10967
  return false;
10556
10968
  }
10557
- const localIndexPath = path15.join(projectRoot, ".opencode", "index");
10969
+ const localIndexPath = path16.join(projectRoot, ".opencode", "index");
10558
10970
  const mainRepoRoot = resolveWorktreeMainRepoRoot(projectRoot);
10559
10971
  if (!mainRepoRoot) {
10560
10972
  return false;
10561
10973
  }
10562
- const inheritedIndexPath = path15.join(mainRepoRoot, ".opencode", "index");
10974
+ const inheritedIndexPath = path16.join(mainRepoRoot, ".opencode", "index");
10563
10975
  return !existsSync9(localIndexPath) && existsSync9(inheritedIndexPath);
10564
10976
  }
10565
10977
  function getIndexerForProject(directory) {
@@ -10575,14 +10987,14 @@ function getIndexerForProject(directory) {
10575
10987
  }
10576
10988
  return indexer;
10577
10989
  }
10578
- var codebase_peek = tool({
10990
+ var codebase_peek = tool2({
10579
10991
  description: "Quick lookup of code locations by meaning. Returns only metadata (file, line, name, type) WITHOUT code content. Use this first to find WHERE code is, then use Read tool to examine specific files. Saves tokens by not returning full code blocks. Best for: discovery, navigation, finding multiple related locations.",
10580
10992
  args: {
10581
- query: z.string().describe("Natural language description of what code you're looking for."),
10582
- limit: z.number().optional().default(10).describe("Maximum number of results to return"),
10583
- fileType: z.string().optional().describe("Filter by file extension (e.g., 'ts', 'py', 'rs')"),
10584
- directory: z.string().optional().describe("Filter by directory path (e.g., 'src/utils', 'lib')"),
10585
- chunkType: z.enum(["function", "class", "method", "interface", "type", "enum", "struct", "impl", "trait", "module", "other"]).optional().describe("Filter by code chunk type")
10993
+ query: z2.string().describe("Natural language description of what code you're looking for."),
10994
+ limit: z2.number().optional().default(10).describe("Maximum number of results to return"),
10995
+ fileType: z2.string().optional().describe("Filter by file extension (e.g., 'ts', 'py', 'rs')"),
10996
+ directory: z2.string().optional().describe("Filter by directory path (e.g., 'src/utils', 'lib')"),
10997
+ chunkType: z2.enum(["function", "class", "method", "interface", "type", "enum", "struct", "impl", "trait", "module", "other"]).optional().describe("Filter by code chunk type")
10586
10998
  },
10587
10999
  async execute(args, context) {
10588
11000
  const indexer = getIndexerForProject(context?.worktree);
@@ -10595,12 +11007,12 @@ var codebase_peek = tool({
10595
11007
  return formatCodebasePeek(results);
10596
11008
  }
10597
11009
  });
10598
- var index_codebase = tool({
11010
+ var index_codebase = tool2({
10599
11011
  description: "Index the codebase for semantic search. Creates vector embeddings of code chunks. Incremental - only re-indexes changed files (~50ms when nothing changed). Run before first codebase_search.",
10600
11012
  args: {
10601
- force: z.boolean().optional().default(false).describe("Force reindex even if already indexed"),
10602
- estimateOnly: z.boolean().optional().default(false).describe("Only show cost estimate without indexing"),
10603
- verbose: z.boolean().optional().default(false).describe("Show detailed info about skipped files and parsing failures")
11013
+ force: z2.boolean().optional().default(false).describe("Force reindex even if already indexed"),
11014
+ estimateOnly: z2.boolean().optional().default(false).describe("Only show cost estimate without indexing"),
11015
+ verbose: z2.boolean().optional().default(false).describe("Show detailed info about skipped files and parsing failures")
10604
11016
  },
10605
11017
  async execute(args, context) {
10606
11018
  const projectRoot = context?.worktree || defaultProjectRoot;
@@ -10633,7 +11045,7 @@ var index_codebase = tool({
10633
11045
  return formatIndexStats(stats, args.verbose ?? false);
10634
11046
  }
10635
11047
  });
10636
- var index_status = tool({
11048
+ var index_status = tool2({
10637
11049
  description: "Check the status of the codebase index. Shows whether the codebase is indexed, how many chunks are stored, and the embedding provider being used.",
10638
11050
  args: {},
10639
11051
  async execute(_args, context) {
@@ -10642,7 +11054,7 @@ var index_status = tool({
10642
11054
  return formatStatus(status);
10643
11055
  }
10644
11056
  });
10645
- var index_health_check = tool({
11057
+ var index_health_check = tool2({
10646
11058
  description: "Check index health and remove stale entries from deleted files. Run this to clean up the index after files have been deleted.",
10647
11059
  args: {},
10648
11060
  async execute(_args, context) {
@@ -10651,7 +11063,7 @@ var index_health_check = tool({
10651
11063
  return formatHealthCheck(result);
10652
11064
  }
10653
11065
  });
10654
- var index_metrics = tool({
11066
+ var index_metrics = tool2({
10655
11067
  description: "Get metrics and performance statistics for the codebase index. Shows indexing stats, search timings, cache hit rates, and API usage. Requires debug.enabled=true and debug.metrics=true in config.",
10656
11068
  args: {},
10657
11069
  async execute(_args, context) {
@@ -10666,12 +11078,12 @@ var index_metrics = tool({
10666
11078
  return logger.formatMetrics();
10667
11079
  }
10668
11080
  });
10669
- var index_logs = tool({
11081
+ var index_logs = tool2({
10670
11082
  description: "Get recent debug logs from the codebase indexer. Shows timestamped log entries with level and category. Requires debug.enabled=true in config.",
10671
11083
  args: {
10672
- limit: z.number().optional().default(20).describe("Maximum number of log entries to return"),
10673
- category: z.enum(["search", "embedding", "cache", "gc", "branch", "general"]).optional().describe("Filter by log category"),
10674
- level: z.enum(["error", "warn", "info", "debug"]).optional().describe("Filter by minimum log level")
11084
+ limit: z2.number().optional().default(20).describe("Maximum number of log entries to return"),
11085
+ category: z2.enum(["search", "embedding", "cache", "gc", "branch", "general"]).optional().describe("Filter by log category"),
11086
+ level: z2.enum(["error", "warn", "info", "debug"]).optional().describe("Filter by minimum log level")
10675
11087
  },
10676
11088
  async execute(args, context) {
10677
11089
  const indexer = getIndexerForProject(context?.worktree);
@@ -10690,15 +11102,15 @@ var index_logs = tool({
10690
11102
  return formatLogs(logs);
10691
11103
  }
10692
11104
  });
10693
- var find_similar = tool({
11105
+ var find_similar = tool2({
10694
11106
  description: "Find code similar to a given snippet. Use for duplicate detection, pattern discovery, or refactoring prep. Paste code and find semantically similar implementations elsewhere in the codebase.",
10695
11107
  args: {
10696
- code: z.string().describe("The code snippet to find similar code for"),
10697
- limit: z.number().optional().default(10).describe("Maximum number of results to return"),
10698
- fileType: z.string().optional().describe("Filter by file extension (e.g., 'ts', 'py', 'rs')"),
10699
- directory: z.string().optional().describe("Filter by directory path (e.g., 'src/utils', 'lib')"),
10700
- chunkType: z.enum(["function", "class", "method", "interface", "type", "enum", "struct", "impl", "trait", "module", "other"]).optional().describe("Filter by code chunk type"),
10701
- excludeFile: z.string().optional().describe("Exclude results from this file path (useful when searching for duplicates of code from a specific file)")
11108
+ code: z2.string().describe("The code snippet to find similar code for"),
11109
+ limit: z2.number().optional().default(10).describe("Maximum number of results to return"),
11110
+ fileType: z2.string().optional().describe("Filter by file extension (e.g., 'ts', 'py', 'rs')"),
11111
+ directory: z2.string().optional().describe("Filter by directory path (e.g., 'src/utils', 'lib')"),
11112
+ chunkType: z2.enum(["function", "class", "method", "interface", "type", "enum", "struct", "impl", "trait", "module", "other"]).optional().describe("Filter by code chunk type"),
11113
+ excludeFile: z2.string().optional().describe("Exclude results from this file path (useful when searching for duplicates of code from a specific file)")
10702
11114
  },
10703
11115
  async execute(args, context) {
10704
11116
  const indexer = getIndexerForProject(context?.worktree);
@@ -10714,15 +11126,15 @@ var find_similar = tool({
10714
11126
  return formatSearchResults(results);
10715
11127
  }
10716
11128
  });
10717
- var codebase_search = tool({
11129
+ var codebase_search = tool2({
10718
11130
  description: "Search codebase by MEANING, not keywords. Returns full code content. Use when you need to see actual implementation. For just finding WHERE code is (saves ~90% tokens), use codebase_peek instead. For known identifiers like 'validateToken', use grep - it's faster.",
10719
11131
  args: {
10720
- query: z.string().describe("Natural language description of what code you're looking for. Describe behavior, not syntax."),
10721
- limit: z.number().optional().default(5).describe("Maximum number of results to return"),
10722
- fileType: z.string().optional().describe("Filter by file extension (e.g., 'ts', 'py', 'rs')"),
10723
- directory: z.string().optional().describe("Filter by directory path (e.g., 'src/utils', 'lib')"),
10724
- chunkType: z.enum(["function", "class", "method", "interface", "type", "enum", "struct", "impl", "trait", "module", "other"]).optional().describe("Filter by code chunk type"),
10725
- contextLines: z.number().optional().describe("Number of extra lines to include before/after each match (default: 0)")
11132
+ query: z2.string().describe("Natural language description of what code you're looking for. Describe behavior, not syntax."),
11133
+ limit: z2.number().optional().default(5).describe("Maximum number of results to return"),
11134
+ fileType: z2.string().optional().describe("Filter by file extension (e.g., 'ts', 'py', 'rs')"),
11135
+ directory: z2.string().optional().describe("Filter by directory path (e.g., 'src/utils', 'lib')"),
11136
+ chunkType: z2.enum(["function", "class", "method", "interface", "type", "enum", "struct", "impl", "trait", "module", "other"]).optional().describe("Filter by code chunk type"),
11137
+ contextLines: z2.number().optional().describe("Number of extra lines to include before/after each match (default: 0)")
10726
11138
  },
10727
11139
  async execute(args, context) {
10728
11140
  const indexer = getIndexerForProject(context?.worktree);
@@ -10738,13 +11150,13 @@ var codebase_search = tool({
10738
11150
  return formatSearchResults(results, "score");
10739
11151
  }
10740
11152
  });
10741
- var implementation_lookup = tool({
11153
+ var implementation_lookup = tool2({
10742
11154
  description: "Jump to symbol definition. Find WHERE something is defined. Returns the authoritative source location(s) for a function, class, method, type, or variable. Prefers real implementation files over tests, docs, examples, and fixtures. Use when you need the definition site, not all usages.",
10743
11155
  args: {
10744
- query: z.string().describe("Symbol name or natural language description (e.g., 'validateToken', 'where is the payment handler defined')"),
10745
- limit: z.number().optional().default(5).describe("Maximum number of results"),
10746
- fileType: z.string().optional().describe("Filter by file extension (e.g., 'ts', 'py')"),
10747
- directory: z.string().optional().describe("Filter by directory path (e.g., 'src/utils')")
11156
+ query: z2.string().describe("Symbol name or natural language description (e.g., 'validateToken', 'where is the payment handler defined')"),
11157
+ limit: z2.number().optional().default(5).describe("Maximum number of results"),
11158
+ fileType: z2.string().optional().describe("Filter by file extension (e.g., 'ts', 'py')"),
11159
+ directory: z2.string().optional().describe("Filter by directory path (e.g., 'src/utils')")
10748
11160
  },
10749
11161
  async execute(args, context) {
10750
11162
  const indexer = getIndexerForProject(context?.worktree);
@@ -10756,13 +11168,13 @@ var implementation_lookup = tool({
10756
11168
  return formatDefinitionLookup(results, args.query);
10757
11169
  }
10758
11170
  });
10759
- var call_graph = tool({
11171
+ var call_graph = tool2({
10760
11172
  description: "Query the call graph to find callers or callees of a function/method. Use to understand code flow and dependencies between functions. Supports relationship types: Call, MethodCall, Constructor, Import, Inherits, Implements.",
10761
11173
  args: {
10762
- name: z.string().describe("Function or method name to query"),
10763
- direction: z.enum(["callers", "callees"]).default("callers").describe("Direction: 'callers' finds who calls this function, 'callees' finds what this function calls"),
10764
- symbolId: z.string().optional().describe("Symbol ID (required for 'callees' direction, returned by previous call_graph queries)"),
10765
- relationshipType: z.enum(["Call", "MethodCall", "Constructor", "Import", "Inherits", "Implements"]).optional().describe("Filter by relationship type. Omit to show all.")
11174
+ name: z2.string().describe("Function or method name to query"),
11175
+ direction: z2.enum(["callers", "callees"]).default("callers").describe("Direction: 'callers' finds who calls this function, 'callees' finds what this function calls"),
11176
+ symbolId: z2.string().optional().describe("Symbol ID (required for 'callees' direction, returned by previous call_graph queries)"),
11177
+ relationshipType: z2.enum(["Call", "MethodCall", "Constructor", "Import", "Inherits", "Implements"]).optional().describe("Filter by relationship type. Omit to show all.")
10766
11178
  },
10767
11179
  async execute(args, context) {
10768
11180
  const indexer = getIndexerForProject(context?.worktree);
@@ -10791,38 +11203,38 @@ var call_graph = tool({
10791
11203
  return formatted.join("\n");
10792
11204
  }
10793
11205
  });
10794
- var call_graph_path = tool({
11206
+ var call_graph_path = tool2({
10795
11207
  description: "Find the shortest connection path between two symbols in the call graph. Given a source and target function/method name, returns the chain of calls connecting them.",
10796
11208
  args: {
10797
- from: z.string().describe("Source function/method name (starting point)"),
10798
- to: z.string().describe("Target function/method name (destination)"),
10799
- maxDepth: z.number().optional().default(10).describe("Maximum traversal depth (default: 10)")
11209
+ from: z2.string().describe("Source function/method name (starting point)"),
11210
+ to: z2.string().describe("Target function/method name (destination)"),
11211
+ maxDepth: z2.number().optional().default(10).describe("Maximum traversal depth (default: 10)")
10800
11212
  },
10801
11213
  async execute(args, context) {
10802
11214
  const indexer = getIndexerForProject(context?.worktree);
10803
- const path18 = await indexer.findCallPath(args.from, args.to, args.maxDepth);
10804
- if (path18.length === 0) {
11215
+ const path19 = await indexer.findCallPath(args.from, args.to, args.maxDepth);
11216
+ if (path19.length === 0) {
10805
11217
  return `No path found between "${args.from}" and "${args.to}". They may be in disconnected components, or the call graph index needs updating.`;
10806
11218
  }
10807
- const formatted = path18.map((hop, i) => {
11219
+ const formatted = path19.map((hop, i) => {
10808
11220
  const prefix = i === 0 ? "[start]" : `--${hop.callType}-->`;
10809
11221
  const location = hop.filePath ? ` (${hop.filePath}:${hop.line})` : "";
10810
11222
  return `${prefix} ${hop.symbolName}${location}`;
10811
11223
  });
10812
- return `Path (${path18.length} hops):
11224
+ return `Path (${path19.length} hops):
10813
11225
  ${formatted.join("\n")}`;
10814
11226
  }
10815
11227
  });
10816
- var add_knowledge_base = tool({
11228
+ var add_knowledge_base = tool2({
10817
11229
  description: "Add a folder as a knowledge base to the semantic search index. The folder will be indexed alongside the main project code. Supports absolute paths or relative paths (relative to the project root).",
10818
11230
  args: {
10819
- path: z.string().describe("Path to the folder to add as a knowledge base (absolute or relative to project root)")
11231
+ path: z2.string().describe("Path to the folder to add as a knowledge base (absolute or relative to project root)")
10820
11232
  },
10821
11233
  async execute(args, context) {
10822
11234
  const projectRoot = context?.worktree || defaultProjectRoot;
10823
11235
  const inputPath = args.path.trim();
10824
- const normalizedPath = path15.resolve(
10825
- path15.isAbsolute(inputPath) ? inputPath : resolveKnowledgeBasePath(inputPath, projectRoot)
11236
+ const normalizedPath = path16.resolve(
11237
+ path16.isAbsolute(inputPath) ? inputPath : resolveKnowledgeBasePath(inputPath, projectRoot)
10826
11238
  );
10827
11239
  if (!existsSync9(normalizedPath)) {
10828
11240
  return `Error: Directory does not exist: ${normalizedPath}`;
@@ -10851,7 +11263,7 @@ var add_knowledge_base = tool({
10851
11263
  }
10852
11264
  }
10853
11265
  for (const dotDir of sensitiveDotDirs) {
10854
- const sensitiveDir = path15.join(homeDir, dotDir);
11266
+ const sensitiveDir = path16.join(homeDir, dotDir);
10855
11267
  if (realPath === sensitiveDir || realPath.startsWith(sensitiveDir + "/")) {
10856
11268
  return `Error: Adding sensitive directory as knowledge base is not allowed: ${normalizedPath}`;
10857
11269
  }
@@ -10885,7 +11297,7 @@ Run /index to rebuild the index with the new knowledge base.`;
10885
11297
  return result;
10886
11298
  }
10887
11299
  });
10888
- var list_knowledge_bases = tool({
11300
+ var list_knowledge_bases = tool2({
10889
11301
  description: "List all configured knowledge base folders that are indexed alongside the main project.",
10890
11302
  args: {},
10891
11303
  async execute(_args, context) {
@@ -10922,10 +11334,10 @@ var list_knowledge_bases = tool({
10922
11334
  return result;
10923
11335
  }
10924
11336
  });
10925
- var remove_knowledge_base = tool({
11337
+ var remove_knowledge_base = tool2({
10926
11338
  description: "Remove a knowledge base folder from the semantic search index.",
10927
11339
  args: {
10928
- path: z.string().describe("Path of the knowledge base to remove (must match the configured path exactly)")
11340
+ path: z2.string().describe("Path of the knowledge base to remove (must match the configured path exactly)")
10929
11341
  },
10930
11342
  async execute(args, context) {
10931
11343
  const projectRoot = context?.worktree || defaultProjectRoot;
@@ -10967,7 +11379,7 @@ Run /index to rebuild the index without the removed knowledge base.`;
10967
11379
 
10968
11380
  // src/commands/loader.ts
10969
11381
  import { existsSync as existsSync10, readdirSync as readdirSync2, readFileSync as readFileSync7 } from "fs";
10970
- import * as path16 from "path";
11382
+ import * as path17 from "path";
10971
11383
  function parseFrontmatter(content) {
10972
11384
  const frontmatterRegex = /^---\s*\n([\s\S]*?)\n---\s*\n([\s\S]*)$/;
10973
11385
  const match = content.match(frontmatterRegex);
@@ -10993,7 +11405,7 @@ function loadCommandsFromDirectory(commandsDir) {
10993
11405
  }
10994
11406
  const files = readdirSync2(commandsDir).filter((f) => f.endsWith(".md"));
10995
11407
  for (const file of files) {
10996
- const filePath = path16.join(commandsDir, file);
11408
+ const filePath = path17.join(commandsDir, file);
10997
11409
  let content;
10998
11410
  try {
10999
11411
  content = readFileSync7(filePath, "utf-8");
@@ -11002,7 +11414,7 @@ function loadCommandsFromDirectory(commandsDir) {
11002
11414
  throw new Error(`Failed to load command file ${filePath}: ${message}`);
11003
11415
  }
11004
11416
  const { frontmatter, body } = parseFrontmatter(content);
11005
- const name = path16.basename(file, ".md");
11417
+ const name = path17.basename(file, ".md");
11006
11418
  const description = frontmatter.description || `Run the ${name} command`;
11007
11419
  commands.set(name, {
11008
11420
  description,
@@ -11224,7 +11636,7 @@ function assessRoutingIntent(text) {
11224
11636
  reason: "no_local_discovery_signal"
11225
11637
  };
11226
11638
  }
11227
- function buildRoutingHint(assessment, status) {
11639
+ function buildRoutingHint(assessment, status, includeGraphHandoff = false) {
11228
11640
  if (assessment.intent === "definition_lookup") {
11229
11641
  if (!status || !status.indexed || status.compatibility?.compatible === false) {
11230
11642
  return "For this turn, if you need a symbol definition, check `index_status` first and run `index_codebase` if the index is missing or incompatible. Then use `implementation_lookup` for the definition site. Use `grep` for exhaustive literal matches.";
@@ -11235,17 +11647,21 @@ function buildRoutingHint(assessment, status) {
11235
11647
  return null;
11236
11648
  }
11237
11649
  if (!status || !status.indexed || status.compatibility?.compatible === false) {
11238
- return "For this turn, if local code discovery by behavior is needed, check `index_status` first and run `index_codebase` if the index is missing or incompatible. Use `grep` for exact identifiers or exhaustive matches.";
11650
+ const graphHandoff2 = includeGraphHandoff ? " Use graph tools after semantic discovery identifies relevant symbols." : "";
11651
+ return `For this turn, if local code discovery by behavior is needed, check \`index_status\` first and run \`index_codebase\` if the index is missing or incompatible.${graphHandoff2} Use \`grep\` for exact identifiers or exhaustive matches.`;
11239
11652
  }
11240
- return "For this turn, prefer `codebase_peek` for local code discovery by behavior or likely location, then use `codebase_search` when you need implementation content. Use `grep` for exact identifiers or exhaustive matches.";
11653
+ const graphHandoff = includeGraphHandoff ? " before graph tools such as `call_graph`, `call_graph_path`, `pr_impact`, or OMO CodeGraph" : "";
11654
+ return `For this turn, prefer \`codebase_peek\` for local code discovery by behavior or likely location${graphHandoff}. Then use \`codebase_search\` when you need implementation content. Use \`grep\` for exact identifiers or exhaustive matches.`;
11241
11655
  }
11242
11656
  var RoutingHintController = class {
11243
- constructor(getStatus, maxSessions = 200) {
11657
+ constructor(getStatus, maxSessions = 200, includeGraphHandoff = false) {
11244
11658
  this.getStatus = getStatus;
11245
11659
  this.maxSessions = maxSessions;
11660
+ this.includeGraphHandoff = includeGraphHandoff;
11246
11661
  }
11247
11662
  getStatus;
11248
11663
  maxSessions;
11664
+ includeGraphHandoff;
11249
11665
  sessionState = /* @__PURE__ */ new Map();
11250
11666
  observeUserMessage(sessionID, parts) {
11251
11667
  const assessment = assessRoutingIntent(extractUserText(parts));
@@ -11266,7 +11682,7 @@ var RoutingHintController = class {
11266
11682
  return [];
11267
11683
  }
11268
11684
  const status = await this.safeGetStatus();
11269
- const hint = buildRoutingHint(state.assessment, status);
11685
+ const hint = buildRoutingHint(state.assessment, status, this.includeGraphHandoff);
11270
11686
  return hint ? [hint] : [];
11271
11687
  }
11272
11688
  markToolUsed(sessionID, toolName) {
@@ -11316,9 +11732,9 @@ function replaceActiveWatcher(projectRoot, nextWatcher) {
11316
11732
  function getCommandsDir() {
11317
11733
  let currentDir = process.cwd();
11318
11734
  if (typeof import.meta !== "undefined" && import.meta.url) {
11319
- currentDir = path17.dirname(fileURLToPath2(import.meta.url));
11735
+ currentDir = path18.dirname(fileURLToPath2(import.meta.url));
11320
11736
  }
11321
- return path17.join(currentDir, "..", "commands");
11737
+ return path18.join(currentDir, "..", "commands");
11322
11738
  }
11323
11739
  function appendRoutingHints(output, hints, preferredRole) {
11324
11740
  const preferredBucket = preferredRole === "developer" ? output.developer : output.system;
@@ -11337,8 +11753,8 @@ var plugin = async ({ directory, worktree }) => {
11337
11753
  const config = parseConfig(rawConfig);
11338
11754
  initializeTools(projectRoot, config);
11339
11755
  const getProjectIndexer = () => getIndexerForProject(projectRoot);
11340
- const routingHints = config.search.routingHints ? new RoutingHintController(() => getProjectIndexer().getStatus()) : null;
11341
- const isHomeDir = path17.resolve(projectRoot) === path17.resolve(os6.homedir());
11756
+ const routingHints = config.search.routingHints ? new RoutingHintController(() => getProjectIndexer().getStatus(), 200, config.search.routingGraphHandoffHints) : null;
11757
+ const isHomeDir = path18.resolve(projectRoot) === path18.resolve(os6.homedir());
11342
11758
  const isValidProject = !isHomeDir && (!config.indexing.requireProjectMarker || hasProjectMarker(projectRoot));
11343
11759
  if (isHomeDir) {
11344
11760
  console.warn(
@@ -11377,7 +11793,8 @@ var plugin = async ({ directory, worktree }) => {
11377
11793
  implementation_lookup,
11378
11794
  add_knowledge_base,
11379
11795
  list_knowledge_bases,
11380
- remove_knowledge_base
11796
+ remove_knowledge_base,
11797
+ pr_impact
11381
11798
  },
11382
11799
  async "chat.message"(input, output) {
11383
11800
  routingHints?.observeUserMessage(input.sessionID, output.parts);