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