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.cjs CHANGED
@@ -333,7 +333,7 @@ var require_ignore = __commonJS({
333
333
  // path matching.
334
334
  // - check `string` either `MODE_IGNORE` or `MODE_CHECK_IGNORE`
335
335
  // @returns {TestResult} true if a file is ignored
336
- test(path18, checkUnignored, mode) {
336
+ test(path19, checkUnignored, mode) {
337
337
  let ignored = false;
338
338
  let unignored = false;
339
339
  let matchedRule;
@@ -342,7 +342,7 @@ var require_ignore = __commonJS({
342
342
  if (unignored === negative && ignored !== unignored || negative && !ignored && !unignored && !checkUnignored) {
343
343
  return;
344
344
  }
345
- const matched = rule[mode].test(path18);
345
+ const matched = rule[mode].test(path19);
346
346
  if (!matched) {
347
347
  return;
348
348
  }
@@ -363,17 +363,17 @@ var require_ignore = __commonJS({
363
363
  var throwError = (message, Ctor) => {
364
364
  throw new Ctor(message);
365
365
  };
366
- var checkPath = (path18, originalPath, doThrow) => {
367
- if (!isString(path18)) {
366
+ var checkPath = (path19, originalPath, doThrow) => {
367
+ if (!isString(path19)) {
368
368
  return doThrow(
369
369
  `path must be a string, but got \`${originalPath}\``,
370
370
  TypeError
371
371
  );
372
372
  }
373
- if (!path18) {
373
+ if (!path19) {
374
374
  return doThrow(`path must not be empty`, TypeError);
375
375
  }
376
- if (checkPath.isNotRelative(path18)) {
376
+ if (checkPath.isNotRelative(path19)) {
377
377
  const r = "`path.relative()`d";
378
378
  return doThrow(
379
379
  `path should be a ${r} string, but got "${originalPath}"`,
@@ -382,7 +382,7 @@ var require_ignore = __commonJS({
382
382
  }
383
383
  return true;
384
384
  };
385
- var isNotRelative = (path18) => REGEX_TEST_INVALID_PATH.test(path18);
385
+ var isNotRelative = (path19) => REGEX_TEST_INVALID_PATH.test(path19);
386
386
  checkPath.isNotRelative = isNotRelative;
387
387
  checkPath.convert = (p) => p;
388
388
  var Ignore2 = class {
@@ -412,19 +412,19 @@ var require_ignore = __commonJS({
412
412
  }
413
413
  // @returns {TestResult}
414
414
  _test(originalPath, cache, checkUnignored, slices) {
415
- const path18 = originalPath && checkPath.convert(originalPath);
415
+ const path19 = originalPath && checkPath.convert(originalPath);
416
416
  checkPath(
417
- path18,
417
+ path19,
418
418
  originalPath,
419
419
  this._strictPathCheck ? throwError : RETURN_FALSE
420
420
  );
421
- return this._t(path18, cache, checkUnignored, slices);
421
+ return this._t(path19, cache, checkUnignored, slices);
422
422
  }
423
- checkIgnore(path18) {
424
- if (!REGEX_TEST_TRAILING_SLASH.test(path18)) {
425
- return this.test(path18);
423
+ checkIgnore(path19) {
424
+ if (!REGEX_TEST_TRAILING_SLASH.test(path19)) {
425
+ return this.test(path19);
426
426
  }
427
- const slices = path18.split(SLASH2).filter(Boolean);
427
+ const slices = path19.split(SLASH2).filter(Boolean);
428
428
  slices.pop();
429
429
  if (slices.length) {
430
430
  const parent = this._t(
@@ -437,18 +437,18 @@ var require_ignore = __commonJS({
437
437
  return parent;
438
438
  }
439
439
  }
440
- return this._rules.test(path18, false, MODE_CHECK_IGNORE);
440
+ return this._rules.test(path19, false, MODE_CHECK_IGNORE);
441
441
  }
442
- _t(path18, cache, checkUnignored, slices) {
443
- if (path18 in cache) {
444
- return cache[path18];
442
+ _t(path19, cache, checkUnignored, slices) {
443
+ if (path19 in cache) {
444
+ return cache[path19];
445
445
  }
446
446
  if (!slices) {
447
- slices = path18.split(SLASH2).filter(Boolean);
447
+ slices = path19.split(SLASH2).filter(Boolean);
448
448
  }
449
449
  slices.pop();
450
450
  if (!slices.length) {
451
- return cache[path18] = this._rules.test(path18, checkUnignored, MODE_IGNORE);
451
+ return cache[path19] = this._rules.test(path19, checkUnignored, MODE_IGNORE);
452
452
  }
453
453
  const parent = this._t(
454
454
  slices.join(SLASH2) + SLASH2,
@@ -456,29 +456,29 @@ var require_ignore = __commonJS({
456
456
  checkUnignored,
457
457
  slices
458
458
  );
459
- return cache[path18] = parent.ignored ? parent : this._rules.test(path18, checkUnignored, MODE_IGNORE);
459
+ return cache[path19] = parent.ignored ? parent : this._rules.test(path19, checkUnignored, MODE_IGNORE);
460
460
  }
461
- ignores(path18) {
462
- return this._test(path18, this._ignoreCache, false).ignored;
461
+ ignores(path19) {
462
+ return this._test(path19, this._ignoreCache, false).ignored;
463
463
  }
464
464
  createFilter() {
465
- return (path18) => !this.ignores(path18);
465
+ return (path19) => !this.ignores(path19);
466
466
  }
467
467
  filter(paths) {
468
468
  return makeArray(paths).filter(this.createFilter());
469
469
  }
470
470
  // @returns {TestResult}
471
- test(path18) {
472
- return this._test(path18, this._testCache, true);
471
+ test(path19) {
472
+ return this._test(path19, this._testCache, true);
473
473
  }
474
474
  };
475
475
  var factory = (options) => new Ignore2(options);
476
- var isPathValid = (path18) => checkPath(path18 && checkPath.convert(path18), path18, RETURN_FALSE);
476
+ var isPathValid = (path19) => checkPath(path19 && checkPath.convert(path19), path19, RETURN_FALSE);
477
477
  var setupWindows = () => {
478
478
  const makePosix = (str) => /^\\\\\?\\/.test(str) || /["<>|\u0000-\u001F]+/u.test(str) ? str : str.replace(/\\/g, "/");
479
479
  checkPath.convert = makePosix;
480
480
  const REGEX_TEST_WINDOWS_PATH_ABSOLUTE = /^[a-z]:\//i;
481
- checkPath.isNotRelative = (path18) => REGEX_TEST_WINDOWS_PATH_ABSOLUTE.test(path18) || isNotRelative(path18);
481
+ checkPath.isNotRelative = (path19) => REGEX_TEST_WINDOWS_PATH_ABSOLUTE.test(path19) || isNotRelative(path19);
482
482
  };
483
483
  if (
484
484
  // Detect `process` so that it can run in browsers.
@@ -662,7 +662,7 @@ __export(index_exports, {
662
662
  });
663
663
  module.exports = __toCommonJS(index_exports);
664
664
  var os6 = __toESM(require("os"), 1);
665
- var path17 = __toESM(require("path"), 1);
665
+ var path18 = __toESM(require("path"), 1);
666
666
  var import_url2 = require("url");
667
667
 
668
668
  // src/config/constants.ts
@@ -809,6 +809,7 @@ function getDefaultSearchConfig() {
809
809
  rerankTopN: 20,
810
810
  contextLines: 0,
811
811
  routingHints: true,
812
+ routingGraphHandoffHints: false,
812
813
  routingHintRole: "system"
813
814
  };
814
815
  }
@@ -931,6 +932,7 @@ function parseConfig(raw) {
931
932
  rerankTopN: typeof rawSearch.rerankTopN === "number" ? Math.min(200, Math.max(0, Math.floor(rawSearch.rerankTopN))) : defaultSearch.rerankTopN,
932
933
  contextLines: typeof rawSearch.contextLines === "number" ? Math.min(50, Math.max(0, rawSearch.contextLines)) : defaultSearch.contextLines,
933
934
  routingHints: typeof rawSearch.routingHints === "boolean" ? rawSearch.routingHints : defaultSearch.routingHints,
935
+ routingGraphHandoffHints: typeof rawSearch.routingGraphHandoffHints === "boolean" ? rawSearch.routingGraphHandoffHints : defaultSearch.routingGraphHandoffHints,
934
936
  routingHintRole: rawSearch.routingHintRole === "developer" || rawSearch.routingHintRole === "system" ? rawSearch.routingHintRole : defaultSearch.routingHintRole
935
937
  };
936
938
  const rawDebug = input.debug && typeof input.debug === "object" ? input.debug : {};
@@ -1557,7 +1559,7 @@ var ReaddirpStream = class extends import_node_stream.Readable {
1557
1559
  this._directoryFilter = normalizeFilter(opts.directoryFilter);
1558
1560
  const statMethod = opts.lstat ? import_promises.lstat : import_promises.stat;
1559
1561
  if (wantBigintFsStats) {
1560
- this._stat = (path18) => statMethod(path18, { bigint: true });
1562
+ this._stat = (path19) => statMethod(path19, { bigint: true });
1561
1563
  } else {
1562
1564
  this._stat = statMethod;
1563
1565
  }
@@ -1582,8 +1584,8 @@ var ReaddirpStream = class extends import_node_stream.Readable {
1582
1584
  const par = this.parent;
1583
1585
  const fil = par && par.files;
1584
1586
  if (fil && fil.length > 0) {
1585
- const { path: path18, depth } = par;
1586
- const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path18));
1587
+ const { path: path19, depth } = par;
1588
+ const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path19));
1587
1589
  const awaited = await Promise.all(slice);
1588
1590
  for (const entry of awaited) {
1589
1591
  if (!entry)
@@ -1623,20 +1625,20 @@ var ReaddirpStream = class extends import_node_stream.Readable {
1623
1625
  this.reading = false;
1624
1626
  }
1625
1627
  }
1626
- async _exploreDir(path18, depth) {
1628
+ async _exploreDir(path19, depth) {
1627
1629
  let files;
1628
1630
  try {
1629
- files = await (0, import_promises.readdir)(path18, this._rdOptions);
1631
+ files = await (0, import_promises.readdir)(path19, this._rdOptions);
1630
1632
  } catch (error) {
1631
1633
  this._onError(error);
1632
1634
  }
1633
- return { files, depth, path: path18 };
1635
+ return { files, depth, path: path19 };
1634
1636
  }
1635
- async _formatEntry(dirent, path18) {
1637
+ async _formatEntry(dirent, path19) {
1636
1638
  let entry;
1637
1639
  const basename5 = this._isDirent ? dirent.name : dirent;
1638
1640
  try {
1639
- const fullPath = (0, import_node_path.resolve)((0, import_node_path.join)(path18, basename5));
1641
+ const fullPath = (0, import_node_path.resolve)((0, import_node_path.join)(path19, basename5));
1640
1642
  entry = { path: (0, import_node_path.relative)(this._root, fullPath), fullPath, basename: basename5 };
1641
1643
  entry[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath);
1642
1644
  } catch (err) {
@@ -2036,16 +2038,16 @@ var delFromSet = (main, prop, item) => {
2036
2038
  };
2037
2039
  var isEmptySet = (val) => val instanceof Set ? val.size === 0 : !val;
2038
2040
  var FsWatchInstances = /* @__PURE__ */ new Map();
2039
- function createFsWatchInstance(path18, options, listener, errHandler, emitRaw) {
2041
+ function createFsWatchInstance(path19, options, listener, errHandler, emitRaw) {
2040
2042
  const handleEvent = (rawEvent, evPath) => {
2041
- listener(path18);
2042
- emitRaw(rawEvent, evPath, { watchedPath: path18 });
2043
- if (evPath && path18 !== evPath) {
2044
- fsWatchBroadcast(sp.resolve(path18, evPath), KEY_LISTENERS, sp.join(path18, evPath));
2043
+ listener(path19);
2044
+ emitRaw(rawEvent, evPath, { watchedPath: path19 });
2045
+ if (evPath && path19 !== evPath) {
2046
+ fsWatchBroadcast(sp.resolve(path19, evPath), KEY_LISTENERS, sp.join(path19, evPath));
2045
2047
  }
2046
2048
  };
2047
2049
  try {
2048
- return (0, import_node_fs.watch)(path18, {
2050
+ return (0, import_node_fs.watch)(path19, {
2049
2051
  persistent: options.persistent
2050
2052
  }, handleEvent);
2051
2053
  } catch (error) {
@@ -2061,12 +2063,12 @@ var fsWatchBroadcast = (fullPath, listenerType, val1, val2, val3) => {
2061
2063
  listener(val1, val2, val3);
2062
2064
  });
2063
2065
  };
2064
- var setFsWatchListener = (path18, fullPath, options, handlers) => {
2066
+ var setFsWatchListener = (path19, fullPath, options, handlers) => {
2065
2067
  const { listener, errHandler, rawEmitter } = handlers;
2066
2068
  let cont = FsWatchInstances.get(fullPath);
2067
2069
  let watcher;
2068
2070
  if (!options.persistent) {
2069
- watcher = createFsWatchInstance(path18, options, listener, errHandler, rawEmitter);
2071
+ watcher = createFsWatchInstance(path19, options, listener, errHandler, rawEmitter);
2070
2072
  if (!watcher)
2071
2073
  return;
2072
2074
  return watcher.close.bind(watcher);
@@ -2077,7 +2079,7 @@ var setFsWatchListener = (path18, fullPath, options, handlers) => {
2077
2079
  addAndConvert(cont, KEY_RAW, rawEmitter);
2078
2080
  } else {
2079
2081
  watcher = createFsWatchInstance(
2080
- path18,
2082
+ path19,
2081
2083
  options,
2082
2084
  fsWatchBroadcast.bind(null, fullPath, KEY_LISTENERS),
2083
2085
  errHandler,
@@ -2092,7 +2094,7 @@ var setFsWatchListener = (path18, fullPath, options, handlers) => {
2092
2094
  cont.watcherUnusable = true;
2093
2095
  if (isWindows && error.code === "EPERM") {
2094
2096
  try {
2095
- const fd = await (0, import_promises2.open)(path18, "r");
2097
+ const fd = await (0, import_promises2.open)(path19, "r");
2096
2098
  await fd.close();
2097
2099
  broadcastErr(error);
2098
2100
  } catch (err) {
@@ -2123,7 +2125,7 @@ var setFsWatchListener = (path18, fullPath, options, handlers) => {
2123
2125
  };
2124
2126
  };
2125
2127
  var FsWatchFileInstances = /* @__PURE__ */ new Map();
2126
- var setFsWatchFileListener = (path18, fullPath, options, handlers) => {
2128
+ var setFsWatchFileListener = (path19, fullPath, options, handlers) => {
2127
2129
  const { listener, rawEmitter } = handlers;
2128
2130
  let cont = FsWatchFileInstances.get(fullPath);
2129
2131
  const copts = cont && cont.options;
@@ -2145,7 +2147,7 @@ var setFsWatchFileListener = (path18, fullPath, options, handlers) => {
2145
2147
  });
2146
2148
  const currmtime = curr.mtimeMs;
2147
2149
  if (curr.size !== prev.size || currmtime > prev.mtimeMs || currmtime === 0) {
2148
- foreach(cont.listeners, (listener2) => listener2(path18, curr));
2150
+ foreach(cont.listeners, (listener2) => listener2(path19, curr));
2149
2151
  }
2150
2152
  })
2151
2153
  };
@@ -2175,13 +2177,13 @@ var NodeFsHandler = class {
2175
2177
  * @param listener on fs change
2176
2178
  * @returns closer for the watcher instance
2177
2179
  */
2178
- _watchWithNodeFs(path18, listener) {
2180
+ _watchWithNodeFs(path19, listener) {
2179
2181
  const opts = this.fsw.options;
2180
- const directory = sp.dirname(path18);
2181
- const basename5 = sp.basename(path18);
2182
+ const directory = sp.dirname(path19);
2183
+ const basename5 = sp.basename(path19);
2182
2184
  const parent = this.fsw._getWatchedDir(directory);
2183
2185
  parent.add(basename5);
2184
- const absolutePath = sp.resolve(path18);
2186
+ const absolutePath = sp.resolve(path19);
2185
2187
  const options = {
2186
2188
  persistent: opts.persistent
2187
2189
  };
@@ -2191,12 +2193,12 @@ var NodeFsHandler = class {
2191
2193
  if (opts.usePolling) {
2192
2194
  const enableBin = opts.interval !== opts.binaryInterval;
2193
2195
  options.interval = enableBin && isBinaryPath(basename5) ? opts.binaryInterval : opts.interval;
2194
- closer = setFsWatchFileListener(path18, absolutePath, options, {
2196
+ closer = setFsWatchFileListener(path19, absolutePath, options, {
2195
2197
  listener,
2196
2198
  rawEmitter: this.fsw._emitRaw
2197
2199
  });
2198
2200
  } else {
2199
- closer = setFsWatchListener(path18, absolutePath, options, {
2201
+ closer = setFsWatchListener(path19, absolutePath, options, {
2200
2202
  listener,
2201
2203
  errHandler: this._boundHandleError,
2202
2204
  rawEmitter: this.fsw._emitRaw
@@ -2218,7 +2220,7 @@ var NodeFsHandler = class {
2218
2220
  let prevStats = stats;
2219
2221
  if (parent.has(basename5))
2220
2222
  return;
2221
- const listener = async (path18, newStats) => {
2223
+ const listener = async (path19, newStats) => {
2222
2224
  if (!this.fsw._throttle(THROTTLE_MODE_WATCH, file, 5))
2223
2225
  return;
2224
2226
  if (!newStats || newStats.mtimeMs === 0) {
@@ -2232,11 +2234,11 @@ var NodeFsHandler = class {
2232
2234
  this.fsw._emit(EV.CHANGE, file, newStats2);
2233
2235
  }
2234
2236
  if ((isMacos || isLinux || isFreeBSD) && prevStats.ino !== newStats2.ino) {
2235
- this.fsw._closeFile(path18);
2237
+ this.fsw._closeFile(path19);
2236
2238
  prevStats = newStats2;
2237
2239
  const closer2 = this._watchWithNodeFs(file, listener);
2238
2240
  if (closer2)
2239
- this.fsw._addPathCloser(path18, closer2);
2241
+ this.fsw._addPathCloser(path19, closer2);
2240
2242
  } else {
2241
2243
  prevStats = newStats2;
2242
2244
  }
@@ -2268,7 +2270,7 @@ var NodeFsHandler = class {
2268
2270
  * @param item basename of this item
2269
2271
  * @returns true if no more processing is needed for this entry.
2270
2272
  */
2271
- async _handleSymlink(entry, directory, path18, item) {
2273
+ async _handleSymlink(entry, directory, path19, item) {
2272
2274
  if (this.fsw.closed) {
2273
2275
  return;
2274
2276
  }
@@ -2278,7 +2280,7 @@ var NodeFsHandler = class {
2278
2280
  this.fsw._incrReadyCount();
2279
2281
  let linkPath;
2280
2282
  try {
2281
- linkPath = await (0, import_promises2.realpath)(path18);
2283
+ linkPath = await (0, import_promises2.realpath)(path19);
2282
2284
  } catch (e) {
2283
2285
  this.fsw._emitReady();
2284
2286
  return true;
@@ -2288,12 +2290,12 @@ var NodeFsHandler = class {
2288
2290
  if (dir.has(item)) {
2289
2291
  if (this.fsw._symlinkPaths.get(full) !== linkPath) {
2290
2292
  this.fsw._symlinkPaths.set(full, linkPath);
2291
- this.fsw._emit(EV.CHANGE, path18, entry.stats);
2293
+ this.fsw._emit(EV.CHANGE, path19, entry.stats);
2292
2294
  }
2293
2295
  } else {
2294
2296
  dir.add(item);
2295
2297
  this.fsw._symlinkPaths.set(full, linkPath);
2296
- this.fsw._emit(EV.ADD, path18, entry.stats);
2298
+ this.fsw._emit(EV.ADD, path19, entry.stats);
2297
2299
  }
2298
2300
  this.fsw._emitReady();
2299
2301
  return true;
@@ -2323,9 +2325,9 @@ var NodeFsHandler = class {
2323
2325
  return;
2324
2326
  }
2325
2327
  const item = entry.path;
2326
- let path18 = sp.join(directory, item);
2328
+ let path19 = sp.join(directory, item);
2327
2329
  current.add(item);
2328
- if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path18, item)) {
2330
+ if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path19, item)) {
2329
2331
  return;
2330
2332
  }
2331
2333
  if (this.fsw.closed) {
@@ -2334,11 +2336,11 @@ var NodeFsHandler = class {
2334
2336
  }
2335
2337
  if (item === target || !target && !previous.has(item)) {
2336
2338
  this.fsw._incrReadyCount();
2337
- path18 = sp.join(dir, sp.relative(dir, path18));
2338
- this._addToNodeFs(path18, initialAdd, wh, depth + 1);
2339
+ path19 = sp.join(dir, sp.relative(dir, path19));
2340
+ this._addToNodeFs(path19, initialAdd, wh, depth + 1);
2339
2341
  }
2340
2342
  }).on(EV.ERROR, this._boundHandleError);
2341
- return new Promise((resolve12, reject) => {
2343
+ return new Promise((resolve13, reject) => {
2342
2344
  if (!stream)
2343
2345
  return reject();
2344
2346
  stream.once(STR_END, () => {
@@ -2347,7 +2349,7 @@ var NodeFsHandler = class {
2347
2349
  return;
2348
2350
  }
2349
2351
  const wasThrottled = throttler ? throttler.clear() : false;
2350
- resolve12(void 0);
2352
+ resolve13(void 0);
2351
2353
  previous.getChildren().filter((item) => {
2352
2354
  return item !== directory && !current.has(item);
2353
2355
  }).forEach((item) => {
@@ -2404,13 +2406,13 @@ var NodeFsHandler = class {
2404
2406
  * @param depth Child path actually targeted for watch
2405
2407
  * @param target Child path actually targeted for watch
2406
2408
  */
2407
- async _addToNodeFs(path18, initialAdd, priorWh, depth, target) {
2409
+ async _addToNodeFs(path19, initialAdd, priorWh, depth, target) {
2408
2410
  const ready = this.fsw._emitReady;
2409
- if (this.fsw._isIgnored(path18) || this.fsw.closed) {
2411
+ if (this.fsw._isIgnored(path19) || this.fsw.closed) {
2410
2412
  ready();
2411
2413
  return false;
2412
2414
  }
2413
- const wh = this.fsw._getWatchHelpers(path18);
2415
+ const wh = this.fsw._getWatchHelpers(path19);
2414
2416
  if (priorWh) {
2415
2417
  wh.filterPath = (entry) => priorWh.filterPath(entry);
2416
2418
  wh.filterDir = (entry) => priorWh.filterDir(entry);
@@ -2426,8 +2428,8 @@ var NodeFsHandler = class {
2426
2428
  const follow = this.fsw.options.followSymlinks;
2427
2429
  let closer;
2428
2430
  if (stats.isDirectory()) {
2429
- const absPath = sp.resolve(path18);
2430
- const targetPath = follow ? await (0, import_promises2.realpath)(path18) : path18;
2431
+ const absPath = sp.resolve(path19);
2432
+ const targetPath = follow ? await (0, import_promises2.realpath)(path19) : path19;
2431
2433
  if (this.fsw.closed)
2432
2434
  return;
2433
2435
  closer = await this._handleDir(wh.watchPath, stats, initialAdd, depth, target, wh, targetPath);
@@ -2437,29 +2439,29 @@ var NodeFsHandler = class {
2437
2439
  this.fsw._symlinkPaths.set(absPath, targetPath);
2438
2440
  }
2439
2441
  } else if (stats.isSymbolicLink()) {
2440
- const targetPath = follow ? await (0, import_promises2.realpath)(path18) : path18;
2442
+ const targetPath = follow ? await (0, import_promises2.realpath)(path19) : path19;
2441
2443
  if (this.fsw.closed)
2442
2444
  return;
2443
2445
  const parent = sp.dirname(wh.watchPath);
2444
2446
  this.fsw._getWatchedDir(parent).add(wh.watchPath);
2445
2447
  this.fsw._emit(EV.ADD, wh.watchPath, stats);
2446
- closer = await this._handleDir(parent, stats, initialAdd, depth, path18, wh, targetPath);
2448
+ closer = await this._handleDir(parent, stats, initialAdd, depth, path19, wh, targetPath);
2447
2449
  if (this.fsw.closed)
2448
2450
  return;
2449
2451
  if (targetPath !== void 0) {
2450
- this.fsw._symlinkPaths.set(sp.resolve(path18), targetPath);
2452
+ this.fsw._symlinkPaths.set(sp.resolve(path19), targetPath);
2451
2453
  }
2452
2454
  } else {
2453
2455
  closer = this._handleFile(wh.watchPath, stats, initialAdd);
2454
2456
  }
2455
2457
  ready();
2456
2458
  if (closer)
2457
- this.fsw._addPathCloser(path18, closer);
2459
+ this.fsw._addPathCloser(path19, closer);
2458
2460
  return false;
2459
2461
  } catch (error) {
2460
2462
  if (this.fsw._handleError(error)) {
2461
2463
  ready();
2462
- return path18;
2464
+ return path19;
2463
2465
  }
2464
2466
  }
2465
2467
  }
@@ -2491,35 +2493,35 @@ function createPattern(matcher) {
2491
2493
  if (matcher.path === string)
2492
2494
  return true;
2493
2495
  if (matcher.recursive) {
2494
- const relative8 = sp2.relative(matcher.path, string);
2495
- if (!relative8) {
2496
+ const relative9 = sp2.relative(matcher.path, string);
2497
+ if (!relative9) {
2496
2498
  return false;
2497
2499
  }
2498
- return !relative8.startsWith("..") && !sp2.isAbsolute(relative8);
2500
+ return !relative9.startsWith("..") && !sp2.isAbsolute(relative9);
2499
2501
  }
2500
2502
  return false;
2501
2503
  };
2502
2504
  }
2503
2505
  return () => false;
2504
2506
  }
2505
- function normalizePath(path18) {
2506
- if (typeof path18 !== "string")
2507
+ function normalizePath(path19) {
2508
+ if (typeof path19 !== "string")
2507
2509
  throw new Error("string expected");
2508
- path18 = sp2.normalize(path18);
2509
- path18 = path18.replace(/\\/g, "/");
2510
+ path19 = sp2.normalize(path19);
2511
+ path19 = path19.replace(/\\/g, "/");
2510
2512
  let prepend = false;
2511
- if (path18.startsWith("//"))
2513
+ if (path19.startsWith("//"))
2512
2514
  prepend = true;
2513
- path18 = path18.replace(DOUBLE_SLASH_RE, "/");
2515
+ path19 = path19.replace(DOUBLE_SLASH_RE, "/");
2514
2516
  if (prepend)
2515
- path18 = "/" + path18;
2516
- return path18;
2517
+ path19 = "/" + path19;
2518
+ return path19;
2517
2519
  }
2518
2520
  function matchPatterns(patterns, testString, stats) {
2519
- const path18 = normalizePath(testString);
2521
+ const path19 = normalizePath(testString);
2520
2522
  for (let index = 0; index < patterns.length; index++) {
2521
2523
  const pattern = patterns[index];
2522
- if (pattern(path18, stats)) {
2524
+ if (pattern(path19, stats)) {
2523
2525
  return true;
2524
2526
  }
2525
2527
  }
@@ -2557,19 +2559,19 @@ var toUnix = (string) => {
2557
2559
  }
2558
2560
  return str;
2559
2561
  };
2560
- var normalizePathToUnix = (path18) => toUnix(sp2.normalize(toUnix(path18)));
2561
- var normalizeIgnored = (cwd = "") => (path18) => {
2562
- if (typeof path18 === "string") {
2563
- return normalizePathToUnix(sp2.isAbsolute(path18) ? path18 : sp2.join(cwd, path18));
2562
+ var normalizePathToUnix = (path19) => toUnix(sp2.normalize(toUnix(path19)));
2563
+ var normalizeIgnored = (cwd = "") => (path19) => {
2564
+ if (typeof path19 === "string") {
2565
+ return normalizePathToUnix(sp2.isAbsolute(path19) ? path19 : sp2.join(cwd, path19));
2564
2566
  } else {
2565
- return path18;
2567
+ return path19;
2566
2568
  }
2567
2569
  };
2568
- var getAbsolutePath = (path18, cwd) => {
2569
- if (sp2.isAbsolute(path18)) {
2570
- return path18;
2570
+ var getAbsolutePath = (path19, cwd) => {
2571
+ if (sp2.isAbsolute(path19)) {
2572
+ return path19;
2571
2573
  }
2572
- return sp2.join(cwd, path18);
2574
+ return sp2.join(cwd, path19);
2573
2575
  };
2574
2576
  var EMPTY_SET = Object.freeze(/* @__PURE__ */ new Set());
2575
2577
  var DirEntry = class {
@@ -2634,10 +2636,10 @@ var WatchHelper = class {
2634
2636
  dirParts;
2635
2637
  followSymlinks;
2636
2638
  statMethod;
2637
- constructor(path18, follow, fsw) {
2639
+ constructor(path19, follow, fsw) {
2638
2640
  this.fsw = fsw;
2639
- const watchPath = path18;
2640
- this.path = path18 = path18.replace(REPLACER_RE, "");
2641
+ const watchPath = path19;
2642
+ this.path = path19 = path19.replace(REPLACER_RE, "");
2641
2643
  this.watchPath = watchPath;
2642
2644
  this.fullWatchPath = sp2.resolve(watchPath);
2643
2645
  this.dirParts = [];
@@ -2777,20 +2779,20 @@ var FSWatcher = class extends import_node_events.EventEmitter {
2777
2779
  this._closePromise = void 0;
2778
2780
  let paths = unifyPaths(paths_);
2779
2781
  if (cwd) {
2780
- paths = paths.map((path18) => {
2781
- const absPath = getAbsolutePath(path18, cwd);
2782
+ paths = paths.map((path19) => {
2783
+ const absPath = getAbsolutePath(path19, cwd);
2782
2784
  return absPath;
2783
2785
  });
2784
2786
  }
2785
- paths.forEach((path18) => {
2786
- this._removeIgnoredPath(path18);
2787
+ paths.forEach((path19) => {
2788
+ this._removeIgnoredPath(path19);
2787
2789
  });
2788
2790
  this._userIgnored = void 0;
2789
2791
  if (!this._readyCount)
2790
2792
  this._readyCount = 0;
2791
2793
  this._readyCount += paths.length;
2792
- Promise.all(paths.map(async (path18) => {
2793
- const res = await this._nodeFsHandler._addToNodeFs(path18, !_internal, void 0, 0, _origAdd);
2794
+ Promise.all(paths.map(async (path19) => {
2795
+ const res = await this._nodeFsHandler._addToNodeFs(path19, !_internal, void 0, 0, _origAdd);
2794
2796
  if (res)
2795
2797
  this._emitReady();
2796
2798
  return res;
@@ -2812,17 +2814,17 @@ var FSWatcher = class extends import_node_events.EventEmitter {
2812
2814
  return this;
2813
2815
  const paths = unifyPaths(paths_);
2814
2816
  const { cwd } = this.options;
2815
- paths.forEach((path18) => {
2816
- if (!sp2.isAbsolute(path18) && !this._closers.has(path18)) {
2817
+ paths.forEach((path19) => {
2818
+ if (!sp2.isAbsolute(path19) && !this._closers.has(path19)) {
2817
2819
  if (cwd)
2818
- path18 = sp2.join(cwd, path18);
2819
- path18 = sp2.resolve(path18);
2820
+ path19 = sp2.join(cwd, path19);
2821
+ path19 = sp2.resolve(path19);
2820
2822
  }
2821
- this._closePath(path18);
2822
- this._addIgnoredPath(path18);
2823
- if (this._watched.has(path18)) {
2823
+ this._closePath(path19);
2824
+ this._addIgnoredPath(path19);
2825
+ if (this._watched.has(path19)) {
2824
2826
  this._addIgnoredPath({
2825
- path: path18,
2827
+ path: path19,
2826
2828
  recursive: true
2827
2829
  });
2828
2830
  }
@@ -2886,38 +2888,38 @@ var FSWatcher = class extends import_node_events.EventEmitter {
2886
2888
  * @param stats arguments to be passed with event
2887
2889
  * @returns the error if defined, otherwise the value of the FSWatcher instance's `closed` flag
2888
2890
  */
2889
- async _emit(event, path18, stats) {
2891
+ async _emit(event, path19, stats) {
2890
2892
  if (this.closed)
2891
2893
  return;
2892
2894
  const opts = this.options;
2893
2895
  if (isWindows)
2894
- path18 = sp2.normalize(path18);
2896
+ path19 = sp2.normalize(path19);
2895
2897
  if (opts.cwd)
2896
- path18 = sp2.relative(opts.cwd, path18);
2897
- const args = [path18];
2898
+ path19 = sp2.relative(opts.cwd, path19);
2899
+ const args = [path19];
2898
2900
  if (stats != null)
2899
2901
  args.push(stats);
2900
2902
  const awf = opts.awaitWriteFinish;
2901
2903
  let pw;
2902
- if (awf && (pw = this._pendingWrites.get(path18))) {
2904
+ if (awf && (pw = this._pendingWrites.get(path19))) {
2903
2905
  pw.lastChange = /* @__PURE__ */ new Date();
2904
2906
  return this;
2905
2907
  }
2906
2908
  if (opts.atomic) {
2907
2909
  if (event === EVENTS.UNLINK) {
2908
- this._pendingUnlinks.set(path18, [event, ...args]);
2910
+ this._pendingUnlinks.set(path19, [event, ...args]);
2909
2911
  setTimeout(() => {
2910
- this._pendingUnlinks.forEach((entry, path19) => {
2912
+ this._pendingUnlinks.forEach((entry, path20) => {
2911
2913
  this.emit(...entry);
2912
2914
  this.emit(EVENTS.ALL, ...entry);
2913
- this._pendingUnlinks.delete(path19);
2915
+ this._pendingUnlinks.delete(path20);
2914
2916
  });
2915
2917
  }, typeof opts.atomic === "number" ? opts.atomic : 100);
2916
2918
  return this;
2917
2919
  }
2918
- if (event === EVENTS.ADD && this._pendingUnlinks.has(path18)) {
2920
+ if (event === EVENTS.ADD && this._pendingUnlinks.has(path19)) {
2919
2921
  event = EVENTS.CHANGE;
2920
- this._pendingUnlinks.delete(path18);
2922
+ this._pendingUnlinks.delete(path19);
2921
2923
  }
2922
2924
  }
2923
2925
  if (awf && (event === EVENTS.ADD || event === EVENTS.CHANGE) && this._readyEmitted) {
@@ -2935,16 +2937,16 @@ var FSWatcher = class extends import_node_events.EventEmitter {
2935
2937
  this.emitWithAll(event, args);
2936
2938
  }
2937
2939
  };
2938
- this._awaitWriteFinish(path18, awf.stabilityThreshold, event, awfEmit);
2940
+ this._awaitWriteFinish(path19, awf.stabilityThreshold, event, awfEmit);
2939
2941
  return this;
2940
2942
  }
2941
2943
  if (event === EVENTS.CHANGE) {
2942
- const isThrottled = !this._throttle(EVENTS.CHANGE, path18, 50);
2944
+ const isThrottled = !this._throttle(EVENTS.CHANGE, path19, 50);
2943
2945
  if (isThrottled)
2944
2946
  return this;
2945
2947
  }
2946
2948
  if (opts.alwaysStat && stats === void 0 && (event === EVENTS.ADD || event === EVENTS.ADD_DIR || event === EVENTS.CHANGE)) {
2947
- const fullPath = opts.cwd ? sp2.join(opts.cwd, path18) : path18;
2949
+ const fullPath = opts.cwd ? sp2.join(opts.cwd, path19) : path19;
2948
2950
  let stats2;
2949
2951
  try {
2950
2952
  stats2 = await (0, import_promises3.stat)(fullPath);
@@ -2975,23 +2977,23 @@ var FSWatcher = class extends import_node_events.EventEmitter {
2975
2977
  * @param timeout duration of time to suppress duplicate actions
2976
2978
  * @returns tracking object or false if action should be suppressed
2977
2979
  */
2978
- _throttle(actionType, path18, timeout) {
2980
+ _throttle(actionType, path19, timeout) {
2979
2981
  if (!this._throttled.has(actionType)) {
2980
2982
  this._throttled.set(actionType, /* @__PURE__ */ new Map());
2981
2983
  }
2982
2984
  const action = this._throttled.get(actionType);
2983
2985
  if (!action)
2984
2986
  throw new Error("invalid throttle");
2985
- const actionPath = action.get(path18);
2987
+ const actionPath = action.get(path19);
2986
2988
  if (actionPath) {
2987
2989
  actionPath.count++;
2988
2990
  return false;
2989
2991
  }
2990
2992
  let timeoutObject;
2991
2993
  const clear = () => {
2992
- const item = action.get(path18);
2994
+ const item = action.get(path19);
2993
2995
  const count = item ? item.count : 0;
2994
- action.delete(path18);
2996
+ action.delete(path19);
2995
2997
  clearTimeout(timeoutObject);
2996
2998
  if (item)
2997
2999
  clearTimeout(item.timeoutObject);
@@ -2999,7 +3001,7 @@ var FSWatcher = class extends import_node_events.EventEmitter {
2999
3001
  };
3000
3002
  timeoutObject = setTimeout(clear, timeout);
3001
3003
  const thr = { timeoutObject, clear, count: 0 };
3002
- action.set(path18, thr);
3004
+ action.set(path19, thr);
3003
3005
  return thr;
3004
3006
  }
3005
3007
  _incrReadyCount() {
@@ -3013,44 +3015,44 @@ var FSWatcher = class extends import_node_events.EventEmitter {
3013
3015
  * @param event
3014
3016
  * @param awfEmit Callback to be called when ready for event to be emitted.
3015
3017
  */
3016
- _awaitWriteFinish(path18, threshold, event, awfEmit) {
3018
+ _awaitWriteFinish(path19, threshold, event, awfEmit) {
3017
3019
  const awf = this.options.awaitWriteFinish;
3018
3020
  if (typeof awf !== "object")
3019
3021
  return;
3020
3022
  const pollInterval = awf.pollInterval;
3021
3023
  let timeoutHandler;
3022
- let fullPath = path18;
3023
- if (this.options.cwd && !sp2.isAbsolute(path18)) {
3024
- fullPath = sp2.join(this.options.cwd, path18);
3024
+ let fullPath = path19;
3025
+ if (this.options.cwd && !sp2.isAbsolute(path19)) {
3026
+ fullPath = sp2.join(this.options.cwd, path19);
3025
3027
  }
3026
3028
  const now = /* @__PURE__ */ new Date();
3027
3029
  const writes = this._pendingWrites;
3028
3030
  function awaitWriteFinishFn(prevStat) {
3029
3031
  (0, import_node_fs2.stat)(fullPath, (err, curStat) => {
3030
- if (err || !writes.has(path18)) {
3032
+ if (err || !writes.has(path19)) {
3031
3033
  if (err && err.code !== "ENOENT")
3032
3034
  awfEmit(err);
3033
3035
  return;
3034
3036
  }
3035
3037
  const now2 = Number(/* @__PURE__ */ new Date());
3036
3038
  if (prevStat && curStat.size !== prevStat.size) {
3037
- writes.get(path18).lastChange = now2;
3039
+ writes.get(path19).lastChange = now2;
3038
3040
  }
3039
- const pw = writes.get(path18);
3041
+ const pw = writes.get(path19);
3040
3042
  const df = now2 - pw.lastChange;
3041
3043
  if (df >= threshold) {
3042
- writes.delete(path18);
3044
+ writes.delete(path19);
3043
3045
  awfEmit(void 0, curStat);
3044
3046
  } else {
3045
3047
  timeoutHandler = setTimeout(awaitWriteFinishFn, pollInterval, curStat);
3046
3048
  }
3047
3049
  });
3048
3050
  }
3049
- if (!writes.has(path18)) {
3050
- writes.set(path18, {
3051
+ if (!writes.has(path19)) {
3052
+ writes.set(path19, {
3051
3053
  lastChange: now,
3052
3054
  cancelWait: () => {
3053
- writes.delete(path18);
3055
+ writes.delete(path19);
3054
3056
  clearTimeout(timeoutHandler);
3055
3057
  return event;
3056
3058
  }
@@ -3061,8 +3063,8 @@ var FSWatcher = class extends import_node_events.EventEmitter {
3061
3063
  /**
3062
3064
  * Determines whether user has asked to ignore this path.
3063
3065
  */
3064
- _isIgnored(path18, stats) {
3065
- if (this.options.atomic && DOT_RE.test(path18))
3066
+ _isIgnored(path19, stats) {
3067
+ if (this.options.atomic && DOT_RE.test(path19))
3066
3068
  return true;
3067
3069
  if (!this._userIgnored) {
3068
3070
  const { cwd } = this.options;
@@ -3072,17 +3074,17 @@ var FSWatcher = class extends import_node_events.EventEmitter {
3072
3074
  const list = [...ignoredPaths.map(normalizeIgnored(cwd)), ...ignored];
3073
3075
  this._userIgnored = anymatch(list, void 0);
3074
3076
  }
3075
- return this._userIgnored(path18, stats);
3077
+ return this._userIgnored(path19, stats);
3076
3078
  }
3077
- _isntIgnored(path18, stat4) {
3078
- return !this._isIgnored(path18, stat4);
3079
+ _isntIgnored(path19, stat4) {
3080
+ return !this._isIgnored(path19, stat4);
3079
3081
  }
3080
3082
  /**
3081
3083
  * Provides a set of common helpers and properties relating to symlink handling.
3082
3084
  * @param path file or directory pattern being watched
3083
3085
  */
3084
- _getWatchHelpers(path18) {
3085
- return new WatchHelper(path18, this.options.followSymlinks, this);
3086
+ _getWatchHelpers(path19) {
3087
+ return new WatchHelper(path19, this.options.followSymlinks, this);
3086
3088
  }
3087
3089
  // Directory helpers
3088
3090
  // -----------------
@@ -3114,63 +3116,63 @@ var FSWatcher = class extends import_node_events.EventEmitter {
3114
3116
  * @param item base path of item/directory
3115
3117
  */
3116
3118
  _remove(directory, item, isDirectory) {
3117
- const path18 = sp2.join(directory, item);
3118
- const fullPath = sp2.resolve(path18);
3119
- isDirectory = isDirectory != null ? isDirectory : this._watched.has(path18) || this._watched.has(fullPath);
3120
- if (!this._throttle("remove", path18, 100))
3119
+ const path19 = sp2.join(directory, item);
3120
+ const fullPath = sp2.resolve(path19);
3121
+ isDirectory = isDirectory != null ? isDirectory : this._watched.has(path19) || this._watched.has(fullPath);
3122
+ if (!this._throttle("remove", path19, 100))
3121
3123
  return;
3122
3124
  if (!isDirectory && this._watched.size === 1) {
3123
3125
  this.add(directory, item, true);
3124
3126
  }
3125
- const wp = this._getWatchedDir(path18);
3127
+ const wp = this._getWatchedDir(path19);
3126
3128
  const nestedDirectoryChildren = wp.getChildren();
3127
- nestedDirectoryChildren.forEach((nested) => this._remove(path18, nested));
3129
+ nestedDirectoryChildren.forEach((nested) => this._remove(path19, nested));
3128
3130
  const parent = this._getWatchedDir(directory);
3129
3131
  const wasTracked = parent.has(item);
3130
3132
  parent.remove(item);
3131
3133
  if (this._symlinkPaths.has(fullPath)) {
3132
3134
  this._symlinkPaths.delete(fullPath);
3133
3135
  }
3134
- let relPath = path18;
3136
+ let relPath = path19;
3135
3137
  if (this.options.cwd)
3136
- relPath = sp2.relative(this.options.cwd, path18);
3138
+ relPath = sp2.relative(this.options.cwd, path19);
3137
3139
  if (this.options.awaitWriteFinish && this._pendingWrites.has(relPath)) {
3138
3140
  const event = this._pendingWrites.get(relPath).cancelWait();
3139
3141
  if (event === EVENTS.ADD)
3140
3142
  return;
3141
3143
  }
3142
- this._watched.delete(path18);
3144
+ this._watched.delete(path19);
3143
3145
  this._watched.delete(fullPath);
3144
3146
  const eventName = isDirectory ? EVENTS.UNLINK_DIR : EVENTS.UNLINK;
3145
- if (wasTracked && !this._isIgnored(path18))
3146
- this._emit(eventName, path18);
3147
- this._closePath(path18);
3147
+ if (wasTracked && !this._isIgnored(path19))
3148
+ this._emit(eventName, path19);
3149
+ this._closePath(path19);
3148
3150
  }
3149
3151
  /**
3150
3152
  * Closes all watchers for a path
3151
3153
  */
3152
- _closePath(path18) {
3153
- this._closeFile(path18);
3154
- const dir = sp2.dirname(path18);
3155
- this._getWatchedDir(dir).remove(sp2.basename(path18));
3154
+ _closePath(path19) {
3155
+ this._closeFile(path19);
3156
+ const dir = sp2.dirname(path19);
3157
+ this._getWatchedDir(dir).remove(sp2.basename(path19));
3156
3158
  }
3157
3159
  /**
3158
3160
  * Closes only file-specific watchers
3159
3161
  */
3160
- _closeFile(path18) {
3161
- const closers = this._closers.get(path18);
3162
+ _closeFile(path19) {
3163
+ const closers = this._closers.get(path19);
3162
3164
  if (!closers)
3163
3165
  return;
3164
3166
  closers.forEach((closer) => closer());
3165
- this._closers.delete(path18);
3167
+ this._closers.delete(path19);
3166
3168
  }
3167
- _addPathCloser(path18, closer) {
3169
+ _addPathCloser(path19, closer) {
3168
3170
  if (!closer)
3169
3171
  return;
3170
- let list = this._closers.get(path18);
3172
+ let list = this._closers.get(path19);
3171
3173
  if (!list) {
3172
3174
  list = [];
3173
- this._closers.set(path18, list);
3175
+ this._closers.set(path19, list);
3174
3176
  }
3175
3177
  list.push(closer);
3176
3178
  }
@@ -3500,7 +3502,7 @@ var FileWatcher = class {
3500
3502
  return;
3501
3503
  }
3502
3504
  const changes = Array.from(this.pendingChanges.entries()).map(
3503
- ([path18, type]) => ({ path: path18, type })
3505
+ ([path19, type]) => ({ path: path19, type })
3504
3506
  );
3505
3507
  this.pendingChanges.clear();
3506
3508
  try {
@@ -3633,12 +3635,14 @@ function createWatcherWithIndexer(getIndexer, projectRoot, config) {
3633
3635
  }
3634
3636
 
3635
3637
  // src/tools/index.ts
3636
- var import_plugin = require("@opencode-ai/plugin");
3638
+ var import_plugin2 = require("@opencode-ai/plugin");
3637
3639
 
3638
3640
  // src/indexer/index.ts
3639
3641
  var import_fs7 = require("fs");
3640
- var path12 = __toESM(require("path"), 1);
3642
+ var path13 = __toESM(require("path"), 1);
3641
3643
  var import_perf_hooks = require("perf_hooks");
3644
+ var import_child_process2 = require("child_process");
3645
+ var import_util2 = require("util");
3642
3646
 
3643
3647
  // node_modules/eventemitter3/index.mjs
3644
3648
  var import_index = __toESM(require_eventemitter3(), 1);
@@ -3662,7 +3666,7 @@ function pTimeout(promise, options) {
3662
3666
  } = options;
3663
3667
  let timer;
3664
3668
  let abortHandler;
3665
- const wrappedPromise = new Promise((resolve12, reject) => {
3669
+ const wrappedPromise = new Promise((resolve13, reject) => {
3666
3670
  if (typeof milliseconds !== "number" || Math.sign(milliseconds) !== 1) {
3667
3671
  throw new TypeError(`Expected \`milliseconds\` to be a positive number, got \`${milliseconds}\``);
3668
3672
  }
@@ -3676,7 +3680,7 @@ function pTimeout(promise, options) {
3676
3680
  };
3677
3681
  signal.addEventListener("abort", abortHandler, { once: true });
3678
3682
  }
3679
- promise.then(resolve12, reject);
3683
+ promise.then(resolve13, reject);
3680
3684
  if (milliseconds === Number.POSITIVE_INFINITY) {
3681
3685
  return;
3682
3686
  }
@@ -3684,7 +3688,7 @@ function pTimeout(promise, options) {
3684
3688
  timer = customTimers.setTimeout.call(void 0, () => {
3685
3689
  if (fallback) {
3686
3690
  try {
3687
- resolve12(fallback());
3691
+ resolve13(fallback());
3688
3692
  } catch (error) {
3689
3693
  reject(error);
3690
3694
  }
@@ -3694,7 +3698,7 @@ function pTimeout(promise, options) {
3694
3698
  promise.cancel();
3695
3699
  }
3696
3700
  if (message === false) {
3697
- resolve12();
3701
+ resolve13();
3698
3702
  } else if (message instanceof Error) {
3699
3703
  reject(message);
3700
3704
  } else {
@@ -4096,7 +4100,7 @@ var PQueue = class extends import_index.default {
4096
4100
  // Assign unique ID if not provided
4097
4101
  id: options.id ?? (this.#idAssigner++).toString()
4098
4102
  };
4099
- return new Promise((resolve12, reject) => {
4103
+ return new Promise((resolve13, reject) => {
4100
4104
  const taskSymbol = /* @__PURE__ */ Symbol(`task-${options.id}`);
4101
4105
  let cleanupQueueAbortHandler = () => void 0;
4102
4106
  const run = async () => {
@@ -4136,7 +4140,7 @@ var PQueue = class extends import_index.default {
4136
4140
  })]);
4137
4141
  }
4138
4142
  const result = await operation;
4139
- resolve12(result);
4143
+ resolve13(result);
4140
4144
  this.emit("completed", result);
4141
4145
  } catch (error) {
4142
4146
  reject(error);
@@ -4324,13 +4328,13 @@ var PQueue = class extends import_index.default {
4324
4328
  });
4325
4329
  }
4326
4330
  async #onEvent(event, filter) {
4327
- return new Promise((resolve12) => {
4331
+ return new Promise((resolve13) => {
4328
4332
  const listener = () => {
4329
4333
  if (filter && !filter()) {
4330
4334
  return;
4331
4335
  }
4332
4336
  this.off(event, listener);
4333
- resolve12();
4337
+ resolve13();
4334
4338
  };
4335
4339
  this.on(event, listener);
4336
4340
  });
@@ -4616,7 +4620,7 @@ async function onAttemptFailure({ error, attemptNumber, retriesConsumed, startTi
4616
4620
  const finalDelay = Math.min(delayTime, remainingTime);
4617
4621
  options.signal?.throwIfAborted();
4618
4622
  if (finalDelay > 0) {
4619
- await new Promise((resolve12, reject) => {
4623
+ await new Promise((resolve13, reject) => {
4620
4624
  const onAbort = () => {
4621
4625
  clearTimeout(timeoutToken);
4622
4626
  options.signal?.removeEventListener("abort", onAbort);
@@ -4624,7 +4628,7 @@ async function onAttemptFailure({ error, attemptNumber, retriesConsumed, startTi
4624
4628
  };
4625
4629
  const timeoutToken = setTimeout(() => {
4626
4630
  options.signal?.removeEventListener("abort", onAbort);
4627
- resolve12();
4631
+ resolve13();
4628
4632
  }, finalDelay);
4629
4633
  if (options.unref) {
4630
4634
  timeoutToken.unref?.();
@@ -5852,6 +5856,15 @@ function createMockNativeBinding() {
5852
5856
  close() {
5853
5857
  throw error;
5854
5858
  }
5859
+ getTransitiveReachability() {
5860
+ throw error;
5861
+ }
5862
+ detectCommunities() {
5863
+ throw error;
5864
+ }
5865
+ computeCentrality() {
5866
+ throw error;
5867
+ }
5855
5868
  }
5856
5869
  };
5857
5870
  }
@@ -6402,6 +6415,14 @@ var Database = class {
6402
6415
  this.throwIfClosed();
6403
6416
  return this.inner.getSymbolsByNameCi(name);
6404
6417
  }
6418
+ getSymbolsForBranch(branch) {
6419
+ this.throwIfClosed();
6420
+ return this.inner.getSymbolsForBranch(branch);
6421
+ }
6422
+ getSymbolsForFiles(filePaths, branch) {
6423
+ this.throwIfClosed();
6424
+ return this.inner.getSymbolsForFiles(filePaths, branch);
6425
+ }
6405
6426
  deleteSymbolsByFile(filePath) {
6406
6427
  this.throwIfClosed();
6407
6428
  return this.inner.deleteSymbolsByFile(filePath);
@@ -6479,8 +6500,125 @@ var Database = class {
6479
6500
  this.throwIfClosed();
6480
6501
  return this.inner.gcOrphanCallEdges();
6481
6502
  }
6503
+ getTransitiveReachability(rootSymbolIds, branch, direction, maxDepth) {
6504
+ this.throwIfClosed();
6505
+ return this.inner.getTransitiveReachability(
6506
+ rootSymbolIds,
6507
+ branch,
6508
+ direction,
6509
+ maxDepth ?? null
6510
+ );
6511
+ }
6512
+ detectCommunities(branch, symbolIds) {
6513
+ this.throwIfClosed();
6514
+ return this.inner.detectCommunities(branch, symbolIds ?? null);
6515
+ }
6516
+ computeCentrality(branch) {
6517
+ this.throwIfClosed();
6518
+ return this.inner.computeCentrality(branch);
6519
+ }
6482
6520
  };
6483
6521
 
6522
+ // src/tools/changed-files.ts
6523
+ var path12 = __toESM(require("path"), 1);
6524
+ var import_child_process = require("child_process");
6525
+ var import_util = require("util");
6526
+ var execFileAsync = (0, import_util.promisify)(import_child_process.execFile);
6527
+ function getErrorMessage(error) {
6528
+ if (error instanceof Error) return error.message;
6529
+ return String(error);
6530
+ }
6531
+ async function getChangedFiles(opts) {
6532
+ const { pr, branch, projectRoot, baseBranch = "main" } = opts;
6533
+ if (pr !== void 0) {
6534
+ return getChangedFilesForPr(pr, projectRoot, baseBranch);
6535
+ }
6536
+ return getChangedFilesForBranch(branch, projectRoot, baseBranch);
6537
+ }
6538
+ async function getChangedFilesForPr(pr, projectRoot, baseBranch) {
6539
+ let headRefName;
6540
+ let actualBaseBranch = baseBranch;
6541
+ try {
6542
+ const { stdout } = await execFileAsync(
6543
+ "gh",
6544
+ ["pr", "view", String(pr), "--json", "headRefName,baseRefName,files"],
6545
+ { cwd: projectRoot, timeout: 3e4 }
6546
+ );
6547
+ const data = JSON.parse(stdout);
6548
+ headRefName = data.headRefName;
6549
+ actualBaseBranch = data.baseRefName || baseBranch;
6550
+ if (data.files && data.files.length > 0) {
6551
+ return {
6552
+ files: normalizeFiles(
6553
+ data.files.map((f) => f.path),
6554
+ projectRoot
6555
+ ),
6556
+ baseBranch: actualBaseBranch,
6557
+ source: "gh",
6558
+ headRefName
6559
+ };
6560
+ }
6561
+ } catch (error) {
6562
+ throw new Error(
6563
+ `Failed to retrieve PR #${pr} via gh CLI: ${getErrorMessage(error)}`
6564
+ );
6565
+ }
6566
+ if (headRefName === void 0) {
6567
+ throw new Error(
6568
+ `PR #${pr} returned no usable branch or file information.`
6569
+ );
6570
+ }
6571
+ const result = await getChangedFilesForBranch(headRefName, projectRoot, actualBaseBranch);
6572
+ return { ...result, headRefName };
6573
+ }
6574
+ async function getChangedFilesForBranch(branch, projectRoot, baseBranch) {
6575
+ const targetBranch = branch || await getCurrentBranch2(projectRoot);
6576
+ const mergeBase = await getMergeBase(projectRoot, baseBranch, targetBranch);
6577
+ const { stdout } = await execFileAsync(
6578
+ "git",
6579
+ ["diff", "--name-only", `${mergeBase}...${targetBranch}`],
6580
+ { cwd: projectRoot, timeout: 3e4 }
6581
+ );
6582
+ return {
6583
+ files: normalizeFiles(stdout.split("\n"), projectRoot),
6584
+ baseBranch,
6585
+ source: "git",
6586
+ headRefName: targetBranch
6587
+ };
6588
+ }
6589
+ async function getCurrentBranch2(projectRoot) {
6590
+ const { stdout } = await execFileAsync(
6591
+ "git",
6592
+ ["branch", "--show-current"],
6593
+ { cwd: projectRoot, timeout: 3e4 }
6594
+ );
6595
+ return stdout.trim() || "HEAD";
6596
+ }
6597
+ async function getMergeBase(projectRoot, baseBranch, branch) {
6598
+ const { stdout } = await execFileAsync(
6599
+ "git",
6600
+ ["merge-base", baseBranch, branch],
6601
+ { cwd: projectRoot, timeout: 3e4 }
6602
+ );
6603
+ return stdout.trim();
6604
+ }
6605
+ function normalizeFiles(rawFiles, projectRoot) {
6606
+ const seen = /* @__PURE__ */ new Set();
6607
+ const result = [];
6608
+ for (const raw of rawFiles) {
6609
+ const trimmed = raw.trim();
6610
+ if (!trimmed) continue;
6611
+ const absolute = path12.resolve(projectRoot, trimmed);
6612
+ const relative9 = path12.relative(projectRoot, absolute);
6613
+ const cleaned = relative9.startsWith("./") ? relative9.slice(2) : relative9;
6614
+ if (!seen.has(cleaned)) {
6615
+ seen.add(cleaned);
6616
+ result.push(cleaned);
6617
+ }
6618
+ }
6619
+ return result;
6620
+ }
6621
+
6484
6622
  // src/indexer/index.ts
6485
6623
  var CALL_GRAPH_LANGUAGES = /* @__PURE__ */ new Set(["typescript", "tsx", "javascript", "jsx", "python", "go", "rust", "php", "apex", "zig", "gdscript", "matlab"]);
6486
6624
  var CASE_INSENSITIVE_LANGUAGES = /* @__PURE__ */ new Set(["apex"]);
@@ -6488,6 +6626,7 @@ var CALL_GRAPH_SYMBOL_CHUNK_TYPES = /* @__PURE__ */ new Set([
6488
6626
  "function_declaration",
6489
6627
  "function",
6490
6628
  "arrow_function",
6629
+ "export_statement",
6491
6630
  "method_definition",
6492
6631
  "class_declaration",
6493
6632
  "interface_declaration",
@@ -6526,7 +6665,7 @@ function float32ArrayToBuffer(arr) {
6526
6665
  function bufferToFloat32Array(buf) {
6527
6666
  return new Float32Array(buf.buffer, buf.byteOffset, buf.byteLength / 4);
6528
6667
  }
6529
- function getErrorMessage(error) {
6668
+ function getErrorMessage2(error) {
6530
6669
  if (error instanceof Error) {
6531
6670
  return error.message;
6532
6671
  }
@@ -6539,7 +6678,7 @@ function getErrorMessage(error) {
6539
6678
  return String(error);
6540
6679
  }
6541
6680
  function isRateLimitError(error) {
6542
- const message = getErrorMessage(error);
6681
+ const message = getErrorMessage2(error);
6543
6682
  return message.includes("429") || message.toLowerCase().includes("rate limit") || message.toLowerCase().includes("too many requests");
6544
6683
  }
6545
6684
  function getSafeEmbeddingChunkTokenLimit(provider) {
@@ -6557,7 +6696,7 @@ function getDynamicBatchOptions(provider) {
6557
6696
  return {};
6558
6697
  }
6559
6698
  function isSqliteCorruptionError(error) {
6560
- const message = getErrorMessage(error).toLowerCase();
6699
+ const message = getErrorMessage2(error).toLowerCase();
6561
6700
  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");
6562
6701
  }
6563
6702
  var STARTUP_WARNING_METADATA_KEY = "index.startupWarning";
@@ -6719,9 +6858,9 @@ function hasAllEmbeddingParts(parts, expectedPartCount) {
6719
6858
  return true;
6720
6859
  }
6721
6860
  function isPathWithinRoot(filePath, rootPath) {
6722
- const normalizedFilePath = path12.resolve(filePath);
6723
- const normalizedRoot = path12.resolve(rootPath);
6724
- return normalizedFilePath === normalizedRoot || normalizedFilePath.startsWith(`${normalizedRoot}${path12.sep}`);
6861
+ const normalizedFilePath = path13.resolve(filePath);
6862
+ const normalizedRoot = path13.resolve(rootPath);
6863
+ return normalizedFilePath === normalizedRoot || normalizedFilePath.startsWith(`${normalizedRoot}${path13.sep}`);
6725
6864
  }
6726
6865
  var rankingQueryTokenCache = /* @__PURE__ */ new Map();
6727
6866
  var rankingNameTokenCache = /* @__PURE__ */ new Map();
@@ -7774,9 +7913,9 @@ var Indexer = class {
7774
7913
  this.projectRoot = projectRoot;
7775
7914
  this.config = config;
7776
7915
  this.indexPath = this.getIndexPath();
7777
- this.fileHashCachePath = path12.join(this.indexPath, "file-hashes.json");
7778
- this.failedBatchesPath = path12.join(this.indexPath, "failed-batches.json");
7779
- this.indexingLockPath = path12.join(this.indexPath, "indexing.lock");
7916
+ this.fileHashCachePath = path13.join(this.indexPath, "file-hashes.json");
7917
+ this.failedBatchesPath = path13.join(this.indexPath, "failed-batches.json");
7918
+ this.indexingLockPath = path13.join(this.indexPath, "indexing.lock");
7780
7919
  this.logger = initializeLogger(config.debug);
7781
7920
  }
7782
7921
  getIndexPath() {
@@ -7808,14 +7947,14 @@ var Indexer = class {
7808
7947
  }
7809
7948
  atomicWriteSync(targetPath, data) {
7810
7949
  const tempPath = `${targetPath}.tmp`;
7811
- (0, import_fs7.mkdirSync)(path12.dirname(targetPath), { recursive: true });
7950
+ (0, import_fs7.mkdirSync)(path13.dirname(targetPath), { recursive: true });
7812
7951
  (0, import_fs7.writeFileSync)(tempPath, data);
7813
7952
  (0, import_fs7.renameSync)(tempPath, targetPath);
7814
7953
  }
7815
7954
  getScopedRoots() {
7816
- const roots = /* @__PURE__ */ new Set([path12.resolve(this.projectRoot)]);
7955
+ const roots = /* @__PURE__ */ new Set([path13.resolve(this.projectRoot)]);
7817
7956
  for (const kbRoot of this.config.knowledgeBases) {
7818
- roots.add(path12.resolve(this.projectRoot, kbRoot));
7957
+ roots.add(path13.resolve(this.projectRoot, kbRoot));
7819
7958
  }
7820
7959
  return Array.from(roots);
7821
7960
  }
@@ -7824,22 +7963,29 @@ var Indexer = class {
7824
7963
  if (this.config.scope !== "global") {
7825
7964
  return branchName;
7826
7965
  }
7827
- const projectHash = hashContent(path12.resolve(this.projectRoot)).slice(0, 16);
7966
+ const projectHash = hashContent(path13.resolve(this.projectRoot)).slice(0, 16);
7967
+ return `${projectHash}:${branchName}`;
7968
+ }
7969
+ getBranchCatalogKeyFor(branchName) {
7970
+ if (this.config.scope !== "global") {
7971
+ return branchName;
7972
+ }
7973
+ const projectHash = hashContent(path13.resolve(this.projectRoot)).slice(0, 16);
7828
7974
  return `${projectHash}:${branchName}`;
7829
7975
  }
7830
7976
  getLegacyBranchCatalogKey() {
7831
7977
  return this.currentBranch || "default";
7832
7978
  }
7833
7979
  getLegacyMigrationMetadataKey() {
7834
- const projectHash = hashContent(path12.resolve(this.projectRoot)).slice(0, 16);
7980
+ const projectHash = hashContent(path13.resolve(this.projectRoot)).slice(0, 16);
7835
7981
  return `index.globalBranchMigration.${projectHash}`;
7836
7982
  }
7837
7983
  getProjectEmbeddingStrategyMetadataKey() {
7838
- const projectHash = hashContent(path12.resolve(this.projectRoot)).slice(0, 16);
7984
+ const projectHash = hashContent(path13.resolve(this.projectRoot)).slice(0, 16);
7839
7985
  return `index.embeddingStrategyVersion.${projectHash}`;
7840
7986
  }
7841
7987
  getProjectForceReembedMetadataKey() {
7842
- const projectHash = hashContent(path12.resolve(this.projectRoot)).slice(0, 16);
7988
+ const projectHash = hashContent(path13.resolve(this.projectRoot)).slice(0, 16);
7843
7989
  return `index.forceReembed.${projectHash}`;
7844
7990
  }
7845
7991
  hasProjectForceReembedPending() {
@@ -7933,7 +8079,7 @@ var Indexer = class {
7933
8079
  if (!this.database) {
7934
8080
  return { chunkIds, symbolIds };
7935
8081
  }
7936
- const projectRootPath = path12.resolve(this.projectRoot);
8082
+ const projectRootPath = path13.resolve(this.projectRoot);
7937
8083
  const projectLocalFilePaths = /* @__PURE__ */ new Set([
7938
8084
  ...Array.from(this.fileHashCache.keys()).filter(
7939
8085
  (filePath) => this.isFileInCurrentScope(filePath, roots) && isPathWithinRoot(filePath, projectRootPath)
@@ -7956,7 +8102,7 @@ var Indexer = class {
7956
8102
  if (this.config.scope !== "global") {
7957
8103
  return this.getBranchCatalogCleanupKeys();
7958
8104
  }
7959
- const projectHash = hashContent(path12.resolve(this.projectRoot)).slice(0, 16);
8105
+ const projectHash = hashContent(path13.resolve(this.projectRoot)).slice(0, 16);
7960
8106
  const keys = /* @__PURE__ */ new Set();
7961
8107
  const projectChunkIdSet = new Set(projectChunkIds);
7962
8108
  const projectSymbolIdSet = new Set(projectSymbolIds);
@@ -8040,7 +8186,7 @@ var Indexer = class {
8040
8186
  if (!this.database || this.config.scope !== "global") {
8041
8187
  return false;
8042
8188
  }
8043
- const projectHash = hashContent(path12.resolve(this.projectRoot)).slice(0, 16);
8189
+ const projectHash = hashContent(path13.resolve(this.projectRoot)).slice(0, 16);
8044
8190
  const roots = this.getScopedRoots();
8045
8191
  const { chunkIds: projectLocalChunkIds, symbolIds: projectLocalSymbolIds } = this.getProjectLocalScopedOwnershipIds(roots);
8046
8192
  return this.database.getAllBranches().some(
@@ -8074,7 +8220,7 @@ var Indexer = class {
8074
8220
  ...Array.from(this.fileHashCache.keys()).filter((filePath) => this.isFileInCurrentScope(filePath, roots)),
8075
8221
  ...scopedEntries.map(({ metadata }) => metadata.filePath)
8076
8222
  ]);
8077
- const projectRootPath = path12.resolve(this.projectRoot);
8223
+ const projectRootPath = path13.resolve(this.projectRoot);
8078
8224
  const projectLocalFilePaths = new Set(
8079
8225
  Array.from(filePaths).filter((filePath) => isPathWithinRoot(filePath, projectRootPath))
8080
8226
  );
@@ -8327,7 +8473,7 @@ var Indexer = class {
8327
8473
  this.logger.search("warn", "External reranker failed; using deterministic order", {
8328
8474
  provider: reranker.provider,
8329
8475
  model: reranker.model,
8330
- error: getErrorMessage(error)
8476
+ error: getErrorMessage2(error)
8331
8477
  });
8332
8478
  return candidates;
8333
8479
  }
@@ -8434,13 +8580,13 @@ var Indexer = class {
8434
8580
  }
8435
8581
  await import_fs7.promises.mkdir(this.indexPath, { recursive: true });
8436
8582
  const dimensions = this.configuredProviderInfo.modelInfo.dimensions;
8437
- const storePath = path12.join(this.indexPath, "vectors");
8583
+ const storePath = path13.join(this.indexPath, "vectors");
8438
8584
  this.store = new VectorStore(storePath, dimensions);
8439
- const indexFilePath = path12.join(this.indexPath, "vectors.usearch");
8585
+ const indexFilePath = path13.join(this.indexPath, "vectors.usearch");
8440
8586
  if ((0, import_fs7.existsSync)(indexFilePath)) {
8441
8587
  this.store.load();
8442
8588
  }
8443
- const invertedIndexPath = path12.join(this.indexPath, "inverted-index.json");
8589
+ const invertedIndexPath = path13.join(this.indexPath, "inverted-index.json");
8444
8590
  this.invertedIndex = new InvertedIndex(invertedIndexPath);
8445
8591
  try {
8446
8592
  this.invertedIndex.load();
@@ -8450,7 +8596,7 @@ var Indexer = class {
8450
8596
  }
8451
8597
  this.invertedIndex = new InvertedIndex(invertedIndexPath);
8452
8598
  }
8453
- const dbPath = path12.join(this.indexPath, "codebase.db");
8599
+ const dbPath = path13.join(this.indexPath, "codebase.db");
8454
8600
  let dbIsNew = !(0, import_fs7.existsSync)(dbPath);
8455
8601
  try {
8456
8602
  this.database = new Database(dbPath);
@@ -8531,7 +8677,7 @@ var Indexer = class {
8531
8677
  if (await this.tryResetCorruptedIndex("running automatic orphan garbage collection", error)) {
8532
8678
  return {
8533
8679
  resetCorruptedIndex: true,
8534
- warning: this.getCorruptedIndexWarning(path12.join(this.indexPath, "codebase.db"))
8680
+ warning: this.getCorruptedIndexWarning(path13.join(this.indexPath, "codebase.db"))
8535
8681
  };
8536
8682
  }
8537
8683
  throw error;
@@ -8546,7 +8692,7 @@ var Indexer = class {
8546
8692
  return;
8547
8693
  }
8548
8694
  const retainedEntries = store.getAllMetadata().filter(({ key }) => !excludedSet.has(key));
8549
- const storeBasePath = path12.join(this.indexPath, "vectors");
8695
+ const storeBasePath = path13.join(this.indexPath, "vectors");
8550
8696
  const storeIndexPath = `${storeBasePath}.usearch`;
8551
8697
  const storeMetadataPath = `${storeBasePath}.meta.json`;
8552
8698
  const backupIndexPath = `${storeIndexPath}.bak`;
@@ -8631,9 +8777,9 @@ var Indexer = class {
8631
8777
  if (!isSqliteCorruptionError(error)) {
8632
8778
  return false;
8633
8779
  }
8634
- const dbPath = path12.join(this.indexPath, "codebase.db");
8780
+ const dbPath = path13.join(this.indexPath, "codebase.db");
8635
8781
  const warning = this.getCorruptedIndexWarning(dbPath);
8636
- const errorMessage = getErrorMessage(error);
8782
+ const errorMessage = getErrorMessage2(error);
8637
8783
  if (this.config.scope === "global") {
8638
8784
  this.logger.error("Detected corrupted shared global index database", {
8639
8785
  stage,
@@ -8654,15 +8800,15 @@ var Indexer = class {
8654
8800
  this.indexCompatibility = null;
8655
8801
  this.fileHashCache.clear();
8656
8802
  const resetPaths = [
8657
- path12.join(this.indexPath, "codebase.db"),
8658
- path12.join(this.indexPath, "codebase.db-shm"),
8659
- path12.join(this.indexPath, "codebase.db-wal"),
8660
- path12.join(this.indexPath, "vectors.usearch"),
8661
- path12.join(this.indexPath, "inverted-index.json"),
8662
- path12.join(this.indexPath, "file-hashes.json"),
8663
- path12.join(this.indexPath, "failed-batches.json"),
8664
- path12.join(this.indexPath, "indexing.lock"),
8665
- path12.join(this.indexPath, "vectors")
8803
+ path13.join(this.indexPath, "codebase.db"),
8804
+ path13.join(this.indexPath, "codebase.db-shm"),
8805
+ path13.join(this.indexPath, "codebase.db-wal"),
8806
+ path13.join(this.indexPath, "vectors.usearch"),
8807
+ path13.join(this.indexPath, "inverted-index.json"),
8808
+ path13.join(this.indexPath, "file-hashes.json"),
8809
+ path13.join(this.indexPath, "failed-batches.json"),
8810
+ path13.join(this.indexPath, "indexing.lock"),
8811
+ path13.join(this.indexPath, "vectors")
8666
8812
  ];
8667
8813
  await Promise.all(resetPaths.map(async (targetPath) => {
8668
8814
  try {
@@ -8927,7 +9073,7 @@ var Indexer = class {
8927
9073
  for (const parsed of parsedFiles) {
8928
9074
  currentFilePaths.add(parsed.path);
8929
9075
  if (parsed.chunks.length === 0) {
8930
- const relativePath = path12.relative(this.projectRoot, parsed.path);
9076
+ const relativePath = path13.relative(this.projectRoot, parsed.path);
8931
9077
  stats.parseFailures.push(relativePath);
8932
9078
  }
8933
9079
  let fileChunkCount = 0;
@@ -9211,7 +9357,7 @@ var Indexer = class {
9211
9357
  for (const requestBatch of requestBatches) {
9212
9358
  queue.add(async () => {
9213
9359
  if (rateLimitBackoffMs > 0) {
9214
- await new Promise((resolve12) => setTimeout(resolve12, rateLimitBackoffMs));
9360
+ await new Promise((resolve13) => setTimeout(resolve13, rateLimitBackoffMs));
9215
9361
  }
9216
9362
  try {
9217
9363
  const result = await pRetry(
@@ -9226,7 +9372,7 @@ var Indexer = class {
9226
9372
  factor: 2,
9227
9373
  shouldRetry: (error) => !(error.error instanceof CustomProviderNonRetryableError),
9228
9374
  onFailedAttempt: (error) => {
9229
- const message = getErrorMessage(error);
9375
+ const message = getErrorMessage2(error);
9230
9376
  if (isRateLimitError(error)) {
9231
9377
  rateLimitBackoffMs = Math.min(providerRateLimits.maxRetryMs, (rateLimitBackoffMs || providerRateLimits.minRetryMs) * 2);
9232
9378
  this.logger.embedding("warn", `Rate limited, backing off`, {
@@ -9333,7 +9479,7 @@ var Indexer = class {
9333
9479
  });
9334
9480
  } catch (error) {
9335
9481
  const failedChunks = getUniquePendingChunksFromRequests(requestBatch).filter((chunk) => !completedChunkIds.has(chunk.id));
9336
- const failureMessage = getErrorMessage(error);
9482
+ const failureMessage = getErrorMessage2(error);
9337
9483
  const failureTimestamp = (/* @__PURE__ */ new Date()).toISOString();
9338
9484
  for (const chunk of failedChunks) {
9339
9485
  if (!failedChunkIds.has(chunk.id)) {
@@ -9772,8 +9918,8 @@ var Indexer = class {
9772
9918
  this.indexCompatibility = compatibility;
9773
9919
  return;
9774
9920
  }
9775
- const localProjectIndexPath = path12.join(this.projectRoot, ".opencode", "index");
9776
- if (path12.resolve(this.indexPath) !== path12.resolve(localProjectIndexPath)) {
9921
+ const localProjectIndexPath = path13.join(this.projectRoot, ".opencode", "index");
9922
+ if (path13.resolve(this.indexPath) !== path13.resolve(localProjectIndexPath)) {
9777
9923
  throw new Error(
9778
9924
  "Project-scoped force rebuild is unsafe while using an inherited worktree index. Create a local project config boundary before clearing the index."
9779
9925
  );
@@ -9861,7 +10007,7 @@ var Indexer = class {
9861
10007
  gcOrphanSymbols: 0,
9862
10008
  gcOrphanCallEdges: 0,
9863
10009
  resetCorruptedIndex: true,
9864
- warning: this.getCorruptedIndexWarning(path12.join(this.indexPath, "codebase.db"))
10010
+ warning: this.getCorruptedIndexWarning(path13.join(this.indexPath, "codebase.db"))
9865
10011
  };
9866
10012
  }
9867
10013
  this.logger.recordGc(removedCount, gcOrphanChunks, gcOrphanEmbeddings);
@@ -10012,7 +10158,7 @@ var Indexer = class {
10012
10158
  succeeded += successfulResults.length;
10013
10159
  stillFailing.push(...failedChunksForBatch.values());
10014
10160
  } catch (error) {
10015
- const failureMessage = getErrorMessage(error);
10161
+ const failureMessage = getErrorMessage2(error);
10016
10162
  const failureTimestamp = (/* @__PURE__ */ new Date()).toISOString();
10017
10163
  const unaccountedChunks = batch.chunks.filter(
10018
10164
  (chunk) => !failedChunksForBatch.has(chunk.id) && !completedChunkIds.has(chunk.id)
@@ -10218,13 +10364,194 @@ var Indexer = class {
10218
10364
  const { database } = await this.ensureInitialized();
10219
10365
  let shortest = [];
10220
10366
  for (const branchKey of this.getBranchCatalogKeys()) {
10221
- const path18 = database.findShortestPath(fromName, toName, branchKey, maxDepth);
10222
- if (path18.length > 0 && (shortest.length === 0 || path18.length < shortest.length)) {
10223
- shortest = path18;
10367
+ const path19 = database.findShortestPath(fromName, toName, branchKey, maxDepth);
10368
+ if (path19.length > 0 && (shortest.length === 0 || path19.length < shortest.length)) {
10369
+ shortest = path19;
10224
10370
  }
10225
10371
  }
10226
10372
  return shortest;
10227
10373
  }
10374
+ async getSymbolsForBranch(branch) {
10375
+ const { database } = await this.ensureInitialized();
10376
+ const resolvedBranch = branch ?? this.getBranchCatalogKey();
10377
+ return database.getSymbolsForBranch(resolvedBranch);
10378
+ }
10379
+ async getSymbolsForFiles(filePaths, branch) {
10380
+ const { database } = await this.ensureInitialized();
10381
+ const resolvedBranch = branch ?? this.getBranchCatalogKey();
10382
+ return database.getSymbolsForFiles(filePaths, resolvedBranch);
10383
+ }
10384
+ async getTransitiveReachability(rootSymbolIds, direction, maxDepth) {
10385
+ const { database } = await this.ensureInitialized();
10386
+ const branch = this.getBranchCatalogKey();
10387
+ return database.getTransitiveReachability(rootSymbolIds, branch, direction, maxDepth);
10388
+ }
10389
+ async detectCommunities(branch, symbolIds) {
10390
+ const { database } = await this.ensureInitialized();
10391
+ const resolvedBranch = branch ?? this.getBranchCatalogKey();
10392
+ return database.detectCommunities(resolvedBranch, symbolIds);
10393
+ }
10394
+ async computeCentrality(branch) {
10395
+ const { database } = await this.ensureInitialized();
10396
+ const resolvedBranch = branch ?? this.getBranchCatalogKey();
10397
+ return database.computeCentrality(resolvedBranch);
10398
+ }
10399
+ async getPrImpact(opts) {
10400
+ const { database } = await this.ensureInitialized();
10401
+ const execFileAsync2 = (0, import_util2.promisify)(import_child_process2.execFile);
10402
+ const changedFilesResult = await getChangedFiles({
10403
+ pr: opts.pr,
10404
+ branch: opts.branch,
10405
+ projectRoot: this.projectRoot,
10406
+ baseBranch: this.baseBranch
10407
+ });
10408
+ const changedFiles = changedFilesResult.files;
10409
+ const headRefName = changedFilesResult.headRefName;
10410
+ if (opts.pr !== void 0 && headRefName === void 0) {
10411
+ throw new Error(
10412
+ `Could not resolve head branch for PR #${opts.pr}. Run index_codebase on the PR branch first.`
10413
+ );
10414
+ }
10415
+ const resolvedBranch = opts.pr !== void 0 ? headRefName : opts.branch || this.currentBranch;
10416
+ const branchKey = this.getBranchCatalogKeyFor(resolvedBranch || "default");
10417
+ const branchSymbols = database.getSymbolsForBranch(branchKey);
10418
+ if (branchSymbols.length === 0) {
10419
+ throw new Error(
10420
+ "Run index_codebase first to build the call graph and symbol index for this branch."
10421
+ );
10422
+ }
10423
+ const absoluteChangedFiles = changedFiles.map((f) => path13.resolve(this.projectRoot, f));
10424
+ const directSymbols = database.getSymbolsForFiles(absoluteChangedFiles, branchKey);
10425
+ const directIds = directSymbols.map((s) => s.id);
10426
+ const direction = opts.direction ?? "both";
10427
+ const maxDepth = opts.maxDepth ?? 5;
10428
+ const transitiveCallers = database.getTransitiveReachability(
10429
+ directIds,
10430
+ branchKey,
10431
+ direction,
10432
+ maxDepth
10433
+ );
10434
+ const affectedIdsSet = new Set(directIds);
10435
+ for (const caller of transitiveCallers) {
10436
+ affectedIdsSet.add(caller.symbolId);
10437
+ }
10438
+ const allAffectedIds = Array.from(affectedIdsSet);
10439
+ const communitiesData = database.detectCommunities(branchKey, allAffectedIds);
10440
+ const communityMap = /* @__PURE__ */ new Map();
10441
+ for (const c of communitiesData) {
10442
+ if (!communityMap.has(c.communityLabel)) {
10443
+ communityMap.set(c.communityLabel, {
10444
+ label: c.communityLabel,
10445
+ symbolCount: 0,
10446
+ directSymbols: /* @__PURE__ */ new Set()
10447
+ });
10448
+ }
10449
+ const entry = communityMap.get(c.communityLabel);
10450
+ entry.symbolCount++;
10451
+ if (directIds.includes(c.symbolId)) {
10452
+ entry.directSymbols.add(c.symbolId);
10453
+ }
10454
+ }
10455
+ const communities = Array.from(communityMap.values()).map((c) => ({
10456
+ label: c.label,
10457
+ symbolCount: c.symbolCount,
10458
+ directSymbols: Array.from(c.directSymbols)
10459
+ }));
10460
+ const centralityData = database.computeCentrality(branchKey);
10461
+ const hubThreshold = opts.hubThreshold ?? 10;
10462
+ const hubNodes = centralityData.filter((c) => directIds.includes(c.symbolId) && c.callerCount >= hubThreshold).map((c) => ({
10463
+ id: c.symbolId,
10464
+ name: c.symbolName,
10465
+ callerCount: c.callerCount,
10466
+ filePath: c.filePath
10467
+ }));
10468
+ const totalAffected = allAffectedIds.length;
10469
+ let riskLevel;
10470
+ let riskReason;
10471
+ if (totalAffected < 5 && hubNodes.length === 0) {
10472
+ riskLevel = "LOW";
10473
+ riskReason = `Small impact: ${totalAffected} affected symbols, no hub nodes touched.`;
10474
+ } else if (totalAffected > 20 || hubNodes.length > 1) {
10475
+ riskLevel = "HIGH";
10476
+ riskReason = `Large impact: ${totalAffected} affected symbols${hubNodes.length > 0 ? `, ${hubNodes.length} hub nodes touched` : ""}.`;
10477
+ } else {
10478
+ riskLevel = "MEDIUM";
10479
+ riskReason = `Moderate impact: ${totalAffected} affected symbols${hubNodes.length === 1 ? ", 1 hub node touched" : ""}.`;
10480
+ }
10481
+ let conflictingPRs;
10482
+ if (opts.checkConflicts) {
10483
+ conflictingPRs = [];
10484
+ try {
10485
+ const { stdout } = await execFileAsync2(
10486
+ "gh",
10487
+ ["pr", "list", "--state", "open", "--json", "number,headRefName", "--limit", "10000"],
10488
+ { cwd: this.projectRoot, timeout: 3e4 }
10489
+ );
10490
+ const openPRs = JSON.parse(stdout);
10491
+ const currentCommunityLabels = new Set(communities.map((c) => c.label));
10492
+ const allCommunitiesData = database.detectCommunities(branchKey);
10493
+ const symbolToCommunity = /* @__PURE__ */ new Map();
10494
+ const structuralKey = (filePath, name) => `${filePath.toLowerCase()}:${name.toLowerCase()}`;
10495
+ for (const c of allCommunitiesData) {
10496
+ symbolToCommunity.set(structuralKey(c.filePath, c.symbolName), c.communityLabel);
10497
+ }
10498
+ for (const openPr of openPRs) {
10499
+ if (openPr.number === opts.pr) continue;
10500
+ try {
10501
+ const otherChanged = await getChangedFiles({
10502
+ pr: openPr.number,
10503
+ projectRoot: this.projectRoot,
10504
+ baseBranch: this.baseBranch
10505
+ });
10506
+ const otherAbsolute = otherChanged.files.map((f) => path13.resolve(this.projectRoot, f));
10507
+ const otherBranchKey = this.getBranchCatalogKeyFor(openPr.headRefName);
10508
+ const otherSymbols = database.getSymbolsForFiles(otherAbsolute, otherBranchKey);
10509
+ const otherLabels = /* @__PURE__ */ new Set();
10510
+ for (const sym of otherSymbols) {
10511
+ const label = symbolToCommunity.get(structuralKey(sym.filePath, sym.name));
10512
+ if (label) {
10513
+ otherLabels.add(label);
10514
+ }
10515
+ }
10516
+ const overlapping = Array.from(otherLabels).filter(
10517
+ (l) => currentCommunityLabels.has(l)
10518
+ );
10519
+ if (overlapping.length > 0) {
10520
+ conflictingPRs.push({
10521
+ pr: openPr.number,
10522
+ branch: openPr.headRefName,
10523
+ overlappingCommunities: overlapping
10524
+ });
10525
+ }
10526
+ } catch {
10527
+ }
10528
+ }
10529
+ } catch {
10530
+ }
10531
+ }
10532
+ return {
10533
+ changedFiles,
10534
+ directSymbols: directSymbols.map((s) => ({
10535
+ id: s.id,
10536
+ name: s.name,
10537
+ kind: s.kind,
10538
+ filePath: s.filePath
10539
+ })),
10540
+ transitiveCallers: transitiveCallers.map((c) => ({
10541
+ id: c.symbolId,
10542
+ name: c.symbolName,
10543
+ filePath: c.filePath,
10544
+ depth: c.depth
10545
+ })),
10546
+ totalAffected,
10547
+ communities,
10548
+ hubNodes,
10549
+ riskLevel,
10550
+ riskReason,
10551
+ direction,
10552
+ conflictingPRs
10553
+ };
10554
+ }
10228
10555
  async close() {
10229
10556
  await this.database?.close();
10230
10557
  this.database = null;
@@ -10452,54 +10779,139 @@ ${truncateContent(r.content)}
10452
10779
  return formatted.join("\n\n");
10453
10780
  }
10454
10781
 
10782
+ // src/tools/pr-impact.ts
10783
+ var import_plugin = require("@opencode-ai/plugin");
10784
+
10785
+ // src/tools/format-pr-impact.ts
10786
+ function formatPrImpact(result) {
10787
+ const lines = [];
10788
+ lines.push(`\u2192 Files changed: ${result.changedFiles.length}`);
10789
+ for (const file of result.changedFiles) {
10790
+ lines.push(` - ${file}`);
10791
+ }
10792
+ const directCount = result.directSymbols.length;
10793
+ const transitiveCount = result.transitiveCallers.length;
10794
+ const directionLabel = result.direction === "callees" ? "callees" : result.direction === "both" ? "reachable (callers + callees)" : "callers";
10795
+ lines.push(
10796
+ `\u2192 Symbols affected: ${result.totalAffected} (${directCount} direct, ${transitiveCount} transitive ${directionLabel})`
10797
+ );
10798
+ if (directCount > 0) {
10799
+ lines.push(
10800
+ ` Direct: ${result.directSymbols.map((s) => `${s.name} (${s.kind})`).join(", ")}`
10801
+ );
10802
+ }
10803
+ if (transitiveCount > 0) {
10804
+ lines.push(
10805
+ ` Transitive ${directionLabel}: ${result.transitiveCallers.map((s) => s.name).join(", ")}`
10806
+ );
10807
+ }
10808
+ if (result.communities.length > 0) {
10809
+ lines.push(
10810
+ `\u2192 Communities touched: ${result.communities.map((c) => c.label).join(", ")}`
10811
+ );
10812
+ for (const community of result.communities) {
10813
+ lines.push(
10814
+ ` - ${community.label}: ${community.symbolCount} symbols`
10815
+ );
10816
+ }
10817
+ } else {
10818
+ lines.push("\u2192 Communities touched: none");
10819
+ }
10820
+ lines.push(`\u2192 Risk: ${result.riskLevel} \u2014 ${result.riskReason}`);
10821
+ if (result.hubNodes.length > 0) {
10822
+ lines.push(" Hub nodes in change scope:");
10823
+ for (const hub of result.hubNodes) {
10824
+ lines.push(` - ${hub.name} (${hub.callerCount} callers) at ${hub.filePath}`);
10825
+ }
10826
+ }
10827
+ if (result.conflictingPRs && result.conflictingPRs.length > 0) {
10828
+ const conflictList = result.conflictingPRs.map(
10829
+ (c) => `PR #${c.pr} (also touches ${c.overlappingCommunities.join(", ")} community)`
10830
+ );
10831
+ lines.push(`\u2192 Potential conflicts with: ${conflictList.join(", ")}`);
10832
+ }
10833
+ return lines.join("\n");
10834
+ }
10835
+
10836
+ // src/tools/pr-impact.ts
10837
+ var z = import_plugin.tool.schema;
10838
+ var pr_impact = (0, import_plugin.tool)({
10839
+ 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.",
10840
+ args: {
10841
+ pr: z.number().optional().describe("Pull request number to analyze"),
10842
+ branch: z.string().optional().describe("Branch name to analyze (defaults to current branch)"),
10843
+ maxDepth: z.number().optional().default(5).describe("Maximum traversal depth for transitive callers (default: 5)"),
10844
+ hubThreshold: z.number().optional().default(10).describe("Minimum caller count to flag a symbol as a hub node (default: 10)"),
10845
+ checkConflicts: z.boolean().optional().default(false).describe("Check for conflicting open PRs touching the same communities (default: false)"),
10846
+ 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)")
10847
+ },
10848
+ async execute(args, context) {
10849
+ const indexer = getIndexerForProject(context?.worktree);
10850
+ try {
10851
+ const result = await indexer.getPrImpact({
10852
+ pr: args.pr,
10853
+ branch: args.branch,
10854
+ maxDepth: args.maxDepth,
10855
+ hubThreshold: args.hubThreshold,
10856
+ checkConflicts: args.checkConflicts,
10857
+ direction: args.direction
10858
+ });
10859
+ return formatPrImpact(result);
10860
+ } catch (error) {
10861
+ const message = error instanceof Error ? error.message : String(error);
10862
+ return `Error analyzing PR impact: ${message}`;
10863
+ }
10864
+ }
10865
+ });
10866
+
10455
10867
  // src/tools/knowledge-base-paths.ts
10456
- var path13 = __toESM(require("path"), 1);
10868
+ var path14 = __toESM(require("path"), 1);
10457
10869
  function resolveConfigPathValue(value, baseDir) {
10458
10870
  const trimmed = value.trim();
10459
10871
  if (!trimmed) {
10460
10872
  return trimmed;
10461
10873
  }
10462
- const absolutePath = path13.isAbsolute(trimmed) ? trimmed : path13.resolve(baseDir, trimmed);
10463
- return path13.normalize(absolutePath);
10874
+ const absolutePath = path14.isAbsolute(trimmed) ? trimmed : path14.resolve(baseDir, trimmed);
10875
+ return path14.normalize(absolutePath);
10464
10876
  }
10465
10877
  function serializeConfigPathValue(value, baseDir) {
10466
10878
  const trimmed = value.trim();
10467
10879
  if (!trimmed) {
10468
10880
  return trimmed;
10469
10881
  }
10470
- if (!path13.isAbsolute(trimmed)) {
10471
- return normalizePathSeparators(path13.normalize(trimmed));
10882
+ if (!path14.isAbsolute(trimmed)) {
10883
+ return normalizePathSeparators(path14.normalize(trimmed));
10472
10884
  }
10473
- const relativePath = path13.relative(baseDir, trimmed);
10474
- if (!relativePath || !relativePath.startsWith("..") && !path13.isAbsolute(relativePath)) {
10475
- return normalizePathSeparators(path13.normalize(relativePath || "."));
10885
+ const relativePath = path14.relative(baseDir, trimmed);
10886
+ if (!relativePath || !relativePath.startsWith("..") && !path14.isAbsolute(relativePath)) {
10887
+ return normalizePathSeparators(path14.normalize(relativePath || "."));
10476
10888
  }
10477
- return path13.normalize(trimmed);
10889
+ return path14.normalize(trimmed);
10478
10890
  }
10479
10891
  function resolveKnowledgeBasePath(value, projectRoot) {
10480
- return path13.isAbsolute(value) ? value : path13.resolve(projectRoot, value);
10892
+ return path14.isAbsolute(value) ? value : path14.resolve(projectRoot, value);
10481
10893
  }
10482
10894
  function normalizeKnowledgeBasePath2(value, projectRoot) {
10483
- return path13.normalize(resolveKnowledgeBasePath(value, projectRoot));
10895
+ return path14.normalize(resolveKnowledgeBasePath(value, projectRoot));
10484
10896
  }
10485
10897
  function hasMatchingKnowledgeBasePath(knowledgeBases, inputPath, projectRoot) {
10486
- const normalizedInput = path13.normalize(inputPath);
10898
+ const normalizedInput = path14.normalize(inputPath);
10487
10899
  return knowledgeBases.some((kb) => normalizeKnowledgeBasePath2(kb, projectRoot) === normalizedInput);
10488
10900
  }
10489
10901
  function findKnowledgeBasePathIndex(knowledgeBases, inputPath, projectRoot) {
10490
- const normalizedInput = path13.normalize(inputPath);
10902
+ const normalizedInput = path14.normalize(inputPath);
10491
10903
  return knowledgeBases.findIndex(
10492
- (kb) => path13.normalize(kb) === normalizedInput || normalizeKnowledgeBasePath2(kb, projectRoot) === normalizedInput
10904
+ (kb) => path14.normalize(kb) === normalizedInput || normalizeKnowledgeBasePath2(kb, projectRoot) === normalizedInput
10493
10905
  );
10494
10906
  }
10495
10907
 
10496
10908
  // src/tools/index.ts
10497
10909
  var import_fs9 = require("fs");
10498
- var path15 = __toESM(require("path"), 1);
10910
+ var path16 = __toESM(require("path"), 1);
10499
10911
 
10500
10912
  // src/tools/config-state.ts
10501
10913
  var import_fs8 = require("fs");
10502
- var path14 = __toESM(require("path"), 1);
10914
+ var path15 = __toESM(require("path"), 1);
10503
10915
  function normalizeKnowledgeBasePaths(config, projectRoot) {
10504
10916
  const normalized = { ...config };
10505
10917
  if (Array.isArray(normalized.knowledgeBases)) {
@@ -10526,8 +10938,8 @@ function loadEditableConfig(projectRoot) {
10526
10938
  }
10527
10939
  function saveConfig(projectRoot, config) {
10528
10940
  const configPath = getConfigPath(projectRoot);
10529
- const configDir = path14.dirname(configPath);
10530
- const configBaseDir = path14.dirname(configDir);
10941
+ const configDir = path15.dirname(configPath);
10942
+ const configBaseDir = path15.dirname(configDir);
10531
10943
  if (!(0, import_fs8.existsSync)(configDir)) {
10532
10944
  (0, import_fs8.mkdirSync)(configDir, { recursive: true });
10533
10945
  }
@@ -10545,7 +10957,7 @@ var os5 = __toESM(require("os"), 1);
10545
10957
  function ensureStringArray(value) {
10546
10958
  return Array.isArray(value) ? value : [];
10547
10959
  }
10548
- var z = import_plugin.tool.schema;
10960
+ var z2 = import_plugin2.tool.schema;
10549
10961
  var indexerMap = /* @__PURE__ */ new Map();
10550
10962
  var defaultProjectRoot = "";
10551
10963
  function initializeTools(projectRoot, config) {
@@ -10565,12 +10977,12 @@ function shouldForceLocalizeProjectIndex(projectRoot) {
10565
10977
  if (currentConfig.scope !== "project") {
10566
10978
  return false;
10567
10979
  }
10568
- const localIndexPath = path15.join(projectRoot, ".opencode", "index");
10980
+ const localIndexPath = path16.join(projectRoot, ".opencode", "index");
10569
10981
  const mainRepoRoot = resolveWorktreeMainRepoRoot(projectRoot);
10570
10982
  if (!mainRepoRoot) {
10571
10983
  return false;
10572
10984
  }
10573
- const inheritedIndexPath = path15.join(mainRepoRoot, ".opencode", "index");
10985
+ const inheritedIndexPath = path16.join(mainRepoRoot, ".opencode", "index");
10574
10986
  return !(0, import_fs9.existsSync)(localIndexPath) && (0, import_fs9.existsSync)(inheritedIndexPath);
10575
10987
  }
10576
10988
  function getIndexerForProject(directory) {
@@ -10586,14 +10998,14 @@ function getIndexerForProject(directory) {
10586
10998
  }
10587
10999
  return indexer;
10588
11000
  }
10589
- var codebase_peek = (0, import_plugin.tool)({
11001
+ var codebase_peek = (0, import_plugin2.tool)({
10590
11002
  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.",
10591
11003
  args: {
10592
- query: z.string().describe("Natural language description of what code you're looking for."),
10593
- limit: z.number().optional().default(10).describe("Maximum number of results to return"),
10594
- fileType: z.string().optional().describe("Filter by file extension (e.g., 'ts', 'py', 'rs')"),
10595
- directory: z.string().optional().describe("Filter by directory path (e.g., 'src/utils', 'lib')"),
10596
- chunkType: z.enum(["function", "class", "method", "interface", "type", "enum", "struct", "impl", "trait", "module", "other"]).optional().describe("Filter by code chunk type")
11004
+ query: z2.string().describe("Natural language description of what code you're looking for."),
11005
+ limit: z2.number().optional().default(10).describe("Maximum number of results to return"),
11006
+ fileType: z2.string().optional().describe("Filter by file extension (e.g., 'ts', 'py', 'rs')"),
11007
+ directory: z2.string().optional().describe("Filter by directory path (e.g., 'src/utils', 'lib')"),
11008
+ chunkType: z2.enum(["function", "class", "method", "interface", "type", "enum", "struct", "impl", "trait", "module", "other"]).optional().describe("Filter by code chunk type")
10597
11009
  },
10598
11010
  async execute(args, context) {
10599
11011
  const indexer = getIndexerForProject(context?.worktree);
@@ -10606,12 +11018,12 @@ var codebase_peek = (0, import_plugin.tool)({
10606
11018
  return formatCodebasePeek(results);
10607
11019
  }
10608
11020
  });
10609
- var index_codebase = (0, import_plugin.tool)({
11021
+ var index_codebase = (0, import_plugin2.tool)({
10610
11022
  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.",
10611
11023
  args: {
10612
- force: z.boolean().optional().default(false).describe("Force reindex even if already indexed"),
10613
- estimateOnly: z.boolean().optional().default(false).describe("Only show cost estimate without indexing"),
10614
- verbose: z.boolean().optional().default(false).describe("Show detailed info about skipped files and parsing failures")
11024
+ force: z2.boolean().optional().default(false).describe("Force reindex even if already indexed"),
11025
+ estimateOnly: z2.boolean().optional().default(false).describe("Only show cost estimate without indexing"),
11026
+ verbose: z2.boolean().optional().default(false).describe("Show detailed info about skipped files and parsing failures")
10615
11027
  },
10616
11028
  async execute(args, context) {
10617
11029
  const projectRoot = context?.worktree || defaultProjectRoot;
@@ -10644,7 +11056,7 @@ var index_codebase = (0, import_plugin.tool)({
10644
11056
  return formatIndexStats(stats, args.verbose ?? false);
10645
11057
  }
10646
11058
  });
10647
- var index_status = (0, import_plugin.tool)({
11059
+ var index_status = (0, import_plugin2.tool)({
10648
11060
  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.",
10649
11061
  args: {},
10650
11062
  async execute(_args, context) {
@@ -10653,7 +11065,7 @@ var index_status = (0, import_plugin.tool)({
10653
11065
  return formatStatus(status);
10654
11066
  }
10655
11067
  });
10656
- var index_health_check = (0, import_plugin.tool)({
11068
+ var index_health_check = (0, import_plugin2.tool)({
10657
11069
  description: "Check index health and remove stale entries from deleted files. Run this to clean up the index after files have been deleted.",
10658
11070
  args: {},
10659
11071
  async execute(_args, context) {
@@ -10662,7 +11074,7 @@ var index_health_check = (0, import_plugin.tool)({
10662
11074
  return formatHealthCheck(result);
10663
11075
  }
10664
11076
  });
10665
- var index_metrics = (0, import_plugin.tool)({
11077
+ var index_metrics = (0, import_plugin2.tool)({
10666
11078
  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.",
10667
11079
  args: {},
10668
11080
  async execute(_args, context) {
@@ -10677,12 +11089,12 @@ var index_metrics = (0, import_plugin.tool)({
10677
11089
  return logger.formatMetrics();
10678
11090
  }
10679
11091
  });
10680
- var index_logs = (0, import_plugin.tool)({
11092
+ var index_logs = (0, import_plugin2.tool)({
10681
11093
  description: "Get recent debug logs from the codebase indexer. Shows timestamped log entries with level and category. Requires debug.enabled=true in config.",
10682
11094
  args: {
10683
- limit: z.number().optional().default(20).describe("Maximum number of log entries to return"),
10684
- category: z.enum(["search", "embedding", "cache", "gc", "branch", "general"]).optional().describe("Filter by log category"),
10685
- level: z.enum(["error", "warn", "info", "debug"]).optional().describe("Filter by minimum log level")
11095
+ limit: z2.number().optional().default(20).describe("Maximum number of log entries to return"),
11096
+ category: z2.enum(["search", "embedding", "cache", "gc", "branch", "general"]).optional().describe("Filter by log category"),
11097
+ level: z2.enum(["error", "warn", "info", "debug"]).optional().describe("Filter by minimum log level")
10686
11098
  },
10687
11099
  async execute(args, context) {
10688
11100
  const indexer = getIndexerForProject(context?.worktree);
@@ -10701,15 +11113,15 @@ var index_logs = (0, import_plugin.tool)({
10701
11113
  return formatLogs(logs);
10702
11114
  }
10703
11115
  });
10704
- var find_similar = (0, import_plugin.tool)({
11116
+ var find_similar = (0, import_plugin2.tool)({
10705
11117
  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.",
10706
11118
  args: {
10707
- code: z.string().describe("The code snippet to find similar code for"),
10708
- limit: z.number().optional().default(10).describe("Maximum number of results to return"),
10709
- fileType: z.string().optional().describe("Filter by file extension (e.g., 'ts', 'py', 'rs')"),
10710
- directory: z.string().optional().describe("Filter by directory path (e.g., 'src/utils', 'lib')"),
10711
- chunkType: z.enum(["function", "class", "method", "interface", "type", "enum", "struct", "impl", "trait", "module", "other"]).optional().describe("Filter by code chunk type"),
10712
- excludeFile: z.string().optional().describe("Exclude results from this file path (useful when searching for duplicates of code from a specific file)")
11119
+ code: z2.string().describe("The code snippet to find similar code for"),
11120
+ limit: z2.number().optional().default(10).describe("Maximum number of results to return"),
11121
+ fileType: z2.string().optional().describe("Filter by file extension (e.g., 'ts', 'py', 'rs')"),
11122
+ directory: z2.string().optional().describe("Filter by directory path (e.g., 'src/utils', 'lib')"),
11123
+ chunkType: z2.enum(["function", "class", "method", "interface", "type", "enum", "struct", "impl", "trait", "module", "other"]).optional().describe("Filter by code chunk type"),
11124
+ excludeFile: z2.string().optional().describe("Exclude results from this file path (useful when searching for duplicates of code from a specific file)")
10713
11125
  },
10714
11126
  async execute(args, context) {
10715
11127
  const indexer = getIndexerForProject(context?.worktree);
@@ -10725,15 +11137,15 @@ var find_similar = (0, import_plugin.tool)({
10725
11137
  return formatSearchResults(results);
10726
11138
  }
10727
11139
  });
10728
- var codebase_search = (0, import_plugin.tool)({
11140
+ var codebase_search = (0, import_plugin2.tool)({
10729
11141
  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.",
10730
11142
  args: {
10731
- query: z.string().describe("Natural language description of what code you're looking for. Describe behavior, not syntax."),
10732
- limit: z.number().optional().default(5).describe("Maximum number of results to return"),
10733
- fileType: z.string().optional().describe("Filter by file extension (e.g., 'ts', 'py', 'rs')"),
10734
- directory: z.string().optional().describe("Filter by directory path (e.g., 'src/utils', 'lib')"),
10735
- chunkType: z.enum(["function", "class", "method", "interface", "type", "enum", "struct", "impl", "trait", "module", "other"]).optional().describe("Filter by code chunk type"),
10736
- contextLines: z.number().optional().describe("Number of extra lines to include before/after each match (default: 0)")
11143
+ query: z2.string().describe("Natural language description of what code you're looking for. Describe behavior, not syntax."),
11144
+ limit: z2.number().optional().default(5).describe("Maximum number of results to return"),
11145
+ fileType: z2.string().optional().describe("Filter by file extension (e.g., 'ts', 'py', 'rs')"),
11146
+ directory: z2.string().optional().describe("Filter by directory path (e.g., 'src/utils', 'lib')"),
11147
+ chunkType: z2.enum(["function", "class", "method", "interface", "type", "enum", "struct", "impl", "trait", "module", "other"]).optional().describe("Filter by code chunk type"),
11148
+ contextLines: z2.number().optional().describe("Number of extra lines to include before/after each match (default: 0)")
10737
11149
  },
10738
11150
  async execute(args, context) {
10739
11151
  const indexer = getIndexerForProject(context?.worktree);
@@ -10749,13 +11161,13 @@ var codebase_search = (0, import_plugin.tool)({
10749
11161
  return formatSearchResults(results, "score");
10750
11162
  }
10751
11163
  });
10752
- var implementation_lookup = (0, import_plugin.tool)({
11164
+ var implementation_lookup = (0, import_plugin2.tool)({
10753
11165
  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.",
10754
11166
  args: {
10755
- query: z.string().describe("Symbol name or natural language description (e.g., 'validateToken', 'where is the payment handler defined')"),
10756
- limit: z.number().optional().default(5).describe("Maximum number of results"),
10757
- fileType: z.string().optional().describe("Filter by file extension (e.g., 'ts', 'py')"),
10758
- directory: z.string().optional().describe("Filter by directory path (e.g., 'src/utils')")
11167
+ query: z2.string().describe("Symbol name or natural language description (e.g., 'validateToken', 'where is the payment handler defined')"),
11168
+ limit: z2.number().optional().default(5).describe("Maximum number of results"),
11169
+ fileType: z2.string().optional().describe("Filter by file extension (e.g., 'ts', 'py')"),
11170
+ directory: z2.string().optional().describe("Filter by directory path (e.g., 'src/utils')")
10759
11171
  },
10760
11172
  async execute(args, context) {
10761
11173
  const indexer = getIndexerForProject(context?.worktree);
@@ -10767,13 +11179,13 @@ var implementation_lookup = (0, import_plugin.tool)({
10767
11179
  return formatDefinitionLookup(results, args.query);
10768
11180
  }
10769
11181
  });
10770
- var call_graph = (0, import_plugin.tool)({
11182
+ var call_graph = (0, import_plugin2.tool)({
10771
11183
  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.",
10772
11184
  args: {
10773
- name: z.string().describe("Function or method name to query"),
10774
- direction: z.enum(["callers", "callees"]).default("callers").describe("Direction: 'callers' finds who calls this function, 'callees' finds what this function calls"),
10775
- symbolId: z.string().optional().describe("Symbol ID (required for 'callees' direction, returned by previous call_graph queries)"),
10776
- relationshipType: z.enum(["Call", "MethodCall", "Constructor", "Import", "Inherits", "Implements"]).optional().describe("Filter by relationship type. Omit to show all.")
11185
+ name: z2.string().describe("Function or method name to query"),
11186
+ direction: z2.enum(["callers", "callees"]).default("callers").describe("Direction: 'callers' finds who calls this function, 'callees' finds what this function calls"),
11187
+ symbolId: z2.string().optional().describe("Symbol ID (required for 'callees' direction, returned by previous call_graph queries)"),
11188
+ relationshipType: z2.enum(["Call", "MethodCall", "Constructor", "Import", "Inherits", "Implements"]).optional().describe("Filter by relationship type. Omit to show all.")
10777
11189
  },
10778
11190
  async execute(args, context) {
10779
11191
  const indexer = getIndexerForProject(context?.worktree);
@@ -10802,38 +11214,38 @@ var call_graph = (0, import_plugin.tool)({
10802
11214
  return formatted.join("\n");
10803
11215
  }
10804
11216
  });
10805
- var call_graph_path = (0, import_plugin.tool)({
11217
+ var call_graph_path = (0, import_plugin2.tool)({
10806
11218
  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.",
10807
11219
  args: {
10808
- from: z.string().describe("Source function/method name (starting point)"),
10809
- to: z.string().describe("Target function/method name (destination)"),
10810
- maxDepth: z.number().optional().default(10).describe("Maximum traversal depth (default: 10)")
11220
+ from: z2.string().describe("Source function/method name (starting point)"),
11221
+ to: z2.string().describe("Target function/method name (destination)"),
11222
+ maxDepth: z2.number().optional().default(10).describe("Maximum traversal depth (default: 10)")
10811
11223
  },
10812
11224
  async execute(args, context) {
10813
11225
  const indexer = getIndexerForProject(context?.worktree);
10814
- const path18 = await indexer.findCallPath(args.from, args.to, args.maxDepth);
10815
- if (path18.length === 0) {
11226
+ const path19 = await indexer.findCallPath(args.from, args.to, args.maxDepth);
11227
+ if (path19.length === 0) {
10816
11228
  return `No path found between "${args.from}" and "${args.to}". They may be in disconnected components, or the call graph index needs updating.`;
10817
11229
  }
10818
- const formatted = path18.map((hop, i) => {
11230
+ const formatted = path19.map((hop, i) => {
10819
11231
  const prefix = i === 0 ? "[start]" : `--${hop.callType}-->`;
10820
11232
  const location = hop.filePath ? ` (${hop.filePath}:${hop.line})` : "";
10821
11233
  return `${prefix} ${hop.symbolName}${location}`;
10822
11234
  });
10823
- return `Path (${path18.length} hops):
11235
+ return `Path (${path19.length} hops):
10824
11236
  ${formatted.join("\n")}`;
10825
11237
  }
10826
11238
  });
10827
- var add_knowledge_base = (0, import_plugin.tool)({
11239
+ var add_knowledge_base = (0, import_plugin2.tool)({
10828
11240
  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).",
10829
11241
  args: {
10830
- path: z.string().describe("Path to the folder to add as a knowledge base (absolute or relative to project root)")
11242
+ path: z2.string().describe("Path to the folder to add as a knowledge base (absolute or relative to project root)")
10831
11243
  },
10832
11244
  async execute(args, context) {
10833
11245
  const projectRoot = context?.worktree || defaultProjectRoot;
10834
11246
  const inputPath = args.path.trim();
10835
- const normalizedPath = path15.resolve(
10836
- path15.isAbsolute(inputPath) ? inputPath : resolveKnowledgeBasePath(inputPath, projectRoot)
11247
+ const normalizedPath = path16.resolve(
11248
+ path16.isAbsolute(inputPath) ? inputPath : resolveKnowledgeBasePath(inputPath, projectRoot)
10837
11249
  );
10838
11250
  if (!(0, import_fs9.existsSync)(normalizedPath)) {
10839
11251
  return `Error: Directory does not exist: ${normalizedPath}`;
@@ -10862,7 +11274,7 @@ var add_knowledge_base = (0, import_plugin.tool)({
10862
11274
  }
10863
11275
  }
10864
11276
  for (const dotDir of sensitiveDotDirs) {
10865
- const sensitiveDir = path15.join(homeDir, dotDir);
11277
+ const sensitiveDir = path16.join(homeDir, dotDir);
10866
11278
  if (realPath === sensitiveDir || realPath.startsWith(sensitiveDir + "/")) {
10867
11279
  return `Error: Adding sensitive directory as knowledge base is not allowed: ${normalizedPath}`;
10868
11280
  }
@@ -10896,7 +11308,7 @@ Run /index to rebuild the index with the new knowledge base.`;
10896
11308
  return result;
10897
11309
  }
10898
11310
  });
10899
- var list_knowledge_bases = (0, import_plugin.tool)({
11311
+ var list_knowledge_bases = (0, import_plugin2.tool)({
10900
11312
  description: "List all configured knowledge base folders that are indexed alongside the main project.",
10901
11313
  args: {},
10902
11314
  async execute(_args, context) {
@@ -10933,10 +11345,10 @@ var list_knowledge_bases = (0, import_plugin.tool)({
10933
11345
  return result;
10934
11346
  }
10935
11347
  });
10936
- var remove_knowledge_base = (0, import_plugin.tool)({
11348
+ var remove_knowledge_base = (0, import_plugin2.tool)({
10937
11349
  description: "Remove a knowledge base folder from the semantic search index.",
10938
11350
  args: {
10939
- path: z.string().describe("Path of the knowledge base to remove (must match the configured path exactly)")
11351
+ path: z2.string().describe("Path of the knowledge base to remove (must match the configured path exactly)")
10940
11352
  },
10941
11353
  async execute(args, context) {
10942
11354
  const projectRoot = context?.worktree || defaultProjectRoot;
@@ -10978,7 +11390,7 @@ Run /index to rebuild the index without the removed knowledge base.`;
10978
11390
 
10979
11391
  // src/commands/loader.ts
10980
11392
  var import_fs10 = require("fs");
10981
- var path16 = __toESM(require("path"), 1);
11393
+ var path17 = __toESM(require("path"), 1);
10982
11394
  function parseFrontmatter(content) {
10983
11395
  const frontmatterRegex = /^---\s*\n([\s\S]*?)\n---\s*\n([\s\S]*)$/;
10984
11396
  const match = content.match(frontmatterRegex);
@@ -11004,7 +11416,7 @@ function loadCommandsFromDirectory(commandsDir) {
11004
11416
  }
11005
11417
  const files = (0, import_fs10.readdirSync)(commandsDir).filter((f) => f.endsWith(".md"));
11006
11418
  for (const file of files) {
11007
- const filePath = path16.join(commandsDir, file);
11419
+ const filePath = path17.join(commandsDir, file);
11008
11420
  let content;
11009
11421
  try {
11010
11422
  content = (0, import_fs10.readFileSync)(filePath, "utf-8");
@@ -11013,7 +11425,7 @@ function loadCommandsFromDirectory(commandsDir) {
11013
11425
  throw new Error(`Failed to load command file ${filePath}: ${message}`);
11014
11426
  }
11015
11427
  const { frontmatter, body } = parseFrontmatter(content);
11016
- const name = path16.basename(file, ".md");
11428
+ const name = path17.basename(file, ".md");
11017
11429
  const description = frontmatter.description || `Run the ${name} command`;
11018
11430
  commands.set(name, {
11019
11431
  description,
@@ -11235,7 +11647,7 @@ function assessRoutingIntent(text) {
11235
11647
  reason: "no_local_discovery_signal"
11236
11648
  };
11237
11649
  }
11238
- function buildRoutingHint(assessment, status) {
11650
+ function buildRoutingHint(assessment, status, includeGraphHandoff = false) {
11239
11651
  if (assessment.intent === "definition_lookup") {
11240
11652
  if (!status || !status.indexed || status.compatibility?.compatible === false) {
11241
11653
  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.";
@@ -11246,17 +11658,21 @@ function buildRoutingHint(assessment, status) {
11246
11658
  return null;
11247
11659
  }
11248
11660
  if (!status || !status.indexed || status.compatibility?.compatible === false) {
11249
- 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.";
11661
+ const graphHandoff2 = includeGraphHandoff ? " Use graph tools after semantic discovery identifies relevant symbols." : "";
11662
+ 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.`;
11250
11663
  }
11251
- 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.";
11664
+ const graphHandoff = includeGraphHandoff ? " before graph tools such as `call_graph`, `call_graph_path`, `pr_impact`, or OMO CodeGraph" : "";
11665
+ 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.`;
11252
11666
  }
11253
11667
  var RoutingHintController = class {
11254
- constructor(getStatus, maxSessions = 200) {
11668
+ constructor(getStatus, maxSessions = 200, includeGraphHandoff = false) {
11255
11669
  this.getStatus = getStatus;
11256
11670
  this.maxSessions = maxSessions;
11671
+ this.includeGraphHandoff = includeGraphHandoff;
11257
11672
  }
11258
11673
  getStatus;
11259
11674
  maxSessions;
11675
+ includeGraphHandoff;
11260
11676
  sessionState = /* @__PURE__ */ new Map();
11261
11677
  observeUserMessage(sessionID, parts) {
11262
11678
  const assessment = assessRoutingIntent(extractUserText(parts));
@@ -11277,7 +11693,7 @@ var RoutingHintController = class {
11277
11693
  return [];
11278
11694
  }
11279
11695
  const status = await this.safeGetStatus();
11280
- const hint = buildRoutingHint(state.assessment, status);
11696
+ const hint = buildRoutingHint(state.assessment, status, this.includeGraphHandoff);
11281
11697
  return hint ? [hint] : [];
11282
11698
  }
11283
11699
  markToolUsed(sessionID, toolName) {
@@ -11328,9 +11744,9 @@ function replaceActiveWatcher(projectRoot, nextWatcher) {
11328
11744
  function getCommandsDir() {
11329
11745
  let currentDir = process.cwd();
11330
11746
  if (typeof import_meta2 !== "undefined" && import_meta2.url) {
11331
- currentDir = path17.dirname((0, import_url2.fileURLToPath)(import_meta2.url));
11747
+ currentDir = path18.dirname((0, import_url2.fileURLToPath)(import_meta2.url));
11332
11748
  }
11333
- return path17.join(currentDir, "..", "commands");
11749
+ return path18.join(currentDir, "..", "commands");
11334
11750
  }
11335
11751
  function appendRoutingHints(output, hints, preferredRole) {
11336
11752
  const preferredBucket = preferredRole === "developer" ? output.developer : output.system;
@@ -11349,8 +11765,8 @@ var plugin = async ({ directory, worktree }) => {
11349
11765
  const config = parseConfig(rawConfig);
11350
11766
  initializeTools(projectRoot, config);
11351
11767
  const getProjectIndexer = () => getIndexerForProject(projectRoot);
11352
- const routingHints = config.search.routingHints ? new RoutingHintController(() => getProjectIndexer().getStatus()) : null;
11353
- const isHomeDir = path17.resolve(projectRoot) === path17.resolve(os6.homedir());
11768
+ const routingHints = config.search.routingHints ? new RoutingHintController(() => getProjectIndexer().getStatus(), 200, config.search.routingGraphHandoffHints) : null;
11769
+ const isHomeDir = path18.resolve(projectRoot) === path18.resolve(os6.homedir());
11354
11770
  const isValidProject = !isHomeDir && (!config.indexing.requireProjectMarker || hasProjectMarker(projectRoot));
11355
11771
  if (isHomeDir) {
11356
11772
  console.warn(
@@ -11389,7 +11805,8 @@ var plugin = async ({ directory, worktree }) => {
11389
11805
  implementation_lookup,
11390
11806
  add_knowledge_base,
11391
11807
  list_knowledge_bases,
11392
- remove_knowledge_base
11808
+ remove_knowledge_base,
11809
+ pr_impact
11393
11810
  },
11394
11811
  async "chat.message"(input, output) {
11395
11812
  routingHints?.observeUserMessage(input.sessionID, output.parts);