opencode-codebase-index 0.10.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/README.md +42 -3
- package/commands/call-graph.md +9 -5
- package/commands/pr-impact.md +23 -0
- package/dist/cli.cjs +635 -178
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +635 -178
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +805 -338
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +805 -338
- package/dist/index.js.map +1 -1
- package/native/codebase-index-native.darwin-arm64.node +0 -0
- package/native/codebase-index-native.darwin-x64.node +0 -0
- package/native/codebase-index-native.linux-arm64-gnu.node +0 -0
- package/native/codebase-index-native.linux-x64-gnu.node +0 -0
- package/native/codebase-index-native.win32-x64-msvc.node +0 -0
- package/package.json +4 -3
- package/skill/SKILL.md +6 -3
package/dist/index.cjs
CHANGED
|
@@ -7,7 +7,11 @@ var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
|
7
7
|
var __getProtoOf = Object.getPrototypeOf;
|
|
8
8
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
9
|
var __commonJS = (cb, mod) => function __require() {
|
|
10
|
-
|
|
10
|
+
try {
|
|
11
|
+
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
12
|
+
} catch (e) {
|
|
13
|
+
throw mod = 0, e;
|
|
14
|
+
}
|
|
11
15
|
};
|
|
12
16
|
var __export = (target, all) => {
|
|
13
17
|
for (var name in all)
|
|
@@ -329,7 +333,7 @@ var require_ignore = __commonJS({
|
|
|
329
333
|
// path matching.
|
|
330
334
|
// - check `string` either `MODE_IGNORE` or `MODE_CHECK_IGNORE`
|
|
331
335
|
// @returns {TestResult} true if a file is ignored
|
|
332
|
-
test(
|
|
336
|
+
test(path19, checkUnignored, mode) {
|
|
333
337
|
let ignored = false;
|
|
334
338
|
let unignored = false;
|
|
335
339
|
let matchedRule;
|
|
@@ -338,7 +342,7 @@ var require_ignore = __commonJS({
|
|
|
338
342
|
if (unignored === negative && ignored !== unignored || negative && !ignored && !unignored && !checkUnignored) {
|
|
339
343
|
return;
|
|
340
344
|
}
|
|
341
|
-
const matched = rule[mode].test(
|
|
345
|
+
const matched = rule[mode].test(path19);
|
|
342
346
|
if (!matched) {
|
|
343
347
|
return;
|
|
344
348
|
}
|
|
@@ -359,17 +363,17 @@ var require_ignore = __commonJS({
|
|
|
359
363
|
var throwError = (message, Ctor) => {
|
|
360
364
|
throw new Ctor(message);
|
|
361
365
|
};
|
|
362
|
-
var checkPath = (
|
|
363
|
-
if (!isString(
|
|
366
|
+
var checkPath = (path19, originalPath, doThrow) => {
|
|
367
|
+
if (!isString(path19)) {
|
|
364
368
|
return doThrow(
|
|
365
369
|
`path must be a string, but got \`${originalPath}\``,
|
|
366
370
|
TypeError
|
|
367
371
|
);
|
|
368
372
|
}
|
|
369
|
-
if (!
|
|
373
|
+
if (!path19) {
|
|
370
374
|
return doThrow(`path must not be empty`, TypeError);
|
|
371
375
|
}
|
|
372
|
-
if (checkPath.isNotRelative(
|
|
376
|
+
if (checkPath.isNotRelative(path19)) {
|
|
373
377
|
const r = "`path.relative()`d";
|
|
374
378
|
return doThrow(
|
|
375
379
|
`path should be a ${r} string, but got "${originalPath}"`,
|
|
@@ -378,7 +382,7 @@ var require_ignore = __commonJS({
|
|
|
378
382
|
}
|
|
379
383
|
return true;
|
|
380
384
|
};
|
|
381
|
-
var isNotRelative = (
|
|
385
|
+
var isNotRelative = (path19) => REGEX_TEST_INVALID_PATH.test(path19);
|
|
382
386
|
checkPath.isNotRelative = isNotRelative;
|
|
383
387
|
checkPath.convert = (p) => p;
|
|
384
388
|
var Ignore2 = class {
|
|
@@ -408,19 +412,19 @@ var require_ignore = __commonJS({
|
|
|
408
412
|
}
|
|
409
413
|
// @returns {TestResult}
|
|
410
414
|
_test(originalPath, cache, checkUnignored, slices) {
|
|
411
|
-
const
|
|
415
|
+
const path19 = originalPath && checkPath.convert(originalPath);
|
|
412
416
|
checkPath(
|
|
413
|
-
|
|
417
|
+
path19,
|
|
414
418
|
originalPath,
|
|
415
419
|
this._strictPathCheck ? throwError : RETURN_FALSE
|
|
416
420
|
);
|
|
417
|
-
return this._t(
|
|
421
|
+
return this._t(path19, cache, checkUnignored, slices);
|
|
418
422
|
}
|
|
419
|
-
checkIgnore(
|
|
420
|
-
if (!REGEX_TEST_TRAILING_SLASH.test(
|
|
421
|
-
return this.test(
|
|
423
|
+
checkIgnore(path19) {
|
|
424
|
+
if (!REGEX_TEST_TRAILING_SLASH.test(path19)) {
|
|
425
|
+
return this.test(path19);
|
|
422
426
|
}
|
|
423
|
-
const slices =
|
|
427
|
+
const slices = path19.split(SLASH2).filter(Boolean);
|
|
424
428
|
slices.pop();
|
|
425
429
|
if (slices.length) {
|
|
426
430
|
const parent = this._t(
|
|
@@ -433,18 +437,18 @@ var require_ignore = __commonJS({
|
|
|
433
437
|
return parent;
|
|
434
438
|
}
|
|
435
439
|
}
|
|
436
|
-
return this._rules.test(
|
|
440
|
+
return this._rules.test(path19, false, MODE_CHECK_IGNORE);
|
|
437
441
|
}
|
|
438
|
-
_t(
|
|
439
|
-
if (
|
|
440
|
-
return cache[
|
|
442
|
+
_t(path19, cache, checkUnignored, slices) {
|
|
443
|
+
if (path19 in cache) {
|
|
444
|
+
return cache[path19];
|
|
441
445
|
}
|
|
442
446
|
if (!slices) {
|
|
443
|
-
slices =
|
|
447
|
+
slices = path19.split(SLASH2).filter(Boolean);
|
|
444
448
|
}
|
|
445
449
|
slices.pop();
|
|
446
450
|
if (!slices.length) {
|
|
447
|
-
return cache[
|
|
451
|
+
return cache[path19] = this._rules.test(path19, checkUnignored, MODE_IGNORE);
|
|
448
452
|
}
|
|
449
453
|
const parent = this._t(
|
|
450
454
|
slices.join(SLASH2) + SLASH2,
|
|
@@ -452,29 +456,29 @@ var require_ignore = __commonJS({
|
|
|
452
456
|
checkUnignored,
|
|
453
457
|
slices
|
|
454
458
|
);
|
|
455
|
-
return cache[
|
|
459
|
+
return cache[path19] = parent.ignored ? parent : this._rules.test(path19, checkUnignored, MODE_IGNORE);
|
|
456
460
|
}
|
|
457
|
-
ignores(
|
|
458
|
-
return this._test(
|
|
461
|
+
ignores(path19) {
|
|
462
|
+
return this._test(path19, this._ignoreCache, false).ignored;
|
|
459
463
|
}
|
|
460
464
|
createFilter() {
|
|
461
|
-
return (
|
|
465
|
+
return (path19) => !this.ignores(path19);
|
|
462
466
|
}
|
|
463
467
|
filter(paths) {
|
|
464
468
|
return makeArray(paths).filter(this.createFilter());
|
|
465
469
|
}
|
|
466
470
|
// @returns {TestResult}
|
|
467
|
-
test(
|
|
468
|
-
return this._test(
|
|
471
|
+
test(path19) {
|
|
472
|
+
return this._test(path19, this._testCache, true);
|
|
469
473
|
}
|
|
470
474
|
};
|
|
471
475
|
var factory = (options) => new Ignore2(options);
|
|
472
|
-
var isPathValid = (
|
|
476
|
+
var isPathValid = (path19) => checkPath(path19 && checkPath.convert(path19), path19, RETURN_FALSE);
|
|
473
477
|
var setupWindows = () => {
|
|
474
478
|
const makePosix = (str) => /^\\\\\?\\/.test(str) || /["<>|\u0000-\u001F]+/u.test(str) ? str : str.replace(/\\/g, "/");
|
|
475
479
|
checkPath.convert = makePosix;
|
|
476
480
|
const REGEX_TEST_WINDOWS_PATH_ABSOLUTE = /^[a-z]:\//i;
|
|
477
|
-
checkPath.isNotRelative = (
|
|
481
|
+
checkPath.isNotRelative = (path19) => REGEX_TEST_WINDOWS_PATH_ABSOLUTE.test(path19) || isNotRelative(path19);
|
|
478
482
|
};
|
|
479
483
|
if (
|
|
480
484
|
// Detect `process` so that it can run in browsers.
|
|
@@ -658,7 +662,7 @@ __export(index_exports, {
|
|
|
658
662
|
});
|
|
659
663
|
module.exports = __toCommonJS(index_exports);
|
|
660
664
|
var os6 = __toESM(require("os"), 1);
|
|
661
|
-
var
|
|
665
|
+
var path18 = __toESM(require("path"), 1);
|
|
662
666
|
var import_url2 = require("url");
|
|
663
667
|
|
|
664
668
|
// src/config/constants.ts
|
|
@@ -805,6 +809,7 @@ function getDefaultSearchConfig() {
|
|
|
805
809
|
rerankTopN: 20,
|
|
806
810
|
contextLines: 0,
|
|
807
811
|
routingHints: true,
|
|
812
|
+
routingGraphHandoffHints: false,
|
|
808
813
|
routingHintRole: "system"
|
|
809
814
|
};
|
|
810
815
|
}
|
|
@@ -927,6 +932,7 @@ function parseConfig(raw) {
|
|
|
927
932
|
rerankTopN: typeof rawSearch.rerankTopN === "number" ? Math.min(200, Math.max(0, Math.floor(rawSearch.rerankTopN))) : defaultSearch.rerankTopN,
|
|
928
933
|
contextLines: typeof rawSearch.contextLines === "number" ? Math.min(50, Math.max(0, rawSearch.contextLines)) : defaultSearch.contextLines,
|
|
929
934
|
routingHints: typeof rawSearch.routingHints === "boolean" ? rawSearch.routingHints : defaultSearch.routingHints,
|
|
935
|
+
routingGraphHandoffHints: typeof rawSearch.routingGraphHandoffHints === "boolean" ? rawSearch.routingGraphHandoffHints : defaultSearch.routingGraphHandoffHints,
|
|
930
936
|
routingHintRole: rawSearch.routingHintRole === "developer" || rawSearch.routingHintRole === "system" ? rawSearch.routingHintRole : defaultSearch.routingHintRole
|
|
931
937
|
};
|
|
932
938
|
const rawDebug = input.debug && typeof input.debug === "object" ? input.debug : {};
|
|
@@ -1553,7 +1559,7 @@ var ReaddirpStream = class extends import_node_stream.Readable {
|
|
|
1553
1559
|
this._directoryFilter = normalizeFilter(opts.directoryFilter);
|
|
1554
1560
|
const statMethod = opts.lstat ? import_promises.lstat : import_promises.stat;
|
|
1555
1561
|
if (wantBigintFsStats) {
|
|
1556
|
-
this._stat = (
|
|
1562
|
+
this._stat = (path19) => statMethod(path19, { bigint: true });
|
|
1557
1563
|
} else {
|
|
1558
1564
|
this._stat = statMethod;
|
|
1559
1565
|
}
|
|
@@ -1578,8 +1584,8 @@ var ReaddirpStream = class extends import_node_stream.Readable {
|
|
|
1578
1584
|
const par = this.parent;
|
|
1579
1585
|
const fil = par && par.files;
|
|
1580
1586
|
if (fil && fil.length > 0) {
|
|
1581
|
-
const { path:
|
|
1582
|
-
const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent,
|
|
1587
|
+
const { path: path19, depth } = par;
|
|
1588
|
+
const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path19));
|
|
1583
1589
|
const awaited = await Promise.all(slice);
|
|
1584
1590
|
for (const entry of awaited) {
|
|
1585
1591
|
if (!entry)
|
|
@@ -1619,20 +1625,20 @@ var ReaddirpStream = class extends import_node_stream.Readable {
|
|
|
1619
1625
|
this.reading = false;
|
|
1620
1626
|
}
|
|
1621
1627
|
}
|
|
1622
|
-
async _exploreDir(
|
|
1628
|
+
async _exploreDir(path19, depth) {
|
|
1623
1629
|
let files;
|
|
1624
1630
|
try {
|
|
1625
|
-
files = await (0, import_promises.readdir)(
|
|
1631
|
+
files = await (0, import_promises.readdir)(path19, this._rdOptions);
|
|
1626
1632
|
} catch (error) {
|
|
1627
1633
|
this._onError(error);
|
|
1628
1634
|
}
|
|
1629
|
-
return { files, depth, path:
|
|
1635
|
+
return { files, depth, path: path19 };
|
|
1630
1636
|
}
|
|
1631
|
-
async _formatEntry(dirent,
|
|
1637
|
+
async _formatEntry(dirent, path19) {
|
|
1632
1638
|
let entry;
|
|
1633
1639
|
const basename5 = this._isDirent ? dirent.name : dirent;
|
|
1634
1640
|
try {
|
|
1635
|
-
const fullPath = (0, import_node_path.resolve)((0, import_node_path.join)(
|
|
1641
|
+
const fullPath = (0, import_node_path.resolve)((0, import_node_path.join)(path19, basename5));
|
|
1636
1642
|
entry = { path: (0, import_node_path.relative)(this._root, fullPath), fullPath, basename: basename5 };
|
|
1637
1643
|
entry[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath);
|
|
1638
1644
|
} catch (err) {
|
|
@@ -2032,16 +2038,16 @@ var delFromSet = (main, prop, item) => {
|
|
|
2032
2038
|
};
|
|
2033
2039
|
var isEmptySet = (val) => val instanceof Set ? val.size === 0 : !val;
|
|
2034
2040
|
var FsWatchInstances = /* @__PURE__ */ new Map();
|
|
2035
|
-
function createFsWatchInstance(
|
|
2041
|
+
function createFsWatchInstance(path19, options, listener, errHandler, emitRaw) {
|
|
2036
2042
|
const handleEvent = (rawEvent, evPath) => {
|
|
2037
|
-
listener(
|
|
2038
|
-
emitRaw(rawEvent, evPath, { watchedPath:
|
|
2039
|
-
if (evPath &&
|
|
2040
|
-
fsWatchBroadcast(sp.resolve(
|
|
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));
|
|
2041
2047
|
}
|
|
2042
2048
|
};
|
|
2043
2049
|
try {
|
|
2044
|
-
return (0, import_node_fs.watch)(
|
|
2050
|
+
return (0, import_node_fs.watch)(path19, {
|
|
2045
2051
|
persistent: options.persistent
|
|
2046
2052
|
}, handleEvent);
|
|
2047
2053
|
} catch (error) {
|
|
@@ -2057,12 +2063,12 @@ var fsWatchBroadcast = (fullPath, listenerType, val1, val2, val3) => {
|
|
|
2057
2063
|
listener(val1, val2, val3);
|
|
2058
2064
|
});
|
|
2059
2065
|
};
|
|
2060
|
-
var setFsWatchListener = (
|
|
2066
|
+
var setFsWatchListener = (path19, fullPath, options, handlers) => {
|
|
2061
2067
|
const { listener, errHandler, rawEmitter } = handlers;
|
|
2062
2068
|
let cont = FsWatchInstances.get(fullPath);
|
|
2063
2069
|
let watcher;
|
|
2064
2070
|
if (!options.persistent) {
|
|
2065
|
-
watcher = createFsWatchInstance(
|
|
2071
|
+
watcher = createFsWatchInstance(path19, options, listener, errHandler, rawEmitter);
|
|
2066
2072
|
if (!watcher)
|
|
2067
2073
|
return;
|
|
2068
2074
|
return watcher.close.bind(watcher);
|
|
@@ -2073,7 +2079,7 @@ var setFsWatchListener = (path18, fullPath, options, handlers) => {
|
|
|
2073
2079
|
addAndConvert(cont, KEY_RAW, rawEmitter);
|
|
2074
2080
|
} else {
|
|
2075
2081
|
watcher = createFsWatchInstance(
|
|
2076
|
-
|
|
2082
|
+
path19,
|
|
2077
2083
|
options,
|
|
2078
2084
|
fsWatchBroadcast.bind(null, fullPath, KEY_LISTENERS),
|
|
2079
2085
|
errHandler,
|
|
@@ -2088,7 +2094,7 @@ var setFsWatchListener = (path18, fullPath, options, handlers) => {
|
|
|
2088
2094
|
cont.watcherUnusable = true;
|
|
2089
2095
|
if (isWindows && error.code === "EPERM") {
|
|
2090
2096
|
try {
|
|
2091
|
-
const fd = await (0, import_promises2.open)(
|
|
2097
|
+
const fd = await (0, import_promises2.open)(path19, "r");
|
|
2092
2098
|
await fd.close();
|
|
2093
2099
|
broadcastErr(error);
|
|
2094
2100
|
} catch (err) {
|
|
@@ -2119,7 +2125,7 @@ var setFsWatchListener = (path18, fullPath, options, handlers) => {
|
|
|
2119
2125
|
};
|
|
2120
2126
|
};
|
|
2121
2127
|
var FsWatchFileInstances = /* @__PURE__ */ new Map();
|
|
2122
|
-
var setFsWatchFileListener = (
|
|
2128
|
+
var setFsWatchFileListener = (path19, fullPath, options, handlers) => {
|
|
2123
2129
|
const { listener, rawEmitter } = handlers;
|
|
2124
2130
|
let cont = FsWatchFileInstances.get(fullPath);
|
|
2125
2131
|
const copts = cont && cont.options;
|
|
@@ -2141,7 +2147,7 @@ var setFsWatchFileListener = (path18, fullPath, options, handlers) => {
|
|
|
2141
2147
|
});
|
|
2142
2148
|
const currmtime = curr.mtimeMs;
|
|
2143
2149
|
if (curr.size !== prev.size || currmtime > prev.mtimeMs || currmtime === 0) {
|
|
2144
|
-
foreach(cont.listeners, (listener2) => listener2(
|
|
2150
|
+
foreach(cont.listeners, (listener2) => listener2(path19, curr));
|
|
2145
2151
|
}
|
|
2146
2152
|
})
|
|
2147
2153
|
};
|
|
@@ -2171,13 +2177,13 @@ var NodeFsHandler = class {
|
|
|
2171
2177
|
* @param listener on fs change
|
|
2172
2178
|
* @returns closer for the watcher instance
|
|
2173
2179
|
*/
|
|
2174
|
-
_watchWithNodeFs(
|
|
2180
|
+
_watchWithNodeFs(path19, listener) {
|
|
2175
2181
|
const opts = this.fsw.options;
|
|
2176
|
-
const directory = sp.dirname(
|
|
2177
|
-
const basename5 = sp.basename(
|
|
2182
|
+
const directory = sp.dirname(path19);
|
|
2183
|
+
const basename5 = sp.basename(path19);
|
|
2178
2184
|
const parent = this.fsw._getWatchedDir(directory);
|
|
2179
2185
|
parent.add(basename5);
|
|
2180
|
-
const absolutePath = sp.resolve(
|
|
2186
|
+
const absolutePath = sp.resolve(path19);
|
|
2181
2187
|
const options = {
|
|
2182
2188
|
persistent: opts.persistent
|
|
2183
2189
|
};
|
|
@@ -2187,12 +2193,12 @@ var NodeFsHandler = class {
|
|
|
2187
2193
|
if (opts.usePolling) {
|
|
2188
2194
|
const enableBin = opts.interval !== opts.binaryInterval;
|
|
2189
2195
|
options.interval = enableBin && isBinaryPath(basename5) ? opts.binaryInterval : opts.interval;
|
|
2190
|
-
closer = setFsWatchFileListener(
|
|
2196
|
+
closer = setFsWatchFileListener(path19, absolutePath, options, {
|
|
2191
2197
|
listener,
|
|
2192
2198
|
rawEmitter: this.fsw._emitRaw
|
|
2193
2199
|
});
|
|
2194
2200
|
} else {
|
|
2195
|
-
closer = setFsWatchListener(
|
|
2201
|
+
closer = setFsWatchListener(path19, absolutePath, options, {
|
|
2196
2202
|
listener,
|
|
2197
2203
|
errHandler: this._boundHandleError,
|
|
2198
2204
|
rawEmitter: this.fsw._emitRaw
|
|
@@ -2214,7 +2220,7 @@ var NodeFsHandler = class {
|
|
|
2214
2220
|
let prevStats = stats;
|
|
2215
2221
|
if (parent.has(basename5))
|
|
2216
2222
|
return;
|
|
2217
|
-
const listener = async (
|
|
2223
|
+
const listener = async (path19, newStats) => {
|
|
2218
2224
|
if (!this.fsw._throttle(THROTTLE_MODE_WATCH, file, 5))
|
|
2219
2225
|
return;
|
|
2220
2226
|
if (!newStats || newStats.mtimeMs === 0) {
|
|
@@ -2228,11 +2234,11 @@ var NodeFsHandler = class {
|
|
|
2228
2234
|
this.fsw._emit(EV.CHANGE, file, newStats2);
|
|
2229
2235
|
}
|
|
2230
2236
|
if ((isMacos || isLinux || isFreeBSD) && prevStats.ino !== newStats2.ino) {
|
|
2231
|
-
this.fsw._closeFile(
|
|
2237
|
+
this.fsw._closeFile(path19);
|
|
2232
2238
|
prevStats = newStats2;
|
|
2233
2239
|
const closer2 = this._watchWithNodeFs(file, listener);
|
|
2234
2240
|
if (closer2)
|
|
2235
|
-
this.fsw._addPathCloser(
|
|
2241
|
+
this.fsw._addPathCloser(path19, closer2);
|
|
2236
2242
|
} else {
|
|
2237
2243
|
prevStats = newStats2;
|
|
2238
2244
|
}
|
|
@@ -2264,7 +2270,7 @@ var NodeFsHandler = class {
|
|
|
2264
2270
|
* @param item basename of this item
|
|
2265
2271
|
* @returns true if no more processing is needed for this entry.
|
|
2266
2272
|
*/
|
|
2267
|
-
async _handleSymlink(entry, directory,
|
|
2273
|
+
async _handleSymlink(entry, directory, path19, item) {
|
|
2268
2274
|
if (this.fsw.closed) {
|
|
2269
2275
|
return;
|
|
2270
2276
|
}
|
|
@@ -2274,7 +2280,7 @@ var NodeFsHandler = class {
|
|
|
2274
2280
|
this.fsw._incrReadyCount();
|
|
2275
2281
|
let linkPath;
|
|
2276
2282
|
try {
|
|
2277
|
-
linkPath = await (0, import_promises2.realpath)(
|
|
2283
|
+
linkPath = await (0, import_promises2.realpath)(path19);
|
|
2278
2284
|
} catch (e) {
|
|
2279
2285
|
this.fsw._emitReady();
|
|
2280
2286
|
return true;
|
|
@@ -2284,12 +2290,12 @@ var NodeFsHandler = class {
|
|
|
2284
2290
|
if (dir.has(item)) {
|
|
2285
2291
|
if (this.fsw._symlinkPaths.get(full) !== linkPath) {
|
|
2286
2292
|
this.fsw._symlinkPaths.set(full, linkPath);
|
|
2287
|
-
this.fsw._emit(EV.CHANGE,
|
|
2293
|
+
this.fsw._emit(EV.CHANGE, path19, entry.stats);
|
|
2288
2294
|
}
|
|
2289
2295
|
} else {
|
|
2290
2296
|
dir.add(item);
|
|
2291
2297
|
this.fsw._symlinkPaths.set(full, linkPath);
|
|
2292
|
-
this.fsw._emit(EV.ADD,
|
|
2298
|
+
this.fsw._emit(EV.ADD, path19, entry.stats);
|
|
2293
2299
|
}
|
|
2294
2300
|
this.fsw._emitReady();
|
|
2295
2301
|
return true;
|
|
@@ -2319,9 +2325,9 @@ var NodeFsHandler = class {
|
|
|
2319
2325
|
return;
|
|
2320
2326
|
}
|
|
2321
2327
|
const item = entry.path;
|
|
2322
|
-
let
|
|
2328
|
+
let path19 = sp.join(directory, item);
|
|
2323
2329
|
current.add(item);
|
|
2324
|
-
if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory,
|
|
2330
|
+
if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path19, item)) {
|
|
2325
2331
|
return;
|
|
2326
2332
|
}
|
|
2327
2333
|
if (this.fsw.closed) {
|
|
@@ -2330,11 +2336,11 @@ var NodeFsHandler = class {
|
|
|
2330
2336
|
}
|
|
2331
2337
|
if (item === target || !target && !previous.has(item)) {
|
|
2332
2338
|
this.fsw._incrReadyCount();
|
|
2333
|
-
|
|
2334
|
-
this._addToNodeFs(
|
|
2339
|
+
path19 = sp.join(dir, sp.relative(dir, path19));
|
|
2340
|
+
this._addToNodeFs(path19, initialAdd, wh, depth + 1);
|
|
2335
2341
|
}
|
|
2336
2342
|
}).on(EV.ERROR, this._boundHandleError);
|
|
2337
|
-
return new Promise((
|
|
2343
|
+
return new Promise((resolve13, reject) => {
|
|
2338
2344
|
if (!stream)
|
|
2339
2345
|
return reject();
|
|
2340
2346
|
stream.once(STR_END, () => {
|
|
@@ -2343,7 +2349,7 @@ var NodeFsHandler = class {
|
|
|
2343
2349
|
return;
|
|
2344
2350
|
}
|
|
2345
2351
|
const wasThrottled = throttler ? throttler.clear() : false;
|
|
2346
|
-
|
|
2352
|
+
resolve13(void 0);
|
|
2347
2353
|
previous.getChildren().filter((item) => {
|
|
2348
2354
|
return item !== directory && !current.has(item);
|
|
2349
2355
|
}).forEach((item) => {
|
|
@@ -2400,13 +2406,13 @@ var NodeFsHandler = class {
|
|
|
2400
2406
|
* @param depth Child path actually targeted for watch
|
|
2401
2407
|
* @param target Child path actually targeted for watch
|
|
2402
2408
|
*/
|
|
2403
|
-
async _addToNodeFs(
|
|
2409
|
+
async _addToNodeFs(path19, initialAdd, priorWh, depth, target) {
|
|
2404
2410
|
const ready = this.fsw._emitReady;
|
|
2405
|
-
if (this.fsw._isIgnored(
|
|
2411
|
+
if (this.fsw._isIgnored(path19) || this.fsw.closed) {
|
|
2406
2412
|
ready();
|
|
2407
2413
|
return false;
|
|
2408
2414
|
}
|
|
2409
|
-
const wh = this.fsw._getWatchHelpers(
|
|
2415
|
+
const wh = this.fsw._getWatchHelpers(path19);
|
|
2410
2416
|
if (priorWh) {
|
|
2411
2417
|
wh.filterPath = (entry) => priorWh.filterPath(entry);
|
|
2412
2418
|
wh.filterDir = (entry) => priorWh.filterDir(entry);
|
|
@@ -2422,8 +2428,8 @@ var NodeFsHandler = class {
|
|
|
2422
2428
|
const follow = this.fsw.options.followSymlinks;
|
|
2423
2429
|
let closer;
|
|
2424
2430
|
if (stats.isDirectory()) {
|
|
2425
|
-
const absPath = sp.resolve(
|
|
2426
|
-
const targetPath = follow ? await (0, import_promises2.realpath)(
|
|
2431
|
+
const absPath = sp.resolve(path19);
|
|
2432
|
+
const targetPath = follow ? await (0, import_promises2.realpath)(path19) : path19;
|
|
2427
2433
|
if (this.fsw.closed)
|
|
2428
2434
|
return;
|
|
2429
2435
|
closer = await this._handleDir(wh.watchPath, stats, initialAdd, depth, target, wh, targetPath);
|
|
@@ -2433,29 +2439,29 @@ var NodeFsHandler = class {
|
|
|
2433
2439
|
this.fsw._symlinkPaths.set(absPath, targetPath);
|
|
2434
2440
|
}
|
|
2435
2441
|
} else if (stats.isSymbolicLink()) {
|
|
2436
|
-
const targetPath = follow ? await (0, import_promises2.realpath)(
|
|
2442
|
+
const targetPath = follow ? await (0, import_promises2.realpath)(path19) : path19;
|
|
2437
2443
|
if (this.fsw.closed)
|
|
2438
2444
|
return;
|
|
2439
2445
|
const parent = sp.dirname(wh.watchPath);
|
|
2440
2446
|
this.fsw._getWatchedDir(parent).add(wh.watchPath);
|
|
2441
2447
|
this.fsw._emit(EV.ADD, wh.watchPath, stats);
|
|
2442
|
-
closer = await this._handleDir(parent, stats, initialAdd, depth,
|
|
2448
|
+
closer = await this._handleDir(parent, stats, initialAdd, depth, path19, wh, targetPath);
|
|
2443
2449
|
if (this.fsw.closed)
|
|
2444
2450
|
return;
|
|
2445
2451
|
if (targetPath !== void 0) {
|
|
2446
|
-
this.fsw._symlinkPaths.set(sp.resolve(
|
|
2452
|
+
this.fsw._symlinkPaths.set(sp.resolve(path19), targetPath);
|
|
2447
2453
|
}
|
|
2448
2454
|
} else {
|
|
2449
2455
|
closer = this._handleFile(wh.watchPath, stats, initialAdd);
|
|
2450
2456
|
}
|
|
2451
2457
|
ready();
|
|
2452
2458
|
if (closer)
|
|
2453
|
-
this.fsw._addPathCloser(
|
|
2459
|
+
this.fsw._addPathCloser(path19, closer);
|
|
2454
2460
|
return false;
|
|
2455
2461
|
} catch (error) {
|
|
2456
2462
|
if (this.fsw._handleError(error)) {
|
|
2457
2463
|
ready();
|
|
2458
|
-
return
|
|
2464
|
+
return path19;
|
|
2459
2465
|
}
|
|
2460
2466
|
}
|
|
2461
2467
|
}
|
|
@@ -2487,35 +2493,35 @@ function createPattern(matcher) {
|
|
|
2487
2493
|
if (matcher.path === string)
|
|
2488
2494
|
return true;
|
|
2489
2495
|
if (matcher.recursive) {
|
|
2490
|
-
const
|
|
2491
|
-
if (!
|
|
2496
|
+
const relative9 = sp2.relative(matcher.path, string);
|
|
2497
|
+
if (!relative9) {
|
|
2492
2498
|
return false;
|
|
2493
2499
|
}
|
|
2494
|
-
return !
|
|
2500
|
+
return !relative9.startsWith("..") && !sp2.isAbsolute(relative9);
|
|
2495
2501
|
}
|
|
2496
2502
|
return false;
|
|
2497
2503
|
};
|
|
2498
2504
|
}
|
|
2499
2505
|
return () => false;
|
|
2500
2506
|
}
|
|
2501
|
-
function normalizePath(
|
|
2502
|
-
if (typeof
|
|
2507
|
+
function normalizePath(path19) {
|
|
2508
|
+
if (typeof path19 !== "string")
|
|
2503
2509
|
throw new Error("string expected");
|
|
2504
|
-
|
|
2505
|
-
|
|
2510
|
+
path19 = sp2.normalize(path19);
|
|
2511
|
+
path19 = path19.replace(/\\/g, "/");
|
|
2506
2512
|
let prepend = false;
|
|
2507
|
-
if (
|
|
2513
|
+
if (path19.startsWith("//"))
|
|
2508
2514
|
prepend = true;
|
|
2509
|
-
|
|
2515
|
+
path19 = path19.replace(DOUBLE_SLASH_RE, "/");
|
|
2510
2516
|
if (prepend)
|
|
2511
|
-
|
|
2512
|
-
return
|
|
2517
|
+
path19 = "/" + path19;
|
|
2518
|
+
return path19;
|
|
2513
2519
|
}
|
|
2514
2520
|
function matchPatterns(patterns, testString, stats) {
|
|
2515
|
-
const
|
|
2521
|
+
const path19 = normalizePath(testString);
|
|
2516
2522
|
for (let index = 0; index < patterns.length; index++) {
|
|
2517
2523
|
const pattern = patterns[index];
|
|
2518
|
-
if (pattern(
|
|
2524
|
+
if (pattern(path19, stats)) {
|
|
2519
2525
|
return true;
|
|
2520
2526
|
}
|
|
2521
2527
|
}
|
|
@@ -2553,19 +2559,19 @@ var toUnix = (string) => {
|
|
|
2553
2559
|
}
|
|
2554
2560
|
return str;
|
|
2555
2561
|
};
|
|
2556
|
-
var normalizePathToUnix = (
|
|
2557
|
-
var normalizeIgnored = (cwd = "") => (
|
|
2558
|
-
if (typeof
|
|
2559
|
-
return normalizePathToUnix(sp2.isAbsolute(
|
|
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));
|
|
2560
2566
|
} else {
|
|
2561
|
-
return
|
|
2567
|
+
return path19;
|
|
2562
2568
|
}
|
|
2563
2569
|
};
|
|
2564
|
-
var getAbsolutePath = (
|
|
2565
|
-
if (sp2.isAbsolute(
|
|
2566
|
-
return
|
|
2570
|
+
var getAbsolutePath = (path19, cwd) => {
|
|
2571
|
+
if (sp2.isAbsolute(path19)) {
|
|
2572
|
+
return path19;
|
|
2567
2573
|
}
|
|
2568
|
-
return sp2.join(cwd,
|
|
2574
|
+
return sp2.join(cwd, path19);
|
|
2569
2575
|
};
|
|
2570
2576
|
var EMPTY_SET = Object.freeze(/* @__PURE__ */ new Set());
|
|
2571
2577
|
var DirEntry = class {
|
|
@@ -2630,10 +2636,10 @@ var WatchHelper = class {
|
|
|
2630
2636
|
dirParts;
|
|
2631
2637
|
followSymlinks;
|
|
2632
2638
|
statMethod;
|
|
2633
|
-
constructor(
|
|
2639
|
+
constructor(path19, follow, fsw) {
|
|
2634
2640
|
this.fsw = fsw;
|
|
2635
|
-
const watchPath =
|
|
2636
|
-
this.path =
|
|
2641
|
+
const watchPath = path19;
|
|
2642
|
+
this.path = path19 = path19.replace(REPLACER_RE, "");
|
|
2637
2643
|
this.watchPath = watchPath;
|
|
2638
2644
|
this.fullWatchPath = sp2.resolve(watchPath);
|
|
2639
2645
|
this.dirParts = [];
|
|
@@ -2773,20 +2779,20 @@ var FSWatcher = class extends import_node_events.EventEmitter {
|
|
|
2773
2779
|
this._closePromise = void 0;
|
|
2774
2780
|
let paths = unifyPaths(paths_);
|
|
2775
2781
|
if (cwd) {
|
|
2776
|
-
paths = paths.map((
|
|
2777
|
-
const absPath = getAbsolutePath(
|
|
2782
|
+
paths = paths.map((path19) => {
|
|
2783
|
+
const absPath = getAbsolutePath(path19, cwd);
|
|
2778
2784
|
return absPath;
|
|
2779
2785
|
});
|
|
2780
2786
|
}
|
|
2781
|
-
paths.forEach((
|
|
2782
|
-
this._removeIgnoredPath(
|
|
2787
|
+
paths.forEach((path19) => {
|
|
2788
|
+
this._removeIgnoredPath(path19);
|
|
2783
2789
|
});
|
|
2784
2790
|
this._userIgnored = void 0;
|
|
2785
2791
|
if (!this._readyCount)
|
|
2786
2792
|
this._readyCount = 0;
|
|
2787
2793
|
this._readyCount += paths.length;
|
|
2788
|
-
Promise.all(paths.map(async (
|
|
2789
|
-
const res = await this._nodeFsHandler._addToNodeFs(
|
|
2794
|
+
Promise.all(paths.map(async (path19) => {
|
|
2795
|
+
const res = await this._nodeFsHandler._addToNodeFs(path19, !_internal, void 0, 0, _origAdd);
|
|
2790
2796
|
if (res)
|
|
2791
2797
|
this._emitReady();
|
|
2792
2798
|
return res;
|
|
@@ -2808,17 +2814,17 @@ var FSWatcher = class extends import_node_events.EventEmitter {
|
|
|
2808
2814
|
return this;
|
|
2809
2815
|
const paths = unifyPaths(paths_);
|
|
2810
2816
|
const { cwd } = this.options;
|
|
2811
|
-
paths.forEach((
|
|
2812
|
-
if (!sp2.isAbsolute(
|
|
2817
|
+
paths.forEach((path19) => {
|
|
2818
|
+
if (!sp2.isAbsolute(path19) && !this._closers.has(path19)) {
|
|
2813
2819
|
if (cwd)
|
|
2814
|
-
|
|
2815
|
-
|
|
2820
|
+
path19 = sp2.join(cwd, path19);
|
|
2821
|
+
path19 = sp2.resolve(path19);
|
|
2816
2822
|
}
|
|
2817
|
-
this._closePath(
|
|
2818
|
-
this._addIgnoredPath(
|
|
2819
|
-
if (this._watched.has(
|
|
2823
|
+
this._closePath(path19);
|
|
2824
|
+
this._addIgnoredPath(path19);
|
|
2825
|
+
if (this._watched.has(path19)) {
|
|
2820
2826
|
this._addIgnoredPath({
|
|
2821
|
-
path:
|
|
2827
|
+
path: path19,
|
|
2822
2828
|
recursive: true
|
|
2823
2829
|
});
|
|
2824
2830
|
}
|
|
@@ -2882,38 +2888,38 @@ var FSWatcher = class extends import_node_events.EventEmitter {
|
|
|
2882
2888
|
* @param stats arguments to be passed with event
|
|
2883
2889
|
* @returns the error if defined, otherwise the value of the FSWatcher instance's `closed` flag
|
|
2884
2890
|
*/
|
|
2885
|
-
async _emit(event,
|
|
2891
|
+
async _emit(event, path19, stats) {
|
|
2886
2892
|
if (this.closed)
|
|
2887
2893
|
return;
|
|
2888
2894
|
const opts = this.options;
|
|
2889
2895
|
if (isWindows)
|
|
2890
|
-
|
|
2896
|
+
path19 = sp2.normalize(path19);
|
|
2891
2897
|
if (opts.cwd)
|
|
2892
|
-
|
|
2893
|
-
const args = [
|
|
2898
|
+
path19 = sp2.relative(opts.cwd, path19);
|
|
2899
|
+
const args = [path19];
|
|
2894
2900
|
if (stats != null)
|
|
2895
2901
|
args.push(stats);
|
|
2896
2902
|
const awf = opts.awaitWriteFinish;
|
|
2897
2903
|
let pw;
|
|
2898
|
-
if (awf && (pw = this._pendingWrites.get(
|
|
2904
|
+
if (awf && (pw = this._pendingWrites.get(path19))) {
|
|
2899
2905
|
pw.lastChange = /* @__PURE__ */ new Date();
|
|
2900
2906
|
return this;
|
|
2901
2907
|
}
|
|
2902
2908
|
if (opts.atomic) {
|
|
2903
2909
|
if (event === EVENTS.UNLINK) {
|
|
2904
|
-
this._pendingUnlinks.set(
|
|
2910
|
+
this._pendingUnlinks.set(path19, [event, ...args]);
|
|
2905
2911
|
setTimeout(() => {
|
|
2906
|
-
this._pendingUnlinks.forEach((entry,
|
|
2912
|
+
this._pendingUnlinks.forEach((entry, path20) => {
|
|
2907
2913
|
this.emit(...entry);
|
|
2908
2914
|
this.emit(EVENTS.ALL, ...entry);
|
|
2909
|
-
this._pendingUnlinks.delete(
|
|
2915
|
+
this._pendingUnlinks.delete(path20);
|
|
2910
2916
|
});
|
|
2911
2917
|
}, typeof opts.atomic === "number" ? opts.atomic : 100);
|
|
2912
2918
|
return this;
|
|
2913
2919
|
}
|
|
2914
|
-
if (event === EVENTS.ADD && this._pendingUnlinks.has(
|
|
2920
|
+
if (event === EVENTS.ADD && this._pendingUnlinks.has(path19)) {
|
|
2915
2921
|
event = EVENTS.CHANGE;
|
|
2916
|
-
this._pendingUnlinks.delete(
|
|
2922
|
+
this._pendingUnlinks.delete(path19);
|
|
2917
2923
|
}
|
|
2918
2924
|
}
|
|
2919
2925
|
if (awf && (event === EVENTS.ADD || event === EVENTS.CHANGE) && this._readyEmitted) {
|
|
@@ -2931,16 +2937,16 @@ var FSWatcher = class extends import_node_events.EventEmitter {
|
|
|
2931
2937
|
this.emitWithAll(event, args);
|
|
2932
2938
|
}
|
|
2933
2939
|
};
|
|
2934
|
-
this._awaitWriteFinish(
|
|
2940
|
+
this._awaitWriteFinish(path19, awf.stabilityThreshold, event, awfEmit);
|
|
2935
2941
|
return this;
|
|
2936
2942
|
}
|
|
2937
2943
|
if (event === EVENTS.CHANGE) {
|
|
2938
|
-
const isThrottled = !this._throttle(EVENTS.CHANGE,
|
|
2944
|
+
const isThrottled = !this._throttle(EVENTS.CHANGE, path19, 50);
|
|
2939
2945
|
if (isThrottled)
|
|
2940
2946
|
return this;
|
|
2941
2947
|
}
|
|
2942
2948
|
if (opts.alwaysStat && stats === void 0 && (event === EVENTS.ADD || event === EVENTS.ADD_DIR || event === EVENTS.CHANGE)) {
|
|
2943
|
-
const fullPath = opts.cwd ? sp2.join(opts.cwd,
|
|
2949
|
+
const fullPath = opts.cwd ? sp2.join(opts.cwd, path19) : path19;
|
|
2944
2950
|
let stats2;
|
|
2945
2951
|
try {
|
|
2946
2952
|
stats2 = await (0, import_promises3.stat)(fullPath);
|
|
@@ -2971,23 +2977,23 @@ var FSWatcher = class extends import_node_events.EventEmitter {
|
|
|
2971
2977
|
* @param timeout duration of time to suppress duplicate actions
|
|
2972
2978
|
* @returns tracking object or false if action should be suppressed
|
|
2973
2979
|
*/
|
|
2974
|
-
_throttle(actionType,
|
|
2980
|
+
_throttle(actionType, path19, timeout) {
|
|
2975
2981
|
if (!this._throttled.has(actionType)) {
|
|
2976
2982
|
this._throttled.set(actionType, /* @__PURE__ */ new Map());
|
|
2977
2983
|
}
|
|
2978
2984
|
const action = this._throttled.get(actionType);
|
|
2979
2985
|
if (!action)
|
|
2980
2986
|
throw new Error("invalid throttle");
|
|
2981
|
-
const actionPath = action.get(
|
|
2987
|
+
const actionPath = action.get(path19);
|
|
2982
2988
|
if (actionPath) {
|
|
2983
2989
|
actionPath.count++;
|
|
2984
2990
|
return false;
|
|
2985
2991
|
}
|
|
2986
2992
|
let timeoutObject;
|
|
2987
2993
|
const clear = () => {
|
|
2988
|
-
const item = action.get(
|
|
2994
|
+
const item = action.get(path19);
|
|
2989
2995
|
const count = item ? item.count : 0;
|
|
2990
|
-
action.delete(
|
|
2996
|
+
action.delete(path19);
|
|
2991
2997
|
clearTimeout(timeoutObject);
|
|
2992
2998
|
if (item)
|
|
2993
2999
|
clearTimeout(item.timeoutObject);
|
|
@@ -2995,7 +3001,7 @@ var FSWatcher = class extends import_node_events.EventEmitter {
|
|
|
2995
3001
|
};
|
|
2996
3002
|
timeoutObject = setTimeout(clear, timeout);
|
|
2997
3003
|
const thr = { timeoutObject, clear, count: 0 };
|
|
2998
|
-
action.set(
|
|
3004
|
+
action.set(path19, thr);
|
|
2999
3005
|
return thr;
|
|
3000
3006
|
}
|
|
3001
3007
|
_incrReadyCount() {
|
|
@@ -3009,44 +3015,44 @@ var FSWatcher = class extends import_node_events.EventEmitter {
|
|
|
3009
3015
|
* @param event
|
|
3010
3016
|
* @param awfEmit Callback to be called when ready for event to be emitted.
|
|
3011
3017
|
*/
|
|
3012
|
-
_awaitWriteFinish(
|
|
3018
|
+
_awaitWriteFinish(path19, threshold, event, awfEmit) {
|
|
3013
3019
|
const awf = this.options.awaitWriteFinish;
|
|
3014
3020
|
if (typeof awf !== "object")
|
|
3015
3021
|
return;
|
|
3016
3022
|
const pollInterval = awf.pollInterval;
|
|
3017
3023
|
let timeoutHandler;
|
|
3018
|
-
let fullPath =
|
|
3019
|
-
if (this.options.cwd && !sp2.isAbsolute(
|
|
3020
|
-
fullPath = sp2.join(this.options.cwd,
|
|
3024
|
+
let fullPath = path19;
|
|
3025
|
+
if (this.options.cwd && !sp2.isAbsolute(path19)) {
|
|
3026
|
+
fullPath = sp2.join(this.options.cwd, path19);
|
|
3021
3027
|
}
|
|
3022
3028
|
const now = /* @__PURE__ */ new Date();
|
|
3023
3029
|
const writes = this._pendingWrites;
|
|
3024
3030
|
function awaitWriteFinishFn(prevStat) {
|
|
3025
3031
|
(0, import_node_fs2.stat)(fullPath, (err, curStat) => {
|
|
3026
|
-
if (err || !writes.has(
|
|
3032
|
+
if (err || !writes.has(path19)) {
|
|
3027
3033
|
if (err && err.code !== "ENOENT")
|
|
3028
3034
|
awfEmit(err);
|
|
3029
3035
|
return;
|
|
3030
3036
|
}
|
|
3031
3037
|
const now2 = Number(/* @__PURE__ */ new Date());
|
|
3032
3038
|
if (prevStat && curStat.size !== prevStat.size) {
|
|
3033
|
-
writes.get(
|
|
3039
|
+
writes.get(path19).lastChange = now2;
|
|
3034
3040
|
}
|
|
3035
|
-
const pw = writes.get(
|
|
3041
|
+
const pw = writes.get(path19);
|
|
3036
3042
|
const df = now2 - pw.lastChange;
|
|
3037
3043
|
if (df >= threshold) {
|
|
3038
|
-
writes.delete(
|
|
3044
|
+
writes.delete(path19);
|
|
3039
3045
|
awfEmit(void 0, curStat);
|
|
3040
3046
|
} else {
|
|
3041
3047
|
timeoutHandler = setTimeout(awaitWriteFinishFn, pollInterval, curStat);
|
|
3042
3048
|
}
|
|
3043
3049
|
});
|
|
3044
3050
|
}
|
|
3045
|
-
if (!writes.has(
|
|
3046
|
-
writes.set(
|
|
3051
|
+
if (!writes.has(path19)) {
|
|
3052
|
+
writes.set(path19, {
|
|
3047
3053
|
lastChange: now,
|
|
3048
3054
|
cancelWait: () => {
|
|
3049
|
-
writes.delete(
|
|
3055
|
+
writes.delete(path19);
|
|
3050
3056
|
clearTimeout(timeoutHandler);
|
|
3051
3057
|
return event;
|
|
3052
3058
|
}
|
|
@@ -3057,8 +3063,8 @@ var FSWatcher = class extends import_node_events.EventEmitter {
|
|
|
3057
3063
|
/**
|
|
3058
3064
|
* Determines whether user has asked to ignore this path.
|
|
3059
3065
|
*/
|
|
3060
|
-
_isIgnored(
|
|
3061
|
-
if (this.options.atomic && DOT_RE.test(
|
|
3066
|
+
_isIgnored(path19, stats) {
|
|
3067
|
+
if (this.options.atomic && DOT_RE.test(path19))
|
|
3062
3068
|
return true;
|
|
3063
3069
|
if (!this._userIgnored) {
|
|
3064
3070
|
const { cwd } = this.options;
|
|
@@ -3068,17 +3074,17 @@ var FSWatcher = class extends import_node_events.EventEmitter {
|
|
|
3068
3074
|
const list = [...ignoredPaths.map(normalizeIgnored(cwd)), ...ignored];
|
|
3069
3075
|
this._userIgnored = anymatch(list, void 0);
|
|
3070
3076
|
}
|
|
3071
|
-
return this._userIgnored(
|
|
3077
|
+
return this._userIgnored(path19, stats);
|
|
3072
3078
|
}
|
|
3073
|
-
_isntIgnored(
|
|
3074
|
-
return !this._isIgnored(
|
|
3079
|
+
_isntIgnored(path19, stat4) {
|
|
3080
|
+
return !this._isIgnored(path19, stat4);
|
|
3075
3081
|
}
|
|
3076
3082
|
/**
|
|
3077
3083
|
* Provides a set of common helpers and properties relating to symlink handling.
|
|
3078
3084
|
* @param path file or directory pattern being watched
|
|
3079
3085
|
*/
|
|
3080
|
-
_getWatchHelpers(
|
|
3081
|
-
return new WatchHelper(
|
|
3086
|
+
_getWatchHelpers(path19) {
|
|
3087
|
+
return new WatchHelper(path19, this.options.followSymlinks, this);
|
|
3082
3088
|
}
|
|
3083
3089
|
// Directory helpers
|
|
3084
3090
|
// -----------------
|
|
@@ -3110,63 +3116,63 @@ var FSWatcher = class extends import_node_events.EventEmitter {
|
|
|
3110
3116
|
* @param item base path of item/directory
|
|
3111
3117
|
*/
|
|
3112
3118
|
_remove(directory, item, isDirectory) {
|
|
3113
|
-
const
|
|
3114
|
-
const fullPath = sp2.resolve(
|
|
3115
|
-
isDirectory = isDirectory != null ? isDirectory : this._watched.has(
|
|
3116
|
-
if (!this._throttle("remove",
|
|
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))
|
|
3117
3123
|
return;
|
|
3118
3124
|
if (!isDirectory && this._watched.size === 1) {
|
|
3119
3125
|
this.add(directory, item, true);
|
|
3120
3126
|
}
|
|
3121
|
-
const wp = this._getWatchedDir(
|
|
3127
|
+
const wp = this._getWatchedDir(path19);
|
|
3122
3128
|
const nestedDirectoryChildren = wp.getChildren();
|
|
3123
|
-
nestedDirectoryChildren.forEach((nested) => this._remove(
|
|
3129
|
+
nestedDirectoryChildren.forEach((nested) => this._remove(path19, nested));
|
|
3124
3130
|
const parent = this._getWatchedDir(directory);
|
|
3125
3131
|
const wasTracked = parent.has(item);
|
|
3126
3132
|
parent.remove(item);
|
|
3127
3133
|
if (this._symlinkPaths.has(fullPath)) {
|
|
3128
3134
|
this._symlinkPaths.delete(fullPath);
|
|
3129
3135
|
}
|
|
3130
|
-
let relPath =
|
|
3136
|
+
let relPath = path19;
|
|
3131
3137
|
if (this.options.cwd)
|
|
3132
|
-
relPath = sp2.relative(this.options.cwd,
|
|
3138
|
+
relPath = sp2.relative(this.options.cwd, path19);
|
|
3133
3139
|
if (this.options.awaitWriteFinish && this._pendingWrites.has(relPath)) {
|
|
3134
3140
|
const event = this._pendingWrites.get(relPath).cancelWait();
|
|
3135
3141
|
if (event === EVENTS.ADD)
|
|
3136
3142
|
return;
|
|
3137
3143
|
}
|
|
3138
|
-
this._watched.delete(
|
|
3144
|
+
this._watched.delete(path19);
|
|
3139
3145
|
this._watched.delete(fullPath);
|
|
3140
3146
|
const eventName = isDirectory ? EVENTS.UNLINK_DIR : EVENTS.UNLINK;
|
|
3141
|
-
if (wasTracked && !this._isIgnored(
|
|
3142
|
-
this._emit(eventName,
|
|
3143
|
-
this._closePath(
|
|
3147
|
+
if (wasTracked && !this._isIgnored(path19))
|
|
3148
|
+
this._emit(eventName, path19);
|
|
3149
|
+
this._closePath(path19);
|
|
3144
3150
|
}
|
|
3145
3151
|
/**
|
|
3146
3152
|
* Closes all watchers for a path
|
|
3147
3153
|
*/
|
|
3148
|
-
_closePath(
|
|
3149
|
-
this._closeFile(
|
|
3150
|
-
const dir = sp2.dirname(
|
|
3151
|
-
this._getWatchedDir(dir).remove(sp2.basename(
|
|
3154
|
+
_closePath(path19) {
|
|
3155
|
+
this._closeFile(path19);
|
|
3156
|
+
const dir = sp2.dirname(path19);
|
|
3157
|
+
this._getWatchedDir(dir).remove(sp2.basename(path19));
|
|
3152
3158
|
}
|
|
3153
3159
|
/**
|
|
3154
3160
|
* Closes only file-specific watchers
|
|
3155
3161
|
*/
|
|
3156
|
-
_closeFile(
|
|
3157
|
-
const closers = this._closers.get(
|
|
3162
|
+
_closeFile(path19) {
|
|
3163
|
+
const closers = this._closers.get(path19);
|
|
3158
3164
|
if (!closers)
|
|
3159
3165
|
return;
|
|
3160
3166
|
closers.forEach((closer) => closer());
|
|
3161
|
-
this._closers.delete(
|
|
3167
|
+
this._closers.delete(path19);
|
|
3162
3168
|
}
|
|
3163
|
-
_addPathCloser(
|
|
3169
|
+
_addPathCloser(path19, closer) {
|
|
3164
3170
|
if (!closer)
|
|
3165
3171
|
return;
|
|
3166
|
-
let list = this._closers.get(
|
|
3172
|
+
let list = this._closers.get(path19);
|
|
3167
3173
|
if (!list) {
|
|
3168
3174
|
list = [];
|
|
3169
|
-
this._closers.set(
|
|
3175
|
+
this._closers.set(path19, list);
|
|
3170
3176
|
}
|
|
3171
3177
|
list.push(closer);
|
|
3172
3178
|
}
|
|
@@ -3496,7 +3502,7 @@ var FileWatcher = class {
|
|
|
3496
3502
|
return;
|
|
3497
3503
|
}
|
|
3498
3504
|
const changes = Array.from(this.pendingChanges.entries()).map(
|
|
3499
|
-
([
|
|
3505
|
+
([path19, type]) => ({ path: path19, type })
|
|
3500
3506
|
);
|
|
3501
3507
|
this.pendingChanges.clear();
|
|
3502
3508
|
try {
|
|
@@ -3629,12 +3635,14 @@ function createWatcherWithIndexer(getIndexer, projectRoot, config) {
|
|
|
3629
3635
|
}
|
|
3630
3636
|
|
|
3631
3637
|
// src/tools/index.ts
|
|
3632
|
-
var
|
|
3638
|
+
var import_plugin2 = require("@opencode-ai/plugin");
|
|
3633
3639
|
|
|
3634
3640
|
// src/indexer/index.ts
|
|
3635
3641
|
var import_fs7 = require("fs");
|
|
3636
|
-
var
|
|
3642
|
+
var path13 = __toESM(require("path"), 1);
|
|
3637
3643
|
var import_perf_hooks = require("perf_hooks");
|
|
3644
|
+
var import_child_process2 = require("child_process");
|
|
3645
|
+
var import_util2 = require("util");
|
|
3638
3646
|
|
|
3639
3647
|
// node_modules/eventemitter3/index.mjs
|
|
3640
3648
|
var import_index = __toESM(require_eventemitter3(), 1);
|
|
@@ -3658,7 +3666,7 @@ function pTimeout(promise, options) {
|
|
|
3658
3666
|
} = options;
|
|
3659
3667
|
let timer;
|
|
3660
3668
|
let abortHandler;
|
|
3661
|
-
const wrappedPromise = new Promise((
|
|
3669
|
+
const wrappedPromise = new Promise((resolve13, reject) => {
|
|
3662
3670
|
if (typeof milliseconds !== "number" || Math.sign(milliseconds) !== 1) {
|
|
3663
3671
|
throw new TypeError(`Expected \`milliseconds\` to be a positive number, got \`${milliseconds}\``);
|
|
3664
3672
|
}
|
|
@@ -3672,7 +3680,7 @@ function pTimeout(promise, options) {
|
|
|
3672
3680
|
};
|
|
3673
3681
|
signal.addEventListener("abort", abortHandler, { once: true });
|
|
3674
3682
|
}
|
|
3675
|
-
promise.then(
|
|
3683
|
+
promise.then(resolve13, reject);
|
|
3676
3684
|
if (milliseconds === Number.POSITIVE_INFINITY) {
|
|
3677
3685
|
return;
|
|
3678
3686
|
}
|
|
@@ -3680,7 +3688,7 @@ function pTimeout(promise, options) {
|
|
|
3680
3688
|
timer = customTimers.setTimeout.call(void 0, () => {
|
|
3681
3689
|
if (fallback) {
|
|
3682
3690
|
try {
|
|
3683
|
-
|
|
3691
|
+
resolve13(fallback());
|
|
3684
3692
|
} catch (error) {
|
|
3685
3693
|
reject(error);
|
|
3686
3694
|
}
|
|
@@ -3690,7 +3698,7 @@ function pTimeout(promise, options) {
|
|
|
3690
3698
|
promise.cancel();
|
|
3691
3699
|
}
|
|
3692
3700
|
if (message === false) {
|
|
3693
|
-
|
|
3701
|
+
resolve13();
|
|
3694
3702
|
} else if (message instanceof Error) {
|
|
3695
3703
|
reject(message);
|
|
3696
3704
|
} else {
|
|
@@ -4092,7 +4100,7 @@ var PQueue = class extends import_index.default {
|
|
|
4092
4100
|
// Assign unique ID if not provided
|
|
4093
4101
|
id: options.id ?? (this.#idAssigner++).toString()
|
|
4094
4102
|
};
|
|
4095
|
-
return new Promise((
|
|
4103
|
+
return new Promise((resolve13, reject) => {
|
|
4096
4104
|
const taskSymbol = /* @__PURE__ */ Symbol(`task-${options.id}`);
|
|
4097
4105
|
let cleanupQueueAbortHandler = () => void 0;
|
|
4098
4106
|
const run = async () => {
|
|
@@ -4132,7 +4140,7 @@ var PQueue = class extends import_index.default {
|
|
|
4132
4140
|
})]);
|
|
4133
4141
|
}
|
|
4134
4142
|
const result = await operation;
|
|
4135
|
-
|
|
4143
|
+
resolve13(result);
|
|
4136
4144
|
this.emit("completed", result);
|
|
4137
4145
|
} catch (error) {
|
|
4138
4146
|
reject(error);
|
|
@@ -4320,13 +4328,13 @@ var PQueue = class extends import_index.default {
|
|
|
4320
4328
|
});
|
|
4321
4329
|
}
|
|
4322
4330
|
async #onEvent(event, filter) {
|
|
4323
|
-
return new Promise((
|
|
4331
|
+
return new Promise((resolve13) => {
|
|
4324
4332
|
const listener = () => {
|
|
4325
4333
|
if (filter && !filter()) {
|
|
4326
4334
|
return;
|
|
4327
4335
|
}
|
|
4328
4336
|
this.off(event, listener);
|
|
4329
|
-
|
|
4337
|
+
resolve13();
|
|
4330
4338
|
};
|
|
4331
4339
|
this.on(event, listener);
|
|
4332
4340
|
});
|
|
@@ -4612,7 +4620,7 @@ async function onAttemptFailure({ error, attemptNumber, retriesConsumed, startTi
|
|
|
4612
4620
|
const finalDelay = Math.min(delayTime, remainingTime);
|
|
4613
4621
|
options.signal?.throwIfAborted();
|
|
4614
4622
|
if (finalDelay > 0) {
|
|
4615
|
-
await new Promise((
|
|
4623
|
+
await new Promise((resolve13, reject) => {
|
|
4616
4624
|
const onAbort = () => {
|
|
4617
4625
|
clearTimeout(timeoutToken);
|
|
4618
4626
|
options.signal?.removeEventListener("abort", onAbort);
|
|
@@ -4620,7 +4628,7 @@ async function onAttemptFailure({ error, attemptNumber, retriesConsumed, startTi
|
|
|
4620
4628
|
};
|
|
4621
4629
|
const timeoutToken = setTimeout(() => {
|
|
4622
4630
|
options.signal?.removeEventListener("abort", onAbort);
|
|
4623
|
-
|
|
4631
|
+
resolve13();
|
|
4624
4632
|
}, finalDelay);
|
|
4625
4633
|
if (options.unref) {
|
|
4626
4634
|
timeoutToken.unref?.();
|
|
@@ -4861,6 +4869,8 @@ var BaseEmbeddingProvider = class {
|
|
|
4861
4869
|
this.credentials = credentials;
|
|
4862
4870
|
this.modelInfo = modelInfo;
|
|
4863
4871
|
}
|
|
4872
|
+
credentials;
|
|
4873
|
+
modelInfo;
|
|
4864
4874
|
async embedQuery(query) {
|
|
4865
4875
|
const result = await this.embedBatch([query]);
|
|
4866
4876
|
return {
|
|
@@ -5846,6 +5856,15 @@ function createMockNativeBinding() {
|
|
|
5846
5856
|
close() {
|
|
5847
5857
|
throw error;
|
|
5848
5858
|
}
|
|
5859
|
+
getTransitiveReachability() {
|
|
5860
|
+
throw error;
|
|
5861
|
+
}
|
|
5862
|
+
detectCommunities() {
|
|
5863
|
+
throw error;
|
|
5864
|
+
}
|
|
5865
|
+
computeCentrality() {
|
|
5866
|
+
throw error;
|
|
5867
|
+
}
|
|
5849
5868
|
}
|
|
5850
5869
|
};
|
|
5851
5870
|
}
|
|
@@ -6396,6 +6415,14 @@ var Database = class {
|
|
|
6396
6415
|
this.throwIfClosed();
|
|
6397
6416
|
return this.inner.getSymbolsByNameCi(name);
|
|
6398
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
|
+
}
|
|
6399
6426
|
deleteSymbolsByFile(filePath) {
|
|
6400
6427
|
this.throwIfClosed();
|
|
6401
6428
|
return this.inner.deleteSymbolsByFile(filePath);
|
|
@@ -6409,17 +6436,17 @@ var Database = class {
|
|
|
6409
6436
|
if (edges.length === 0) return;
|
|
6410
6437
|
this.inner.upsertCallEdgesBatch(edges);
|
|
6411
6438
|
}
|
|
6412
|
-
getCallers(targetName, branch) {
|
|
6439
|
+
getCallers(targetName, branch, callTypeFilter) {
|
|
6413
6440
|
this.throwIfClosed();
|
|
6414
|
-
return this.inner.getCallers(targetName, branch);
|
|
6441
|
+
return this.inner.getCallers(targetName, branch, callTypeFilter ?? null);
|
|
6415
6442
|
}
|
|
6416
|
-
getCallersWithContext(targetName, branch) {
|
|
6443
|
+
getCallersWithContext(targetName, branch, callTypeFilter) {
|
|
6417
6444
|
this.throwIfClosed();
|
|
6418
|
-
return this.inner.getCallersWithContext(targetName, branch);
|
|
6445
|
+
return this.inner.getCallersWithContext(targetName, branch, callTypeFilter ?? null);
|
|
6419
6446
|
}
|
|
6420
|
-
getCallees(symbolId, branch) {
|
|
6447
|
+
getCallees(symbolId, branch, callTypeFilter) {
|
|
6421
6448
|
this.throwIfClosed();
|
|
6422
|
-
return this.inner.getCallees(symbolId, branch);
|
|
6449
|
+
return this.inner.getCallees(symbolId, branch, callTypeFilter ?? null);
|
|
6423
6450
|
}
|
|
6424
6451
|
deleteCallEdgesByFile(filePath) {
|
|
6425
6452
|
this.throwIfClosed();
|
|
@@ -6429,6 +6456,10 @@ var Database = class {
|
|
|
6429
6456
|
this.throwIfClosed();
|
|
6430
6457
|
this.inner.resolveCallEdge(edgeId, toSymbolId);
|
|
6431
6458
|
}
|
|
6459
|
+
findShortestPath(fromName, toName, branch, maxDepth) {
|
|
6460
|
+
this.throwIfClosed();
|
|
6461
|
+
return this.inner.findShortestPath(fromName, toName, branch, maxDepth ?? null);
|
|
6462
|
+
}
|
|
6432
6463
|
addSymbolsToBranch(branch, symbolIds) {
|
|
6433
6464
|
this.throwIfClosed();
|
|
6434
6465
|
this.inner.addSymbolsToBranch(branch, symbolIds);
|
|
@@ -6469,8 +6500,125 @@ var Database = class {
|
|
|
6469
6500
|
this.throwIfClosed();
|
|
6470
6501
|
return this.inner.gcOrphanCallEdges();
|
|
6471
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
|
+
}
|
|
6472
6520
|
};
|
|
6473
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
|
+
|
|
6474
6622
|
// src/indexer/index.ts
|
|
6475
6623
|
var CALL_GRAPH_LANGUAGES = /* @__PURE__ */ new Set(["typescript", "tsx", "javascript", "jsx", "python", "go", "rust", "php", "apex", "zig", "gdscript", "matlab"]);
|
|
6476
6624
|
var CASE_INSENSITIVE_LANGUAGES = /* @__PURE__ */ new Set(["apex"]);
|
|
@@ -6478,6 +6626,7 @@ var CALL_GRAPH_SYMBOL_CHUNK_TYPES = /* @__PURE__ */ new Set([
|
|
|
6478
6626
|
"function_declaration",
|
|
6479
6627
|
"function",
|
|
6480
6628
|
"arrow_function",
|
|
6629
|
+
"export_statement",
|
|
6481
6630
|
"method_definition",
|
|
6482
6631
|
"class_declaration",
|
|
6483
6632
|
"interface_declaration",
|
|
@@ -6516,7 +6665,7 @@ function float32ArrayToBuffer(arr) {
|
|
|
6516
6665
|
function bufferToFloat32Array(buf) {
|
|
6517
6666
|
return new Float32Array(buf.buffer, buf.byteOffset, buf.byteLength / 4);
|
|
6518
6667
|
}
|
|
6519
|
-
function
|
|
6668
|
+
function getErrorMessage2(error) {
|
|
6520
6669
|
if (error instanceof Error) {
|
|
6521
6670
|
return error.message;
|
|
6522
6671
|
}
|
|
@@ -6529,7 +6678,7 @@ function getErrorMessage(error) {
|
|
|
6529
6678
|
return String(error);
|
|
6530
6679
|
}
|
|
6531
6680
|
function isRateLimitError(error) {
|
|
6532
|
-
const message =
|
|
6681
|
+
const message = getErrorMessage2(error);
|
|
6533
6682
|
return message.includes("429") || message.toLowerCase().includes("rate limit") || message.toLowerCase().includes("too many requests");
|
|
6534
6683
|
}
|
|
6535
6684
|
function getSafeEmbeddingChunkTokenLimit(provider) {
|
|
@@ -6547,7 +6696,7 @@ function getDynamicBatchOptions(provider) {
|
|
|
6547
6696
|
return {};
|
|
6548
6697
|
}
|
|
6549
6698
|
function isSqliteCorruptionError(error) {
|
|
6550
|
-
const message =
|
|
6699
|
+
const message = getErrorMessage2(error).toLowerCase();
|
|
6551
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");
|
|
6552
6701
|
}
|
|
6553
6702
|
var STARTUP_WARNING_METADATA_KEY = "index.startupWarning";
|
|
@@ -6709,9 +6858,9 @@ function hasAllEmbeddingParts(parts, expectedPartCount) {
|
|
|
6709
6858
|
return true;
|
|
6710
6859
|
}
|
|
6711
6860
|
function isPathWithinRoot(filePath, rootPath) {
|
|
6712
|
-
const normalizedFilePath =
|
|
6713
|
-
const normalizedRoot =
|
|
6714
|
-
return normalizedFilePath === normalizedRoot || normalizedFilePath.startsWith(`${normalizedRoot}${
|
|
6861
|
+
const normalizedFilePath = path13.resolve(filePath);
|
|
6862
|
+
const normalizedRoot = path13.resolve(rootPath);
|
|
6863
|
+
return normalizedFilePath === normalizedRoot || normalizedFilePath.startsWith(`${normalizedRoot}${path13.sep}`);
|
|
6715
6864
|
}
|
|
6716
6865
|
var rankingQueryTokenCache = /* @__PURE__ */ new Map();
|
|
6717
6866
|
var rankingNameTokenCache = /* @__PURE__ */ new Map();
|
|
@@ -7764,9 +7913,9 @@ var Indexer = class {
|
|
|
7764
7913
|
this.projectRoot = projectRoot;
|
|
7765
7914
|
this.config = config;
|
|
7766
7915
|
this.indexPath = this.getIndexPath();
|
|
7767
|
-
this.fileHashCachePath =
|
|
7768
|
-
this.failedBatchesPath =
|
|
7769
|
-
this.indexingLockPath =
|
|
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");
|
|
7770
7919
|
this.logger = initializeLogger(config.debug);
|
|
7771
7920
|
}
|
|
7772
7921
|
getIndexPath() {
|
|
@@ -7798,14 +7947,14 @@ var Indexer = class {
|
|
|
7798
7947
|
}
|
|
7799
7948
|
atomicWriteSync(targetPath, data) {
|
|
7800
7949
|
const tempPath = `${targetPath}.tmp`;
|
|
7801
|
-
(0, import_fs7.mkdirSync)(
|
|
7950
|
+
(0, import_fs7.mkdirSync)(path13.dirname(targetPath), { recursive: true });
|
|
7802
7951
|
(0, import_fs7.writeFileSync)(tempPath, data);
|
|
7803
7952
|
(0, import_fs7.renameSync)(tempPath, targetPath);
|
|
7804
7953
|
}
|
|
7805
7954
|
getScopedRoots() {
|
|
7806
|
-
const roots = /* @__PURE__ */ new Set([
|
|
7955
|
+
const roots = /* @__PURE__ */ new Set([path13.resolve(this.projectRoot)]);
|
|
7807
7956
|
for (const kbRoot of this.config.knowledgeBases) {
|
|
7808
|
-
roots.add(
|
|
7957
|
+
roots.add(path13.resolve(this.projectRoot, kbRoot));
|
|
7809
7958
|
}
|
|
7810
7959
|
return Array.from(roots);
|
|
7811
7960
|
}
|
|
@@ -7814,22 +7963,29 @@ var Indexer = class {
|
|
|
7814
7963
|
if (this.config.scope !== "global") {
|
|
7815
7964
|
return branchName;
|
|
7816
7965
|
}
|
|
7817
|
-
const projectHash = hashContent(
|
|
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);
|
|
7818
7974
|
return `${projectHash}:${branchName}`;
|
|
7819
7975
|
}
|
|
7820
7976
|
getLegacyBranchCatalogKey() {
|
|
7821
7977
|
return this.currentBranch || "default";
|
|
7822
7978
|
}
|
|
7823
7979
|
getLegacyMigrationMetadataKey() {
|
|
7824
|
-
const projectHash = hashContent(
|
|
7980
|
+
const projectHash = hashContent(path13.resolve(this.projectRoot)).slice(0, 16);
|
|
7825
7981
|
return `index.globalBranchMigration.${projectHash}`;
|
|
7826
7982
|
}
|
|
7827
7983
|
getProjectEmbeddingStrategyMetadataKey() {
|
|
7828
|
-
const projectHash = hashContent(
|
|
7984
|
+
const projectHash = hashContent(path13.resolve(this.projectRoot)).slice(0, 16);
|
|
7829
7985
|
return `index.embeddingStrategyVersion.${projectHash}`;
|
|
7830
7986
|
}
|
|
7831
7987
|
getProjectForceReembedMetadataKey() {
|
|
7832
|
-
const projectHash = hashContent(
|
|
7988
|
+
const projectHash = hashContent(path13.resolve(this.projectRoot)).slice(0, 16);
|
|
7833
7989
|
return `index.forceReembed.${projectHash}`;
|
|
7834
7990
|
}
|
|
7835
7991
|
hasProjectForceReembedPending() {
|
|
@@ -7923,7 +8079,7 @@ var Indexer = class {
|
|
|
7923
8079
|
if (!this.database) {
|
|
7924
8080
|
return { chunkIds, symbolIds };
|
|
7925
8081
|
}
|
|
7926
|
-
const projectRootPath =
|
|
8082
|
+
const projectRootPath = path13.resolve(this.projectRoot);
|
|
7927
8083
|
const projectLocalFilePaths = /* @__PURE__ */ new Set([
|
|
7928
8084
|
...Array.from(this.fileHashCache.keys()).filter(
|
|
7929
8085
|
(filePath) => this.isFileInCurrentScope(filePath, roots) && isPathWithinRoot(filePath, projectRootPath)
|
|
@@ -7946,7 +8102,7 @@ var Indexer = class {
|
|
|
7946
8102
|
if (this.config.scope !== "global") {
|
|
7947
8103
|
return this.getBranchCatalogCleanupKeys();
|
|
7948
8104
|
}
|
|
7949
|
-
const projectHash = hashContent(
|
|
8105
|
+
const projectHash = hashContent(path13.resolve(this.projectRoot)).slice(0, 16);
|
|
7950
8106
|
const keys = /* @__PURE__ */ new Set();
|
|
7951
8107
|
const projectChunkIdSet = new Set(projectChunkIds);
|
|
7952
8108
|
const projectSymbolIdSet = new Set(projectSymbolIds);
|
|
@@ -8030,7 +8186,7 @@ var Indexer = class {
|
|
|
8030
8186
|
if (!this.database || this.config.scope !== "global") {
|
|
8031
8187
|
return false;
|
|
8032
8188
|
}
|
|
8033
|
-
const projectHash = hashContent(
|
|
8189
|
+
const projectHash = hashContent(path13.resolve(this.projectRoot)).slice(0, 16);
|
|
8034
8190
|
const roots = this.getScopedRoots();
|
|
8035
8191
|
const { chunkIds: projectLocalChunkIds, symbolIds: projectLocalSymbolIds } = this.getProjectLocalScopedOwnershipIds(roots);
|
|
8036
8192
|
return this.database.getAllBranches().some(
|
|
@@ -8064,7 +8220,7 @@ var Indexer = class {
|
|
|
8064
8220
|
...Array.from(this.fileHashCache.keys()).filter((filePath) => this.isFileInCurrentScope(filePath, roots)),
|
|
8065
8221
|
...scopedEntries.map(({ metadata }) => metadata.filePath)
|
|
8066
8222
|
]);
|
|
8067
|
-
const projectRootPath =
|
|
8223
|
+
const projectRootPath = path13.resolve(this.projectRoot);
|
|
8068
8224
|
const projectLocalFilePaths = new Set(
|
|
8069
8225
|
Array.from(filePaths).filter((filePath) => isPathWithinRoot(filePath, projectRootPath))
|
|
8070
8226
|
);
|
|
@@ -8317,7 +8473,7 @@ var Indexer = class {
|
|
|
8317
8473
|
this.logger.search("warn", "External reranker failed; using deterministic order", {
|
|
8318
8474
|
provider: reranker.provider,
|
|
8319
8475
|
model: reranker.model,
|
|
8320
|
-
error:
|
|
8476
|
+
error: getErrorMessage2(error)
|
|
8321
8477
|
});
|
|
8322
8478
|
return candidates;
|
|
8323
8479
|
}
|
|
@@ -8424,13 +8580,13 @@ var Indexer = class {
|
|
|
8424
8580
|
}
|
|
8425
8581
|
await import_fs7.promises.mkdir(this.indexPath, { recursive: true });
|
|
8426
8582
|
const dimensions = this.configuredProviderInfo.modelInfo.dimensions;
|
|
8427
|
-
const storePath =
|
|
8583
|
+
const storePath = path13.join(this.indexPath, "vectors");
|
|
8428
8584
|
this.store = new VectorStore(storePath, dimensions);
|
|
8429
|
-
const indexFilePath =
|
|
8585
|
+
const indexFilePath = path13.join(this.indexPath, "vectors.usearch");
|
|
8430
8586
|
if ((0, import_fs7.existsSync)(indexFilePath)) {
|
|
8431
8587
|
this.store.load();
|
|
8432
8588
|
}
|
|
8433
|
-
const invertedIndexPath =
|
|
8589
|
+
const invertedIndexPath = path13.join(this.indexPath, "inverted-index.json");
|
|
8434
8590
|
this.invertedIndex = new InvertedIndex(invertedIndexPath);
|
|
8435
8591
|
try {
|
|
8436
8592
|
this.invertedIndex.load();
|
|
@@ -8440,7 +8596,7 @@ var Indexer = class {
|
|
|
8440
8596
|
}
|
|
8441
8597
|
this.invertedIndex = new InvertedIndex(invertedIndexPath);
|
|
8442
8598
|
}
|
|
8443
|
-
const dbPath =
|
|
8599
|
+
const dbPath = path13.join(this.indexPath, "codebase.db");
|
|
8444
8600
|
let dbIsNew = !(0, import_fs7.existsSync)(dbPath);
|
|
8445
8601
|
try {
|
|
8446
8602
|
this.database = new Database(dbPath);
|
|
@@ -8521,7 +8677,7 @@ var Indexer = class {
|
|
|
8521
8677
|
if (await this.tryResetCorruptedIndex("running automatic orphan garbage collection", error)) {
|
|
8522
8678
|
return {
|
|
8523
8679
|
resetCorruptedIndex: true,
|
|
8524
|
-
warning: this.getCorruptedIndexWarning(
|
|
8680
|
+
warning: this.getCorruptedIndexWarning(path13.join(this.indexPath, "codebase.db"))
|
|
8525
8681
|
};
|
|
8526
8682
|
}
|
|
8527
8683
|
throw error;
|
|
@@ -8536,7 +8692,7 @@ var Indexer = class {
|
|
|
8536
8692
|
return;
|
|
8537
8693
|
}
|
|
8538
8694
|
const retainedEntries = store.getAllMetadata().filter(({ key }) => !excludedSet.has(key));
|
|
8539
|
-
const storeBasePath =
|
|
8695
|
+
const storeBasePath = path13.join(this.indexPath, "vectors");
|
|
8540
8696
|
const storeIndexPath = `${storeBasePath}.usearch`;
|
|
8541
8697
|
const storeMetadataPath = `${storeBasePath}.meta.json`;
|
|
8542
8698
|
const backupIndexPath = `${storeIndexPath}.bak`;
|
|
@@ -8621,9 +8777,9 @@ var Indexer = class {
|
|
|
8621
8777
|
if (!isSqliteCorruptionError(error)) {
|
|
8622
8778
|
return false;
|
|
8623
8779
|
}
|
|
8624
|
-
const dbPath =
|
|
8780
|
+
const dbPath = path13.join(this.indexPath, "codebase.db");
|
|
8625
8781
|
const warning = this.getCorruptedIndexWarning(dbPath);
|
|
8626
|
-
const errorMessage =
|
|
8782
|
+
const errorMessage = getErrorMessage2(error);
|
|
8627
8783
|
if (this.config.scope === "global") {
|
|
8628
8784
|
this.logger.error("Detected corrupted shared global index database", {
|
|
8629
8785
|
stage,
|
|
@@ -8644,15 +8800,15 @@ var Indexer = class {
|
|
|
8644
8800
|
this.indexCompatibility = null;
|
|
8645
8801
|
this.fileHashCache.clear();
|
|
8646
8802
|
const resetPaths = [
|
|
8647
|
-
|
|
8648
|
-
|
|
8649
|
-
|
|
8650
|
-
|
|
8651
|
-
|
|
8652
|
-
|
|
8653
|
-
|
|
8654
|
-
|
|
8655
|
-
|
|
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")
|
|
8656
8812
|
];
|
|
8657
8813
|
await Promise.all(resetPaths.map(async (targetPath) => {
|
|
8658
8814
|
try {
|
|
@@ -8917,7 +9073,7 @@ var Indexer = class {
|
|
|
8917
9073
|
for (const parsed of parsedFiles) {
|
|
8918
9074
|
currentFilePaths.add(parsed.path);
|
|
8919
9075
|
if (parsed.chunks.length === 0) {
|
|
8920
|
-
const relativePath =
|
|
9076
|
+
const relativePath = path13.relative(this.projectRoot, parsed.path);
|
|
8921
9077
|
stats.parseFailures.push(relativePath);
|
|
8922
9078
|
}
|
|
8923
9079
|
let fileChunkCount = 0;
|
|
@@ -9056,6 +9212,7 @@ var Indexer = class {
|
|
|
9056
9212
|
targetName: site.calleeName,
|
|
9057
9213
|
toSymbolId: void 0,
|
|
9058
9214
|
callType: site.callType,
|
|
9215
|
+
confidence: site.confidence,
|
|
9059
9216
|
line: site.line,
|
|
9060
9217
|
col: site.column,
|
|
9061
9218
|
isResolved: false
|
|
@@ -9200,7 +9357,7 @@ var Indexer = class {
|
|
|
9200
9357
|
for (const requestBatch of requestBatches) {
|
|
9201
9358
|
queue.add(async () => {
|
|
9202
9359
|
if (rateLimitBackoffMs > 0) {
|
|
9203
|
-
await new Promise((
|
|
9360
|
+
await new Promise((resolve13) => setTimeout(resolve13, rateLimitBackoffMs));
|
|
9204
9361
|
}
|
|
9205
9362
|
try {
|
|
9206
9363
|
const result = await pRetry(
|
|
@@ -9215,7 +9372,7 @@ var Indexer = class {
|
|
|
9215
9372
|
factor: 2,
|
|
9216
9373
|
shouldRetry: (error) => !(error.error instanceof CustomProviderNonRetryableError),
|
|
9217
9374
|
onFailedAttempt: (error) => {
|
|
9218
|
-
const message =
|
|
9375
|
+
const message = getErrorMessage2(error);
|
|
9219
9376
|
if (isRateLimitError(error)) {
|
|
9220
9377
|
rateLimitBackoffMs = Math.min(providerRateLimits.maxRetryMs, (rateLimitBackoffMs || providerRateLimits.minRetryMs) * 2);
|
|
9221
9378
|
this.logger.embedding("warn", `Rate limited, backing off`, {
|
|
@@ -9322,7 +9479,7 @@ var Indexer = class {
|
|
|
9322
9479
|
});
|
|
9323
9480
|
} catch (error) {
|
|
9324
9481
|
const failedChunks = getUniquePendingChunksFromRequests(requestBatch).filter((chunk) => !completedChunkIds.has(chunk.id));
|
|
9325
|
-
const failureMessage =
|
|
9482
|
+
const failureMessage = getErrorMessage2(error);
|
|
9326
9483
|
const failureTimestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
9327
9484
|
for (const chunk of failedChunks) {
|
|
9328
9485
|
if (!failedChunkIds.has(chunk.id)) {
|
|
@@ -9761,8 +9918,8 @@ var Indexer = class {
|
|
|
9761
9918
|
this.indexCompatibility = compatibility;
|
|
9762
9919
|
return;
|
|
9763
9920
|
}
|
|
9764
|
-
const localProjectIndexPath =
|
|
9765
|
-
if (
|
|
9921
|
+
const localProjectIndexPath = path13.join(this.projectRoot, ".opencode", "index");
|
|
9922
|
+
if (path13.resolve(this.indexPath) !== path13.resolve(localProjectIndexPath)) {
|
|
9766
9923
|
throw new Error(
|
|
9767
9924
|
"Project-scoped force rebuild is unsafe while using an inherited worktree index. Create a local project config boundary before clearing the index."
|
|
9768
9925
|
);
|
|
@@ -9850,7 +10007,7 @@ var Indexer = class {
|
|
|
9850
10007
|
gcOrphanSymbols: 0,
|
|
9851
10008
|
gcOrphanCallEdges: 0,
|
|
9852
10009
|
resetCorruptedIndex: true,
|
|
9853
|
-
warning: this.getCorruptedIndexWarning(
|
|
10010
|
+
warning: this.getCorruptedIndexWarning(path13.join(this.indexPath, "codebase.db"))
|
|
9854
10011
|
};
|
|
9855
10012
|
}
|
|
9856
10013
|
this.logger.recordGc(removedCount, gcOrphanChunks, gcOrphanEmbeddings);
|
|
@@ -10001,7 +10158,7 @@ var Indexer = class {
|
|
|
10001
10158
|
succeeded += successfulResults.length;
|
|
10002
10159
|
stillFailing.push(...failedChunksForBatch.values());
|
|
10003
10160
|
} catch (error) {
|
|
10004
|
-
const failureMessage =
|
|
10161
|
+
const failureMessage = getErrorMessage2(error);
|
|
10005
10162
|
const failureTimestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
10006
10163
|
const unaccountedChunks = batch.chunks.filter(
|
|
10007
10164
|
(chunk) => !failedChunksForBatch.has(chunk.id) && !completedChunkIds.has(chunk.id)
|
|
@@ -10175,12 +10332,12 @@ var Indexer = class {
|
|
|
10175
10332
|
})
|
|
10176
10333
|
);
|
|
10177
10334
|
}
|
|
10178
|
-
async getCallers(targetName) {
|
|
10335
|
+
async getCallers(targetName, callTypeFilter) {
|
|
10179
10336
|
const { database } = await this.ensureInitialized();
|
|
10180
10337
|
const seen = /* @__PURE__ */ new Set();
|
|
10181
10338
|
const results = [];
|
|
10182
10339
|
for (const branchKey of this.getBranchCatalogKeys()) {
|
|
10183
|
-
for (const edge of database.getCallersWithContext(targetName, branchKey)) {
|
|
10340
|
+
for (const edge of database.getCallersWithContext(targetName, branchKey, callTypeFilter)) {
|
|
10184
10341
|
if (!seen.has(edge.id)) {
|
|
10185
10342
|
seen.add(edge.id);
|
|
10186
10343
|
results.push(edge);
|
|
@@ -10189,12 +10346,12 @@ var Indexer = class {
|
|
|
10189
10346
|
}
|
|
10190
10347
|
return results;
|
|
10191
10348
|
}
|
|
10192
|
-
async getCallees(symbolId) {
|
|
10349
|
+
async getCallees(symbolId, callTypeFilter) {
|
|
10193
10350
|
const { database } = await this.ensureInitialized();
|
|
10194
10351
|
const seen = /* @__PURE__ */ new Set();
|
|
10195
10352
|
const results = [];
|
|
10196
10353
|
for (const branchKey of this.getBranchCatalogKeys()) {
|
|
10197
|
-
for (const edge of database.getCallees(symbolId, branchKey)) {
|
|
10354
|
+
for (const edge of database.getCallees(symbolId, branchKey, callTypeFilter)) {
|
|
10198
10355
|
if (!seen.has(edge.id)) {
|
|
10199
10356
|
seen.add(edge.id);
|
|
10200
10357
|
results.push(edge);
|
|
@@ -10203,6 +10360,198 @@ var Indexer = class {
|
|
|
10203
10360
|
}
|
|
10204
10361
|
return results;
|
|
10205
10362
|
}
|
|
10363
|
+
async findCallPath(fromName, toName, maxDepth) {
|
|
10364
|
+
const { database } = await this.ensureInitialized();
|
|
10365
|
+
let shortest = [];
|
|
10366
|
+
for (const branchKey of this.getBranchCatalogKeys()) {
|
|
10367
|
+
const path19 = database.findShortestPath(fromName, toName, branchKey, maxDepth);
|
|
10368
|
+
if (path19.length > 0 && (shortest.length === 0 || path19.length < shortest.length)) {
|
|
10369
|
+
shortest = path19;
|
|
10370
|
+
}
|
|
10371
|
+
}
|
|
10372
|
+
return shortest;
|
|
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
|
+
}
|
|
10206
10555
|
async close() {
|
|
10207
10556
|
await this.database?.close();
|
|
10208
10557
|
this.database = null;
|
|
@@ -10430,54 +10779,139 @@ ${truncateContent(r.content)}
|
|
|
10430
10779
|
return formatted.join("\n\n");
|
|
10431
10780
|
}
|
|
10432
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
|
+
|
|
10433
10867
|
// src/tools/knowledge-base-paths.ts
|
|
10434
|
-
var
|
|
10868
|
+
var path14 = __toESM(require("path"), 1);
|
|
10435
10869
|
function resolveConfigPathValue(value, baseDir) {
|
|
10436
10870
|
const trimmed = value.trim();
|
|
10437
10871
|
if (!trimmed) {
|
|
10438
10872
|
return trimmed;
|
|
10439
10873
|
}
|
|
10440
|
-
const absolutePath =
|
|
10441
|
-
return
|
|
10874
|
+
const absolutePath = path14.isAbsolute(trimmed) ? trimmed : path14.resolve(baseDir, trimmed);
|
|
10875
|
+
return path14.normalize(absolutePath);
|
|
10442
10876
|
}
|
|
10443
10877
|
function serializeConfigPathValue(value, baseDir) {
|
|
10444
10878
|
const trimmed = value.trim();
|
|
10445
10879
|
if (!trimmed) {
|
|
10446
10880
|
return trimmed;
|
|
10447
10881
|
}
|
|
10448
|
-
if (!
|
|
10449
|
-
return normalizePathSeparators(
|
|
10882
|
+
if (!path14.isAbsolute(trimmed)) {
|
|
10883
|
+
return normalizePathSeparators(path14.normalize(trimmed));
|
|
10450
10884
|
}
|
|
10451
|
-
const relativePath =
|
|
10452
|
-
if (!relativePath || !relativePath.startsWith("..") && !
|
|
10453
|
-
return normalizePathSeparators(
|
|
10885
|
+
const relativePath = path14.relative(baseDir, trimmed);
|
|
10886
|
+
if (!relativePath || !relativePath.startsWith("..") && !path14.isAbsolute(relativePath)) {
|
|
10887
|
+
return normalizePathSeparators(path14.normalize(relativePath || "."));
|
|
10454
10888
|
}
|
|
10455
|
-
return
|
|
10889
|
+
return path14.normalize(trimmed);
|
|
10456
10890
|
}
|
|
10457
10891
|
function resolveKnowledgeBasePath(value, projectRoot) {
|
|
10458
|
-
return
|
|
10892
|
+
return path14.isAbsolute(value) ? value : path14.resolve(projectRoot, value);
|
|
10459
10893
|
}
|
|
10460
10894
|
function normalizeKnowledgeBasePath2(value, projectRoot) {
|
|
10461
|
-
return
|
|
10895
|
+
return path14.normalize(resolveKnowledgeBasePath(value, projectRoot));
|
|
10462
10896
|
}
|
|
10463
10897
|
function hasMatchingKnowledgeBasePath(knowledgeBases, inputPath, projectRoot) {
|
|
10464
|
-
const normalizedInput =
|
|
10898
|
+
const normalizedInput = path14.normalize(inputPath);
|
|
10465
10899
|
return knowledgeBases.some((kb) => normalizeKnowledgeBasePath2(kb, projectRoot) === normalizedInput);
|
|
10466
10900
|
}
|
|
10467
10901
|
function findKnowledgeBasePathIndex(knowledgeBases, inputPath, projectRoot) {
|
|
10468
|
-
const normalizedInput =
|
|
10902
|
+
const normalizedInput = path14.normalize(inputPath);
|
|
10469
10903
|
return knowledgeBases.findIndex(
|
|
10470
|
-
(kb) =>
|
|
10904
|
+
(kb) => path14.normalize(kb) === normalizedInput || normalizeKnowledgeBasePath2(kb, projectRoot) === normalizedInput
|
|
10471
10905
|
);
|
|
10472
10906
|
}
|
|
10473
10907
|
|
|
10474
10908
|
// src/tools/index.ts
|
|
10475
10909
|
var import_fs9 = require("fs");
|
|
10476
|
-
var
|
|
10910
|
+
var path16 = __toESM(require("path"), 1);
|
|
10477
10911
|
|
|
10478
10912
|
// src/tools/config-state.ts
|
|
10479
10913
|
var import_fs8 = require("fs");
|
|
10480
|
-
var
|
|
10914
|
+
var path15 = __toESM(require("path"), 1);
|
|
10481
10915
|
function normalizeKnowledgeBasePaths(config, projectRoot) {
|
|
10482
10916
|
const normalized = { ...config };
|
|
10483
10917
|
if (Array.isArray(normalized.knowledgeBases)) {
|
|
@@ -10504,8 +10938,8 @@ function loadEditableConfig(projectRoot) {
|
|
|
10504
10938
|
}
|
|
10505
10939
|
function saveConfig(projectRoot, config) {
|
|
10506
10940
|
const configPath = getConfigPath(projectRoot);
|
|
10507
|
-
const configDir =
|
|
10508
|
-
const configBaseDir =
|
|
10941
|
+
const configDir = path15.dirname(configPath);
|
|
10942
|
+
const configBaseDir = path15.dirname(configDir);
|
|
10509
10943
|
if (!(0, import_fs8.existsSync)(configDir)) {
|
|
10510
10944
|
(0, import_fs8.mkdirSync)(configDir, { recursive: true });
|
|
10511
10945
|
}
|
|
@@ -10523,7 +10957,7 @@ var os5 = __toESM(require("os"), 1);
|
|
|
10523
10957
|
function ensureStringArray(value) {
|
|
10524
10958
|
return Array.isArray(value) ? value : [];
|
|
10525
10959
|
}
|
|
10526
|
-
var
|
|
10960
|
+
var z2 = import_plugin2.tool.schema;
|
|
10527
10961
|
var indexerMap = /* @__PURE__ */ new Map();
|
|
10528
10962
|
var defaultProjectRoot = "";
|
|
10529
10963
|
function initializeTools(projectRoot, config) {
|
|
@@ -10543,12 +10977,12 @@ function shouldForceLocalizeProjectIndex(projectRoot) {
|
|
|
10543
10977
|
if (currentConfig.scope !== "project") {
|
|
10544
10978
|
return false;
|
|
10545
10979
|
}
|
|
10546
|
-
const localIndexPath =
|
|
10980
|
+
const localIndexPath = path16.join(projectRoot, ".opencode", "index");
|
|
10547
10981
|
const mainRepoRoot = resolveWorktreeMainRepoRoot(projectRoot);
|
|
10548
10982
|
if (!mainRepoRoot) {
|
|
10549
10983
|
return false;
|
|
10550
10984
|
}
|
|
10551
|
-
const inheritedIndexPath =
|
|
10985
|
+
const inheritedIndexPath = path16.join(mainRepoRoot, ".opencode", "index");
|
|
10552
10986
|
return !(0, import_fs9.existsSync)(localIndexPath) && (0, import_fs9.existsSync)(inheritedIndexPath);
|
|
10553
10987
|
}
|
|
10554
10988
|
function getIndexerForProject(directory) {
|
|
@@ -10564,14 +10998,14 @@ function getIndexerForProject(directory) {
|
|
|
10564
10998
|
}
|
|
10565
10999
|
return indexer;
|
|
10566
11000
|
}
|
|
10567
|
-
var codebase_peek = (0,
|
|
11001
|
+
var codebase_peek = (0, import_plugin2.tool)({
|
|
10568
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.",
|
|
10569
11003
|
args: {
|
|
10570
|
-
query:
|
|
10571
|
-
limit:
|
|
10572
|
-
fileType:
|
|
10573
|
-
directory:
|
|
10574
|
-
chunkType:
|
|
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")
|
|
10575
11009
|
},
|
|
10576
11010
|
async execute(args, context) {
|
|
10577
11011
|
const indexer = getIndexerForProject(context?.worktree);
|
|
@@ -10584,12 +11018,12 @@ var codebase_peek = (0, import_plugin.tool)({
|
|
|
10584
11018
|
return formatCodebasePeek(results);
|
|
10585
11019
|
}
|
|
10586
11020
|
});
|
|
10587
|
-
var index_codebase = (0,
|
|
11021
|
+
var index_codebase = (0, import_plugin2.tool)({
|
|
10588
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.",
|
|
10589
11023
|
args: {
|
|
10590
|
-
force:
|
|
10591
|
-
estimateOnly:
|
|
10592
|
-
verbose:
|
|
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")
|
|
10593
11027
|
},
|
|
10594
11028
|
async execute(args, context) {
|
|
10595
11029
|
const projectRoot = context?.worktree || defaultProjectRoot;
|
|
@@ -10622,7 +11056,7 @@ var index_codebase = (0, import_plugin.tool)({
|
|
|
10622
11056
|
return formatIndexStats(stats, args.verbose ?? false);
|
|
10623
11057
|
}
|
|
10624
11058
|
});
|
|
10625
|
-
var index_status = (0,
|
|
11059
|
+
var index_status = (0, import_plugin2.tool)({
|
|
10626
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.",
|
|
10627
11061
|
args: {},
|
|
10628
11062
|
async execute(_args, context) {
|
|
@@ -10631,7 +11065,7 @@ var index_status = (0, import_plugin.tool)({
|
|
|
10631
11065
|
return formatStatus(status);
|
|
10632
11066
|
}
|
|
10633
11067
|
});
|
|
10634
|
-
var index_health_check = (0,
|
|
11068
|
+
var index_health_check = (0, import_plugin2.tool)({
|
|
10635
11069
|
description: "Check index health and remove stale entries from deleted files. Run this to clean up the index after files have been deleted.",
|
|
10636
11070
|
args: {},
|
|
10637
11071
|
async execute(_args, context) {
|
|
@@ -10640,7 +11074,7 @@ var index_health_check = (0, import_plugin.tool)({
|
|
|
10640
11074
|
return formatHealthCheck(result);
|
|
10641
11075
|
}
|
|
10642
11076
|
});
|
|
10643
|
-
var index_metrics = (0,
|
|
11077
|
+
var index_metrics = (0, import_plugin2.tool)({
|
|
10644
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.",
|
|
10645
11079
|
args: {},
|
|
10646
11080
|
async execute(_args, context) {
|
|
@@ -10655,12 +11089,12 @@ var index_metrics = (0, import_plugin.tool)({
|
|
|
10655
11089
|
return logger.formatMetrics();
|
|
10656
11090
|
}
|
|
10657
11091
|
});
|
|
10658
|
-
var index_logs = (0,
|
|
11092
|
+
var index_logs = (0, import_plugin2.tool)({
|
|
10659
11093
|
description: "Get recent debug logs from the codebase indexer. Shows timestamped log entries with level and category. Requires debug.enabled=true in config.",
|
|
10660
11094
|
args: {
|
|
10661
|
-
limit:
|
|
10662
|
-
category:
|
|
10663
|
-
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")
|
|
10664
11098
|
},
|
|
10665
11099
|
async execute(args, context) {
|
|
10666
11100
|
const indexer = getIndexerForProject(context?.worktree);
|
|
@@ -10679,15 +11113,15 @@ var index_logs = (0, import_plugin.tool)({
|
|
|
10679
11113
|
return formatLogs(logs);
|
|
10680
11114
|
}
|
|
10681
11115
|
});
|
|
10682
|
-
var find_similar = (0,
|
|
11116
|
+
var find_similar = (0, import_plugin2.tool)({
|
|
10683
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.",
|
|
10684
11118
|
args: {
|
|
10685
|
-
code:
|
|
10686
|
-
limit:
|
|
10687
|
-
fileType:
|
|
10688
|
-
directory:
|
|
10689
|
-
chunkType:
|
|
10690
|
-
excludeFile:
|
|
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)")
|
|
10691
11125
|
},
|
|
10692
11126
|
async execute(args, context) {
|
|
10693
11127
|
const indexer = getIndexerForProject(context?.worktree);
|
|
@@ -10703,15 +11137,15 @@ var find_similar = (0, import_plugin.tool)({
|
|
|
10703
11137
|
return formatSearchResults(results);
|
|
10704
11138
|
}
|
|
10705
11139
|
});
|
|
10706
|
-
var codebase_search = (0,
|
|
11140
|
+
var codebase_search = (0, import_plugin2.tool)({
|
|
10707
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.",
|
|
10708
11142
|
args: {
|
|
10709
|
-
query:
|
|
10710
|
-
limit:
|
|
10711
|
-
fileType:
|
|
10712
|
-
directory:
|
|
10713
|
-
chunkType:
|
|
10714
|
-
contextLines:
|
|
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)")
|
|
10715
11149
|
},
|
|
10716
11150
|
async execute(args, context) {
|
|
10717
11151
|
const indexer = getIndexerForProject(context?.worktree);
|
|
@@ -10727,13 +11161,13 @@ var codebase_search = (0, import_plugin.tool)({
|
|
|
10727
11161
|
return formatSearchResults(results, "score");
|
|
10728
11162
|
}
|
|
10729
11163
|
});
|
|
10730
|
-
var implementation_lookup = (0,
|
|
11164
|
+
var implementation_lookup = (0, import_plugin2.tool)({
|
|
10731
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.",
|
|
10732
11166
|
args: {
|
|
10733
|
-
query:
|
|
10734
|
-
limit:
|
|
10735
|
-
fileType:
|
|
10736
|
-
directory:
|
|
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')")
|
|
10737
11171
|
},
|
|
10738
11172
|
async execute(args, context) {
|
|
10739
11173
|
const indexer = getIndexerForProject(context?.worktree);
|
|
@@ -10745,12 +11179,13 @@ var implementation_lookup = (0, import_plugin.tool)({
|
|
|
10745
11179
|
return formatDefinitionLookup(results, args.query);
|
|
10746
11180
|
}
|
|
10747
11181
|
});
|
|
10748
|
-
var call_graph = (0,
|
|
10749
|
-
description: "Query the call graph to find callers or callees of a function/method. Use to understand code flow and dependencies between functions.",
|
|
11182
|
+
var call_graph = (0, import_plugin2.tool)({
|
|
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.",
|
|
10750
11184
|
args: {
|
|
10751
|
-
name:
|
|
10752
|
-
direction:
|
|
10753
|
-
symbolId:
|
|
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.")
|
|
10754
11189
|
},
|
|
10755
11190
|
async execute(args, context) {
|
|
10756
11191
|
const indexer = getIndexerForProject(context?.worktree);
|
|
@@ -10758,35 +11193,59 @@ var call_graph = (0, import_plugin.tool)({
|
|
|
10758
11193
|
if (!args.symbolId) {
|
|
10759
11194
|
return "Error: 'symbolId' is required when direction is 'callees'. First use direction='callers' to find the symbol ID.";
|
|
10760
11195
|
}
|
|
10761
|
-
const callees = await indexer.getCallees(args.symbolId);
|
|
11196
|
+
const callees = await indexer.getCallees(args.symbolId, args.relationshipType);
|
|
10762
11197
|
if (callees.length === 0) {
|
|
10763
|
-
return `No callees found for symbol ${args.symbolId}. The function may not call any other tracked functions.`;
|
|
11198
|
+
return `No callees found for symbol ${args.symbolId}${args.relationshipType ? ` with type ${args.relationshipType}` : ""}. The function may not call any other tracked functions.`;
|
|
10764
11199
|
}
|
|
10765
|
-
const formatted2 = callees.map(
|
|
10766
|
-
|
|
10767
|
-
|
|
11200
|
+
const formatted2 = callees.map((e, i) => {
|
|
11201
|
+
const conf = e.confidence !== "Direct" ? ` [${e.confidence.toLowerCase()}]` : "";
|
|
11202
|
+
return `[${i + 1}] \u2192 ${e.targetName} (${e.callType})${conf} at line ${e.line}${e.isResolved ? ` [resolved: ${e.toSymbolId}]` : " [unresolved]"}`;
|
|
11203
|
+
});
|
|
10768
11204
|
return formatted2.join("\n");
|
|
10769
11205
|
}
|
|
10770
|
-
const callers = await indexer.getCallers(args.name);
|
|
11206
|
+
const callers = await indexer.getCallers(args.name, args.relationshipType);
|
|
10771
11207
|
if (callers.length === 0) {
|
|
10772
|
-
return `No callers found for "${args.name}". It may not be called by any tracked function, or the index needs updating.`;
|
|
11208
|
+
return `No callers found for "${args.name}"${args.relationshipType ? ` with type ${args.relationshipType}` : ""}. It may not be called by any tracked function, or the index needs updating.`;
|
|
10773
11209
|
}
|
|
10774
|
-
const formatted = callers.map(
|
|
10775
|
-
|
|
10776
|
-
|
|
11210
|
+
const formatted = callers.map((e, i) => {
|
|
11211
|
+
const conf = e.confidence !== "Direct" ? ` [${e.confidence.toLowerCase()}]` : "";
|
|
11212
|
+
return `[${i + 1}] \u2190 from ${e.fromSymbolName ?? "<unknown>"} in ${e.fromSymbolFilePath ?? "<unknown file>"} [${e.fromSymbolId}] (${e.callType})${conf} at line ${e.line}${e.isResolved ? " [resolved]" : " [unresolved]"}`;
|
|
11213
|
+
});
|
|
10777
11214
|
return formatted.join("\n");
|
|
10778
11215
|
}
|
|
10779
11216
|
});
|
|
10780
|
-
var
|
|
11217
|
+
var call_graph_path = (0, import_plugin2.tool)({
|
|
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.",
|
|
11219
|
+
args: {
|
|
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)")
|
|
11223
|
+
},
|
|
11224
|
+
async execute(args, context) {
|
|
11225
|
+
const indexer = getIndexerForProject(context?.worktree);
|
|
11226
|
+
const path19 = await indexer.findCallPath(args.from, args.to, args.maxDepth);
|
|
11227
|
+
if (path19.length === 0) {
|
|
11228
|
+
return `No path found between "${args.from}" and "${args.to}". They may be in disconnected components, or the call graph index needs updating.`;
|
|
11229
|
+
}
|
|
11230
|
+
const formatted = path19.map((hop, i) => {
|
|
11231
|
+
const prefix = i === 0 ? "[start]" : `--${hop.callType}-->`;
|
|
11232
|
+
const location = hop.filePath ? ` (${hop.filePath}:${hop.line})` : "";
|
|
11233
|
+
return `${prefix} ${hop.symbolName}${location}`;
|
|
11234
|
+
});
|
|
11235
|
+
return `Path (${path19.length} hops):
|
|
11236
|
+
${formatted.join("\n")}`;
|
|
11237
|
+
}
|
|
11238
|
+
});
|
|
11239
|
+
var add_knowledge_base = (0, import_plugin2.tool)({
|
|
10781
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).",
|
|
10782
11241
|
args: {
|
|
10783
|
-
path:
|
|
11242
|
+
path: z2.string().describe("Path to the folder to add as a knowledge base (absolute or relative to project root)")
|
|
10784
11243
|
},
|
|
10785
11244
|
async execute(args, context) {
|
|
10786
11245
|
const projectRoot = context?.worktree || defaultProjectRoot;
|
|
10787
11246
|
const inputPath = args.path.trim();
|
|
10788
|
-
const normalizedPath =
|
|
10789
|
-
|
|
11247
|
+
const normalizedPath = path16.resolve(
|
|
11248
|
+
path16.isAbsolute(inputPath) ? inputPath : resolveKnowledgeBasePath(inputPath, projectRoot)
|
|
10790
11249
|
);
|
|
10791
11250
|
if (!(0, import_fs9.existsSync)(normalizedPath)) {
|
|
10792
11251
|
return `Error: Directory does not exist: ${normalizedPath}`;
|
|
@@ -10815,7 +11274,7 @@ var add_knowledge_base = (0, import_plugin.tool)({
|
|
|
10815
11274
|
}
|
|
10816
11275
|
}
|
|
10817
11276
|
for (const dotDir of sensitiveDotDirs) {
|
|
10818
|
-
const sensitiveDir =
|
|
11277
|
+
const sensitiveDir = path16.join(homeDir, dotDir);
|
|
10819
11278
|
if (realPath === sensitiveDir || realPath.startsWith(sensitiveDir + "/")) {
|
|
10820
11279
|
return `Error: Adding sensitive directory as knowledge base is not allowed: ${normalizedPath}`;
|
|
10821
11280
|
}
|
|
@@ -10849,7 +11308,7 @@ Run /index to rebuild the index with the new knowledge base.`;
|
|
|
10849
11308
|
return result;
|
|
10850
11309
|
}
|
|
10851
11310
|
});
|
|
10852
|
-
var list_knowledge_bases = (0,
|
|
11311
|
+
var list_knowledge_bases = (0, import_plugin2.tool)({
|
|
10853
11312
|
description: "List all configured knowledge base folders that are indexed alongside the main project.",
|
|
10854
11313
|
args: {},
|
|
10855
11314
|
async execute(_args, context) {
|
|
@@ -10886,10 +11345,10 @@ var list_knowledge_bases = (0, import_plugin.tool)({
|
|
|
10886
11345
|
return result;
|
|
10887
11346
|
}
|
|
10888
11347
|
});
|
|
10889
|
-
var remove_knowledge_base = (0,
|
|
11348
|
+
var remove_knowledge_base = (0, import_plugin2.tool)({
|
|
10890
11349
|
description: "Remove a knowledge base folder from the semantic search index.",
|
|
10891
11350
|
args: {
|
|
10892
|
-
path:
|
|
11351
|
+
path: z2.string().describe("Path of the knowledge base to remove (must match the configured path exactly)")
|
|
10893
11352
|
},
|
|
10894
11353
|
async execute(args, context) {
|
|
10895
11354
|
const projectRoot = context?.worktree || defaultProjectRoot;
|
|
@@ -10931,7 +11390,7 @@ Run /index to rebuild the index without the removed knowledge base.`;
|
|
|
10931
11390
|
|
|
10932
11391
|
// src/commands/loader.ts
|
|
10933
11392
|
var import_fs10 = require("fs");
|
|
10934
|
-
var
|
|
11393
|
+
var path17 = __toESM(require("path"), 1);
|
|
10935
11394
|
function parseFrontmatter(content) {
|
|
10936
11395
|
const frontmatterRegex = /^---\s*\n([\s\S]*?)\n---\s*\n([\s\S]*)$/;
|
|
10937
11396
|
const match = content.match(frontmatterRegex);
|
|
@@ -10957,7 +11416,7 @@ function loadCommandsFromDirectory(commandsDir) {
|
|
|
10957
11416
|
}
|
|
10958
11417
|
const files = (0, import_fs10.readdirSync)(commandsDir).filter((f) => f.endsWith(".md"));
|
|
10959
11418
|
for (const file of files) {
|
|
10960
|
-
const filePath =
|
|
11419
|
+
const filePath = path17.join(commandsDir, file);
|
|
10961
11420
|
let content;
|
|
10962
11421
|
try {
|
|
10963
11422
|
content = (0, import_fs10.readFileSync)(filePath, "utf-8");
|
|
@@ -10966,7 +11425,7 @@ function loadCommandsFromDirectory(commandsDir) {
|
|
|
10966
11425
|
throw new Error(`Failed to load command file ${filePath}: ${message}`);
|
|
10967
11426
|
}
|
|
10968
11427
|
const { frontmatter, body } = parseFrontmatter(content);
|
|
10969
|
-
const name =
|
|
11428
|
+
const name = path17.basename(file, ".md");
|
|
10970
11429
|
const description = frontmatter.description || `Run the ${name} command`;
|
|
10971
11430
|
commands.set(name, {
|
|
10972
11431
|
description,
|
|
@@ -11188,7 +11647,7 @@ function assessRoutingIntent(text) {
|
|
|
11188
11647
|
reason: "no_local_discovery_signal"
|
|
11189
11648
|
};
|
|
11190
11649
|
}
|
|
11191
|
-
function buildRoutingHint(assessment, status) {
|
|
11650
|
+
function buildRoutingHint(assessment, status, includeGraphHandoff = false) {
|
|
11192
11651
|
if (assessment.intent === "definition_lookup") {
|
|
11193
11652
|
if (!status || !status.indexed || status.compatibility?.compatible === false) {
|
|
11194
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.";
|
|
@@ -11199,15 +11658,21 @@ function buildRoutingHint(assessment, status) {
|
|
|
11199
11658
|
return null;
|
|
11200
11659
|
}
|
|
11201
11660
|
if (!status || !status.indexed || status.compatibility?.compatible === false) {
|
|
11202
|
-
|
|
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.`;
|
|
11203
11663
|
}
|
|
11204
|
-
|
|
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.`;
|
|
11205
11666
|
}
|
|
11206
11667
|
var RoutingHintController = class {
|
|
11207
|
-
constructor(getStatus, maxSessions = 200) {
|
|
11668
|
+
constructor(getStatus, maxSessions = 200, includeGraphHandoff = false) {
|
|
11208
11669
|
this.getStatus = getStatus;
|
|
11209
11670
|
this.maxSessions = maxSessions;
|
|
11671
|
+
this.includeGraphHandoff = includeGraphHandoff;
|
|
11210
11672
|
}
|
|
11673
|
+
getStatus;
|
|
11674
|
+
maxSessions;
|
|
11675
|
+
includeGraphHandoff;
|
|
11211
11676
|
sessionState = /* @__PURE__ */ new Map();
|
|
11212
11677
|
observeUserMessage(sessionID, parts) {
|
|
11213
11678
|
const assessment = assessRoutingIntent(extractUserText(parts));
|
|
@@ -11228,7 +11693,7 @@ var RoutingHintController = class {
|
|
|
11228
11693
|
return [];
|
|
11229
11694
|
}
|
|
11230
11695
|
const status = await this.safeGetStatus();
|
|
11231
|
-
const hint = buildRoutingHint(state.assessment, status);
|
|
11696
|
+
const hint = buildRoutingHint(state.assessment, status, this.includeGraphHandoff);
|
|
11232
11697
|
return hint ? [hint] : [];
|
|
11233
11698
|
}
|
|
11234
11699
|
markToolUsed(sessionID, toolName) {
|
|
@@ -11279,9 +11744,9 @@ function replaceActiveWatcher(projectRoot, nextWatcher) {
|
|
|
11279
11744
|
function getCommandsDir() {
|
|
11280
11745
|
let currentDir = process.cwd();
|
|
11281
11746
|
if (typeof import_meta2 !== "undefined" && import_meta2.url) {
|
|
11282
|
-
currentDir =
|
|
11747
|
+
currentDir = path18.dirname((0, import_url2.fileURLToPath)(import_meta2.url));
|
|
11283
11748
|
}
|
|
11284
|
-
return
|
|
11749
|
+
return path18.join(currentDir, "..", "commands");
|
|
11285
11750
|
}
|
|
11286
11751
|
function appendRoutingHints(output, hints, preferredRole) {
|
|
11287
11752
|
const preferredBucket = preferredRole === "developer" ? output.developer : output.system;
|
|
@@ -11300,8 +11765,8 @@ var plugin = async ({ directory, worktree }) => {
|
|
|
11300
11765
|
const config = parseConfig(rawConfig);
|
|
11301
11766
|
initializeTools(projectRoot, config);
|
|
11302
11767
|
const getProjectIndexer = () => getIndexerForProject(projectRoot);
|
|
11303
|
-
const routingHints = config.search.routingHints ? new RoutingHintController(() => getProjectIndexer().getStatus()) : null;
|
|
11304
|
-
const isHomeDir =
|
|
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());
|
|
11305
11770
|
const isValidProject = !isHomeDir && (!config.indexing.requireProjectMarker || hasProjectMarker(projectRoot));
|
|
11306
11771
|
if (isHomeDir) {
|
|
11307
11772
|
console.warn(
|
|
@@ -11336,10 +11801,12 @@ var plugin = async ({ directory, worktree }) => {
|
|
|
11336
11801
|
index_logs,
|
|
11337
11802
|
find_similar,
|
|
11338
11803
|
call_graph,
|
|
11804
|
+
call_graph_path,
|
|
11339
11805
|
implementation_lookup,
|
|
11340
11806
|
add_knowledge_base,
|
|
11341
11807
|
list_knowledge_bases,
|
|
11342
|
-
remove_knowledge_base
|
|
11808
|
+
remove_knowledge_base,
|
|
11809
|
+
pr_impact
|
|
11343
11810
|
},
|
|
11344
11811
|
async "chat.message"(input, output) {
|
|
11345
11812
|
routingHints?.observeUserMessage(input.sessionID, output.parts);
|