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