opencode-codebase-index 0.10.0 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +42 -3
- package/commands/call-graph.md +9 -5
- package/commands/pr-impact.md +23 -0
- package/dist/cli.cjs +635 -178
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +635 -178
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +805 -338
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +805 -338
- package/dist/index.js.map +1 -1
- package/native/codebase-index-native.darwin-arm64.node +0 -0
- package/native/codebase-index-native.darwin-x64.node +0 -0
- package/native/codebase-index-native.linux-arm64-gnu.node +0 -0
- package/native/codebase-index-native.linux-x64-gnu.node +0 -0
- package/native/codebase-index-native.win32-x64-msvc.node +0 -0
- package/package.json +4 -3
- package/skill/SKILL.md +6 -3
package/dist/cli.cjs
CHANGED
|
@@ -8,7 +8,11 @@ var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
|
8
8
|
var __getProtoOf = Object.getPrototypeOf;
|
|
9
9
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
10
10
|
var __commonJS = (cb, mod) => function __require() {
|
|
11
|
-
|
|
11
|
+
try {
|
|
12
|
+
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
13
|
+
} catch (e) {
|
|
14
|
+
throw mod = 0, e;
|
|
15
|
+
}
|
|
12
16
|
};
|
|
13
17
|
var __copyProps = (to, from, except, desc) => {
|
|
14
18
|
if (from && typeof from === "object" || typeof from === "function") {
|
|
@@ -487,7 +491,7 @@ var require_ignore = __commonJS({
|
|
|
487
491
|
// path matching.
|
|
488
492
|
// - check `string` either `MODE_IGNORE` or `MODE_CHECK_IGNORE`
|
|
489
493
|
// @returns {TestResult} true if a file is ignored
|
|
490
|
-
test(
|
|
494
|
+
test(path19, checkUnignored, mode) {
|
|
491
495
|
let ignored = false;
|
|
492
496
|
let unignored = false;
|
|
493
497
|
let matchedRule;
|
|
@@ -496,7 +500,7 @@ var require_ignore = __commonJS({
|
|
|
496
500
|
if (unignored === negative && ignored !== unignored || negative && !ignored && !unignored && !checkUnignored) {
|
|
497
501
|
return;
|
|
498
502
|
}
|
|
499
|
-
const matched = rule[mode].test(
|
|
503
|
+
const matched = rule[mode].test(path19);
|
|
500
504
|
if (!matched) {
|
|
501
505
|
return;
|
|
502
506
|
}
|
|
@@ -517,17 +521,17 @@ var require_ignore = __commonJS({
|
|
|
517
521
|
var throwError = (message, Ctor) => {
|
|
518
522
|
throw new Ctor(message);
|
|
519
523
|
};
|
|
520
|
-
var checkPath = (
|
|
521
|
-
if (!isString(
|
|
524
|
+
var checkPath = (path19, originalPath, doThrow) => {
|
|
525
|
+
if (!isString(path19)) {
|
|
522
526
|
return doThrow(
|
|
523
527
|
`path must be a string, but got \`${originalPath}\``,
|
|
524
528
|
TypeError
|
|
525
529
|
);
|
|
526
530
|
}
|
|
527
|
-
if (!
|
|
531
|
+
if (!path19) {
|
|
528
532
|
return doThrow(`path must not be empty`, TypeError);
|
|
529
533
|
}
|
|
530
|
-
if (checkPath.isNotRelative(
|
|
534
|
+
if (checkPath.isNotRelative(path19)) {
|
|
531
535
|
const r = "`path.relative()`d";
|
|
532
536
|
return doThrow(
|
|
533
537
|
`path should be a ${r} string, but got "${originalPath}"`,
|
|
@@ -536,7 +540,7 @@ var require_ignore = __commonJS({
|
|
|
536
540
|
}
|
|
537
541
|
return true;
|
|
538
542
|
};
|
|
539
|
-
var isNotRelative = (
|
|
543
|
+
var isNotRelative = (path19) => REGEX_TEST_INVALID_PATH.test(path19);
|
|
540
544
|
checkPath.isNotRelative = isNotRelative;
|
|
541
545
|
checkPath.convert = (p) => p;
|
|
542
546
|
var Ignore2 = class {
|
|
@@ -566,19 +570,19 @@ var require_ignore = __commonJS({
|
|
|
566
570
|
}
|
|
567
571
|
// @returns {TestResult}
|
|
568
572
|
_test(originalPath, cache, checkUnignored, slices) {
|
|
569
|
-
const
|
|
573
|
+
const path19 = originalPath && checkPath.convert(originalPath);
|
|
570
574
|
checkPath(
|
|
571
|
-
|
|
575
|
+
path19,
|
|
572
576
|
originalPath,
|
|
573
577
|
this._strictPathCheck ? throwError : RETURN_FALSE
|
|
574
578
|
);
|
|
575
|
-
return this._t(
|
|
579
|
+
return this._t(path19, cache, checkUnignored, slices);
|
|
576
580
|
}
|
|
577
|
-
checkIgnore(
|
|
578
|
-
if (!REGEX_TEST_TRAILING_SLASH.test(
|
|
579
|
-
return this.test(
|
|
581
|
+
checkIgnore(path19) {
|
|
582
|
+
if (!REGEX_TEST_TRAILING_SLASH.test(path19)) {
|
|
583
|
+
return this.test(path19);
|
|
580
584
|
}
|
|
581
|
-
const slices =
|
|
585
|
+
const slices = path19.split(SLASH).filter(Boolean);
|
|
582
586
|
slices.pop();
|
|
583
587
|
if (slices.length) {
|
|
584
588
|
const parent = this._t(
|
|
@@ -591,18 +595,18 @@ var require_ignore = __commonJS({
|
|
|
591
595
|
return parent;
|
|
592
596
|
}
|
|
593
597
|
}
|
|
594
|
-
return this._rules.test(
|
|
598
|
+
return this._rules.test(path19, false, MODE_CHECK_IGNORE);
|
|
595
599
|
}
|
|
596
|
-
_t(
|
|
597
|
-
if (
|
|
598
|
-
return cache[
|
|
600
|
+
_t(path19, cache, checkUnignored, slices) {
|
|
601
|
+
if (path19 in cache) {
|
|
602
|
+
return cache[path19];
|
|
599
603
|
}
|
|
600
604
|
if (!slices) {
|
|
601
|
-
slices =
|
|
605
|
+
slices = path19.split(SLASH).filter(Boolean);
|
|
602
606
|
}
|
|
603
607
|
slices.pop();
|
|
604
608
|
if (!slices.length) {
|
|
605
|
-
return cache[
|
|
609
|
+
return cache[path19] = this._rules.test(path19, checkUnignored, MODE_IGNORE);
|
|
606
610
|
}
|
|
607
611
|
const parent = this._t(
|
|
608
612
|
slices.join(SLASH) + SLASH,
|
|
@@ -610,29 +614,29 @@ var require_ignore = __commonJS({
|
|
|
610
614
|
checkUnignored,
|
|
611
615
|
slices
|
|
612
616
|
);
|
|
613
|
-
return cache[
|
|
617
|
+
return cache[path19] = parent.ignored ? parent : this._rules.test(path19, checkUnignored, MODE_IGNORE);
|
|
614
618
|
}
|
|
615
|
-
ignores(
|
|
616
|
-
return this._test(
|
|
619
|
+
ignores(path19) {
|
|
620
|
+
return this._test(path19, this._ignoreCache, false).ignored;
|
|
617
621
|
}
|
|
618
622
|
createFilter() {
|
|
619
|
-
return (
|
|
623
|
+
return (path19) => !this.ignores(path19);
|
|
620
624
|
}
|
|
621
625
|
filter(paths) {
|
|
622
626
|
return makeArray(paths).filter(this.createFilter());
|
|
623
627
|
}
|
|
624
628
|
// @returns {TestResult}
|
|
625
|
-
test(
|
|
626
|
-
return this._test(
|
|
629
|
+
test(path19) {
|
|
630
|
+
return this._test(path19, this._testCache, true);
|
|
627
631
|
}
|
|
628
632
|
};
|
|
629
633
|
var factory = (options) => new Ignore2(options);
|
|
630
|
-
var isPathValid = (
|
|
634
|
+
var isPathValid = (path19) => checkPath(path19 && checkPath.convert(path19), path19, RETURN_FALSE);
|
|
631
635
|
var setupWindows = () => {
|
|
632
636
|
const makePosix = (str) => /^\\\\\?\\/.test(str) || /["<>|\u0000-\u001F]+/u.test(str) ? str : str.replace(/\\/g, "/");
|
|
633
637
|
checkPath.convert = makePosix;
|
|
634
638
|
const REGEX_TEST_WINDOWS_PATH_ABSOLUTE = /^[a-z]:\//i;
|
|
635
|
-
checkPath.isNotRelative = (
|
|
639
|
+
checkPath.isNotRelative = (path19) => REGEX_TEST_WINDOWS_PATH_ABSOLUTE.test(path19) || isNotRelative(path19);
|
|
636
640
|
};
|
|
637
641
|
if (
|
|
638
642
|
// Detect `process` so that it can run in browsers.
|
|
@@ -649,7 +653,7 @@ var require_ignore = __commonJS({
|
|
|
649
653
|
|
|
650
654
|
// src/cli.ts
|
|
651
655
|
var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
652
|
-
var
|
|
656
|
+
var path18 = __toESM(require("path"), 1);
|
|
653
657
|
|
|
654
658
|
// src/config/constants.ts
|
|
655
659
|
var DEFAULT_INCLUDE = [
|
|
@@ -795,6 +799,7 @@ function getDefaultSearchConfig() {
|
|
|
795
799
|
rerankTopN: 20,
|
|
796
800
|
contextLines: 0,
|
|
797
801
|
routingHints: true,
|
|
802
|
+
routingGraphHandoffHints: false,
|
|
798
803
|
routingHintRole: "system"
|
|
799
804
|
};
|
|
800
805
|
}
|
|
@@ -917,6 +922,7 @@ function parseConfig(raw) {
|
|
|
917
922
|
rerankTopN: typeof rawSearch.rerankTopN === "number" ? Math.min(200, Math.max(0, Math.floor(rawSearch.rerankTopN))) : defaultSearch.rerankTopN,
|
|
918
923
|
contextLines: typeof rawSearch.contextLines === "number" ? Math.min(50, Math.max(0, rawSearch.contextLines)) : defaultSearch.contextLines,
|
|
919
924
|
routingHints: typeof rawSearch.routingHints === "boolean" ? rawSearch.routingHints : defaultSearch.routingHints,
|
|
925
|
+
routingGraphHandoffHints: typeof rawSearch.routingGraphHandoffHints === "boolean" ? rawSearch.routingGraphHandoffHints : defaultSearch.routingGraphHandoffHints,
|
|
920
926
|
routingHintRole: rawSearch.routingHintRole === "developer" || rawSearch.routingHintRole === "system" ? rawSearch.routingHintRole : defaultSearch.routingHintRole
|
|
921
927
|
};
|
|
922
928
|
const rawDebug = input.debug && typeof input.debug === "object" ? input.debug : {};
|
|
@@ -1075,9 +1081,9 @@ var import_fs = require("fs");
|
|
|
1075
1081
|
var path = __toESM(require("path"), 1);
|
|
1076
1082
|
|
|
1077
1083
|
// src/eval/report-formatters.ts
|
|
1078
|
-
function assertFiniteNumber(value,
|
|
1084
|
+
function assertFiniteNumber(value, path19) {
|
|
1079
1085
|
if (typeof value !== "number" || Number.isNaN(value) || !Number.isFinite(value)) {
|
|
1080
|
-
throw new Error(`${
|
|
1086
|
+
throw new Error(`${path19} must be a finite number`);
|
|
1081
1087
|
}
|
|
1082
1088
|
return value;
|
|
1083
1089
|
}
|
|
@@ -1266,13 +1272,15 @@ function buildPerQueryArtifact(perQuery) {
|
|
|
1266
1272
|
|
|
1267
1273
|
// src/eval/runner.ts
|
|
1268
1274
|
var import_fs10 = require("fs");
|
|
1269
|
-
var
|
|
1275
|
+
var path13 = __toESM(require("path"), 1);
|
|
1270
1276
|
var import_perf_hooks2 = require("perf_hooks");
|
|
1271
1277
|
|
|
1272
1278
|
// src/indexer/index.ts
|
|
1273
1279
|
var import_fs7 = require("fs");
|
|
1274
|
-
var
|
|
1280
|
+
var path10 = __toESM(require("path"), 1);
|
|
1275
1281
|
var import_perf_hooks = require("perf_hooks");
|
|
1282
|
+
var import_child_process2 = require("child_process");
|
|
1283
|
+
var import_util2 = require("util");
|
|
1276
1284
|
|
|
1277
1285
|
// node_modules/eventemitter3/index.mjs
|
|
1278
1286
|
var import_index = __toESM(require_eventemitter3(), 1);
|
|
@@ -1296,7 +1304,7 @@ function pTimeout(promise, options) {
|
|
|
1296
1304
|
} = options;
|
|
1297
1305
|
let timer;
|
|
1298
1306
|
let abortHandler;
|
|
1299
|
-
const wrappedPromise = new Promise((
|
|
1307
|
+
const wrappedPromise = new Promise((resolve11, reject) => {
|
|
1300
1308
|
if (typeof milliseconds !== "number" || Math.sign(milliseconds) !== 1) {
|
|
1301
1309
|
throw new TypeError(`Expected \`milliseconds\` to be a positive number, got \`${milliseconds}\``);
|
|
1302
1310
|
}
|
|
@@ -1310,7 +1318,7 @@ function pTimeout(promise, options) {
|
|
|
1310
1318
|
};
|
|
1311
1319
|
signal.addEventListener("abort", abortHandler, { once: true });
|
|
1312
1320
|
}
|
|
1313
|
-
promise.then(
|
|
1321
|
+
promise.then(resolve11, reject);
|
|
1314
1322
|
if (milliseconds === Number.POSITIVE_INFINITY) {
|
|
1315
1323
|
return;
|
|
1316
1324
|
}
|
|
@@ -1318,7 +1326,7 @@ function pTimeout(promise, options) {
|
|
|
1318
1326
|
timer = customTimers.setTimeout.call(void 0, () => {
|
|
1319
1327
|
if (fallback) {
|
|
1320
1328
|
try {
|
|
1321
|
-
|
|
1329
|
+
resolve11(fallback());
|
|
1322
1330
|
} catch (error) {
|
|
1323
1331
|
reject(error);
|
|
1324
1332
|
}
|
|
@@ -1328,7 +1336,7 @@ function pTimeout(promise, options) {
|
|
|
1328
1336
|
promise.cancel();
|
|
1329
1337
|
}
|
|
1330
1338
|
if (message === false) {
|
|
1331
|
-
|
|
1339
|
+
resolve11();
|
|
1332
1340
|
} else if (message instanceof Error) {
|
|
1333
1341
|
reject(message);
|
|
1334
1342
|
} else {
|
|
@@ -1730,7 +1738,7 @@ var PQueue = class extends import_index.default {
|
|
|
1730
1738
|
// Assign unique ID if not provided
|
|
1731
1739
|
id: options.id ?? (this.#idAssigner++).toString()
|
|
1732
1740
|
};
|
|
1733
|
-
return new Promise((
|
|
1741
|
+
return new Promise((resolve11, reject) => {
|
|
1734
1742
|
const taskSymbol = /* @__PURE__ */ Symbol(`task-${options.id}`);
|
|
1735
1743
|
let cleanupQueueAbortHandler = () => void 0;
|
|
1736
1744
|
const run = async () => {
|
|
@@ -1770,7 +1778,7 @@ var PQueue = class extends import_index.default {
|
|
|
1770
1778
|
})]);
|
|
1771
1779
|
}
|
|
1772
1780
|
const result = await operation;
|
|
1773
|
-
|
|
1781
|
+
resolve11(result);
|
|
1774
1782
|
this.emit("completed", result);
|
|
1775
1783
|
} catch (error) {
|
|
1776
1784
|
reject(error);
|
|
@@ -1958,13 +1966,13 @@ var PQueue = class extends import_index.default {
|
|
|
1958
1966
|
});
|
|
1959
1967
|
}
|
|
1960
1968
|
async #onEvent(event, filter) {
|
|
1961
|
-
return new Promise((
|
|
1969
|
+
return new Promise((resolve11) => {
|
|
1962
1970
|
const listener = () => {
|
|
1963
1971
|
if (filter && !filter()) {
|
|
1964
1972
|
return;
|
|
1965
1973
|
}
|
|
1966
1974
|
this.off(event, listener);
|
|
1967
|
-
|
|
1975
|
+
resolve11();
|
|
1968
1976
|
};
|
|
1969
1977
|
this.on(event, listener);
|
|
1970
1978
|
});
|
|
@@ -2250,7 +2258,7 @@ async function onAttemptFailure({ error, attemptNumber, retriesConsumed, startTi
|
|
|
2250
2258
|
const finalDelay = Math.min(delayTime, remainingTime);
|
|
2251
2259
|
options.signal?.throwIfAborted();
|
|
2252
2260
|
if (finalDelay > 0) {
|
|
2253
|
-
await new Promise((
|
|
2261
|
+
await new Promise((resolve11, reject) => {
|
|
2254
2262
|
const onAbort = () => {
|
|
2255
2263
|
clearTimeout(timeoutToken);
|
|
2256
2264
|
options.signal?.removeEventListener("abort", onAbort);
|
|
@@ -2258,7 +2266,7 @@ async function onAttemptFailure({ error, attemptNumber, retriesConsumed, startTi
|
|
|
2258
2266
|
};
|
|
2259
2267
|
const timeoutToken = setTimeout(() => {
|
|
2260
2268
|
options.signal?.removeEventListener("abort", onAbort);
|
|
2261
|
-
|
|
2269
|
+
resolve11();
|
|
2262
2270
|
}, finalDelay);
|
|
2263
2271
|
if (options.unref) {
|
|
2264
2272
|
timeoutToken.unref?.();
|
|
@@ -2499,6 +2507,8 @@ var BaseEmbeddingProvider = class {
|
|
|
2499
2507
|
this.credentials = credentials;
|
|
2500
2508
|
this.modelInfo = modelInfo;
|
|
2501
2509
|
}
|
|
2510
|
+
credentials;
|
|
2511
|
+
modelInfo;
|
|
2502
2512
|
async embedQuery(query) {
|
|
2503
2513
|
const result = await this.embedBatch([query]);
|
|
2504
2514
|
return {
|
|
@@ -3676,6 +3686,15 @@ function createMockNativeBinding() {
|
|
|
3676
3686
|
close() {
|
|
3677
3687
|
throw error;
|
|
3678
3688
|
}
|
|
3689
|
+
getTransitiveReachability() {
|
|
3690
|
+
throw error;
|
|
3691
|
+
}
|
|
3692
|
+
detectCommunities() {
|
|
3693
|
+
throw error;
|
|
3694
|
+
}
|
|
3695
|
+
computeCentrality() {
|
|
3696
|
+
throw error;
|
|
3697
|
+
}
|
|
3679
3698
|
}
|
|
3680
3699
|
};
|
|
3681
3700
|
}
|
|
@@ -4226,6 +4245,14 @@ var Database = class {
|
|
|
4226
4245
|
this.throwIfClosed();
|
|
4227
4246
|
return this.inner.getSymbolsByNameCi(name);
|
|
4228
4247
|
}
|
|
4248
|
+
getSymbolsForBranch(branch) {
|
|
4249
|
+
this.throwIfClosed();
|
|
4250
|
+
return this.inner.getSymbolsForBranch(branch);
|
|
4251
|
+
}
|
|
4252
|
+
getSymbolsForFiles(filePaths, branch) {
|
|
4253
|
+
this.throwIfClosed();
|
|
4254
|
+
return this.inner.getSymbolsForFiles(filePaths, branch);
|
|
4255
|
+
}
|
|
4229
4256
|
deleteSymbolsByFile(filePath) {
|
|
4230
4257
|
this.throwIfClosed();
|
|
4231
4258
|
return this.inner.deleteSymbolsByFile(filePath);
|
|
@@ -4239,17 +4266,17 @@ var Database = class {
|
|
|
4239
4266
|
if (edges.length === 0) return;
|
|
4240
4267
|
this.inner.upsertCallEdgesBatch(edges);
|
|
4241
4268
|
}
|
|
4242
|
-
getCallers(targetName, branch) {
|
|
4269
|
+
getCallers(targetName, branch, callTypeFilter) {
|
|
4243
4270
|
this.throwIfClosed();
|
|
4244
|
-
return this.inner.getCallers(targetName, branch);
|
|
4271
|
+
return this.inner.getCallers(targetName, branch, callTypeFilter ?? null);
|
|
4245
4272
|
}
|
|
4246
|
-
getCallersWithContext(targetName, branch) {
|
|
4273
|
+
getCallersWithContext(targetName, branch, callTypeFilter) {
|
|
4247
4274
|
this.throwIfClosed();
|
|
4248
|
-
return this.inner.getCallersWithContext(targetName, branch);
|
|
4275
|
+
return this.inner.getCallersWithContext(targetName, branch, callTypeFilter ?? null);
|
|
4249
4276
|
}
|
|
4250
|
-
getCallees(symbolId, branch) {
|
|
4277
|
+
getCallees(symbolId, branch, callTypeFilter) {
|
|
4251
4278
|
this.throwIfClosed();
|
|
4252
|
-
return this.inner.getCallees(symbolId, branch);
|
|
4279
|
+
return this.inner.getCallees(symbolId, branch, callTypeFilter ?? null);
|
|
4253
4280
|
}
|
|
4254
4281
|
deleteCallEdgesByFile(filePath) {
|
|
4255
4282
|
this.throwIfClosed();
|
|
@@ -4259,6 +4286,10 @@ var Database = class {
|
|
|
4259
4286
|
this.throwIfClosed();
|
|
4260
4287
|
this.inner.resolveCallEdge(edgeId, toSymbolId);
|
|
4261
4288
|
}
|
|
4289
|
+
findShortestPath(fromName, toName, branch, maxDepth) {
|
|
4290
|
+
this.throwIfClosed();
|
|
4291
|
+
return this.inner.findShortestPath(fromName, toName, branch, maxDepth ?? null);
|
|
4292
|
+
}
|
|
4262
4293
|
addSymbolsToBranch(branch, symbolIds) {
|
|
4263
4294
|
this.throwIfClosed();
|
|
4264
4295
|
this.inner.addSymbolsToBranch(branch, symbolIds);
|
|
@@ -4299,6 +4330,23 @@ var Database = class {
|
|
|
4299
4330
|
this.throwIfClosed();
|
|
4300
4331
|
return this.inner.gcOrphanCallEdges();
|
|
4301
4332
|
}
|
|
4333
|
+
getTransitiveReachability(rootSymbolIds, branch, direction, maxDepth) {
|
|
4334
|
+
this.throwIfClosed();
|
|
4335
|
+
return this.inner.getTransitiveReachability(
|
|
4336
|
+
rootSymbolIds,
|
|
4337
|
+
branch,
|
|
4338
|
+
direction,
|
|
4339
|
+
maxDepth ?? null
|
|
4340
|
+
);
|
|
4341
|
+
}
|
|
4342
|
+
detectCommunities(branch, symbolIds) {
|
|
4343
|
+
this.throwIfClosed();
|
|
4344
|
+
return this.inner.detectCommunities(branch, symbolIds ?? null);
|
|
4345
|
+
}
|
|
4346
|
+
computeCentrality(branch) {
|
|
4347
|
+
this.throwIfClosed();
|
|
4348
|
+
return this.inner.computeCentrality(branch);
|
|
4349
|
+
}
|
|
4302
4350
|
};
|
|
4303
4351
|
|
|
4304
4352
|
// src/git/index.ts
|
|
@@ -4472,6 +4520,106 @@ function resolveProjectIndexPath(projectRoot, scope) {
|
|
|
4472
4520
|
return resolveWorktreeFallbackPath(projectRoot, PROJECT_INDEX_RELATIVE_PATH) ?? localIndexPath;
|
|
4473
4521
|
}
|
|
4474
4522
|
|
|
4523
|
+
// src/tools/changed-files.ts
|
|
4524
|
+
var path9 = __toESM(require("path"), 1);
|
|
4525
|
+
var import_child_process = require("child_process");
|
|
4526
|
+
var import_util = require("util");
|
|
4527
|
+
var execFileAsync = (0, import_util.promisify)(import_child_process.execFile);
|
|
4528
|
+
function getErrorMessage(error) {
|
|
4529
|
+
if (error instanceof Error) return error.message;
|
|
4530
|
+
return String(error);
|
|
4531
|
+
}
|
|
4532
|
+
async function getChangedFiles(opts) {
|
|
4533
|
+
const { pr, branch, projectRoot, baseBranch = "main" } = opts;
|
|
4534
|
+
if (pr !== void 0) {
|
|
4535
|
+
return getChangedFilesForPr(pr, projectRoot, baseBranch);
|
|
4536
|
+
}
|
|
4537
|
+
return getChangedFilesForBranch(branch, projectRoot, baseBranch);
|
|
4538
|
+
}
|
|
4539
|
+
async function getChangedFilesForPr(pr, projectRoot, baseBranch) {
|
|
4540
|
+
let headRefName;
|
|
4541
|
+
let actualBaseBranch = baseBranch;
|
|
4542
|
+
try {
|
|
4543
|
+
const { stdout } = await execFileAsync(
|
|
4544
|
+
"gh",
|
|
4545
|
+
["pr", "view", String(pr), "--json", "headRefName,baseRefName,files"],
|
|
4546
|
+
{ cwd: projectRoot, timeout: 3e4 }
|
|
4547
|
+
);
|
|
4548
|
+
const data = JSON.parse(stdout);
|
|
4549
|
+
headRefName = data.headRefName;
|
|
4550
|
+
actualBaseBranch = data.baseRefName || baseBranch;
|
|
4551
|
+
if (data.files && data.files.length > 0) {
|
|
4552
|
+
return {
|
|
4553
|
+
files: normalizeFiles(
|
|
4554
|
+
data.files.map((f) => f.path),
|
|
4555
|
+
projectRoot
|
|
4556
|
+
),
|
|
4557
|
+
baseBranch: actualBaseBranch,
|
|
4558
|
+
source: "gh",
|
|
4559
|
+
headRefName
|
|
4560
|
+
};
|
|
4561
|
+
}
|
|
4562
|
+
} catch (error) {
|
|
4563
|
+
throw new Error(
|
|
4564
|
+
`Failed to retrieve PR #${pr} via gh CLI: ${getErrorMessage(error)}`
|
|
4565
|
+
);
|
|
4566
|
+
}
|
|
4567
|
+
if (headRefName === void 0) {
|
|
4568
|
+
throw new Error(
|
|
4569
|
+
`PR #${pr} returned no usable branch or file information.`
|
|
4570
|
+
);
|
|
4571
|
+
}
|
|
4572
|
+
const result = await getChangedFilesForBranch(headRefName, projectRoot, actualBaseBranch);
|
|
4573
|
+
return { ...result, headRefName };
|
|
4574
|
+
}
|
|
4575
|
+
async function getChangedFilesForBranch(branch, projectRoot, baseBranch) {
|
|
4576
|
+
const targetBranch = branch || await getCurrentBranch2(projectRoot);
|
|
4577
|
+
const mergeBase = await getMergeBase(projectRoot, baseBranch, targetBranch);
|
|
4578
|
+
const { stdout } = await execFileAsync(
|
|
4579
|
+
"git",
|
|
4580
|
+
["diff", "--name-only", `${mergeBase}...${targetBranch}`],
|
|
4581
|
+
{ cwd: projectRoot, timeout: 3e4 }
|
|
4582
|
+
);
|
|
4583
|
+
return {
|
|
4584
|
+
files: normalizeFiles(stdout.split("\n"), projectRoot),
|
|
4585
|
+
baseBranch,
|
|
4586
|
+
source: "git",
|
|
4587
|
+
headRefName: targetBranch
|
|
4588
|
+
};
|
|
4589
|
+
}
|
|
4590
|
+
async function getCurrentBranch2(projectRoot) {
|
|
4591
|
+
const { stdout } = await execFileAsync(
|
|
4592
|
+
"git",
|
|
4593
|
+
["branch", "--show-current"],
|
|
4594
|
+
{ cwd: projectRoot, timeout: 3e4 }
|
|
4595
|
+
);
|
|
4596
|
+
return stdout.trim() || "HEAD";
|
|
4597
|
+
}
|
|
4598
|
+
async function getMergeBase(projectRoot, baseBranch, branch) {
|
|
4599
|
+
const { stdout } = await execFileAsync(
|
|
4600
|
+
"git",
|
|
4601
|
+
["merge-base", baseBranch, branch],
|
|
4602
|
+
{ cwd: projectRoot, timeout: 3e4 }
|
|
4603
|
+
);
|
|
4604
|
+
return stdout.trim();
|
|
4605
|
+
}
|
|
4606
|
+
function normalizeFiles(rawFiles, projectRoot) {
|
|
4607
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4608
|
+
const result = [];
|
|
4609
|
+
for (const raw of rawFiles) {
|
|
4610
|
+
const trimmed = raw.trim();
|
|
4611
|
+
if (!trimmed) continue;
|
|
4612
|
+
const absolute = path9.resolve(projectRoot, trimmed);
|
|
4613
|
+
const relative5 = path9.relative(projectRoot, absolute);
|
|
4614
|
+
const cleaned = relative5.startsWith("./") ? relative5.slice(2) : relative5;
|
|
4615
|
+
if (!seen.has(cleaned)) {
|
|
4616
|
+
seen.add(cleaned);
|
|
4617
|
+
result.push(cleaned);
|
|
4618
|
+
}
|
|
4619
|
+
}
|
|
4620
|
+
return result;
|
|
4621
|
+
}
|
|
4622
|
+
|
|
4475
4623
|
// src/indexer/index.ts
|
|
4476
4624
|
var CALL_GRAPH_LANGUAGES = /* @__PURE__ */ new Set(["typescript", "tsx", "javascript", "jsx", "python", "go", "rust", "php", "apex", "zig", "gdscript", "matlab"]);
|
|
4477
4625
|
var CASE_INSENSITIVE_LANGUAGES = /* @__PURE__ */ new Set(["apex"]);
|
|
@@ -4479,6 +4627,7 @@ var CALL_GRAPH_SYMBOL_CHUNK_TYPES = /* @__PURE__ */ new Set([
|
|
|
4479
4627
|
"function_declaration",
|
|
4480
4628
|
"function",
|
|
4481
4629
|
"arrow_function",
|
|
4630
|
+
"export_statement",
|
|
4482
4631
|
"method_definition",
|
|
4483
4632
|
"class_declaration",
|
|
4484
4633
|
"interface_declaration",
|
|
@@ -4517,7 +4666,7 @@ function float32ArrayToBuffer(arr) {
|
|
|
4517
4666
|
function bufferToFloat32Array(buf) {
|
|
4518
4667
|
return new Float32Array(buf.buffer, buf.byteOffset, buf.byteLength / 4);
|
|
4519
4668
|
}
|
|
4520
|
-
function
|
|
4669
|
+
function getErrorMessage2(error) {
|
|
4521
4670
|
if (error instanceof Error) {
|
|
4522
4671
|
return error.message;
|
|
4523
4672
|
}
|
|
@@ -4530,7 +4679,7 @@ function getErrorMessage(error) {
|
|
|
4530
4679
|
return String(error);
|
|
4531
4680
|
}
|
|
4532
4681
|
function isRateLimitError(error) {
|
|
4533
|
-
const message =
|
|
4682
|
+
const message = getErrorMessage2(error);
|
|
4534
4683
|
return message.includes("429") || message.toLowerCase().includes("rate limit") || message.toLowerCase().includes("too many requests");
|
|
4535
4684
|
}
|
|
4536
4685
|
function getSafeEmbeddingChunkTokenLimit(provider) {
|
|
@@ -4548,7 +4697,7 @@ function getDynamicBatchOptions(provider) {
|
|
|
4548
4697
|
return {};
|
|
4549
4698
|
}
|
|
4550
4699
|
function isSqliteCorruptionError(error) {
|
|
4551
|
-
const message =
|
|
4700
|
+
const message = getErrorMessage2(error).toLowerCase();
|
|
4552
4701
|
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");
|
|
4553
4702
|
}
|
|
4554
4703
|
var STARTUP_WARNING_METADATA_KEY = "index.startupWarning";
|
|
@@ -4710,9 +4859,9 @@ function hasAllEmbeddingParts(parts, expectedPartCount) {
|
|
|
4710
4859
|
return true;
|
|
4711
4860
|
}
|
|
4712
4861
|
function isPathWithinRoot(filePath, rootPath) {
|
|
4713
|
-
const normalizedFilePath =
|
|
4714
|
-
const normalizedRoot =
|
|
4715
|
-
return normalizedFilePath === normalizedRoot || normalizedFilePath.startsWith(`${normalizedRoot}${
|
|
4862
|
+
const normalizedFilePath = path10.resolve(filePath);
|
|
4863
|
+
const normalizedRoot = path10.resolve(rootPath);
|
|
4864
|
+
return normalizedFilePath === normalizedRoot || normalizedFilePath.startsWith(`${normalizedRoot}${path10.sep}`);
|
|
4716
4865
|
}
|
|
4717
4866
|
var rankingQueryTokenCache = /* @__PURE__ */ new Map();
|
|
4718
4867
|
var rankingNameTokenCache = /* @__PURE__ */ new Map();
|
|
@@ -5765,9 +5914,9 @@ var Indexer = class {
|
|
|
5765
5914
|
this.projectRoot = projectRoot;
|
|
5766
5915
|
this.config = config;
|
|
5767
5916
|
this.indexPath = this.getIndexPath();
|
|
5768
|
-
this.fileHashCachePath =
|
|
5769
|
-
this.failedBatchesPath =
|
|
5770
|
-
this.indexingLockPath =
|
|
5917
|
+
this.fileHashCachePath = path10.join(this.indexPath, "file-hashes.json");
|
|
5918
|
+
this.failedBatchesPath = path10.join(this.indexPath, "failed-batches.json");
|
|
5919
|
+
this.indexingLockPath = path10.join(this.indexPath, "indexing.lock");
|
|
5771
5920
|
this.logger = initializeLogger(config.debug);
|
|
5772
5921
|
}
|
|
5773
5922
|
getIndexPath() {
|
|
@@ -5799,14 +5948,14 @@ var Indexer = class {
|
|
|
5799
5948
|
}
|
|
5800
5949
|
atomicWriteSync(targetPath, data) {
|
|
5801
5950
|
const tempPath = `${targetPath}.tmp`;
|
|
5802
|
-
(0, import_fs7.mkdirSync)(
|
|
5951
|
+
(0, import_fs7.mkdirSync)(path10.dirname(targetPath), { recursive: true });
|
|
5803
5952
|
(0, import_fs7.writeFileSync)(tempPath, data);
|
|
5804
5953
|
(0, import_fs7.renameSync)(tempPath, targetPath);
|
|
5805
5954
|
}
|
|
5806
5955
|
getScopedRoots() {
|
|
5807
|
-
const roots = /* @__PURE__ */ new Set([
|
|
5956
|
+
const roots = /* @__PURE__ */ new Set([path10.resolve(this.projectRoot)]);
|
|
5808
5957
|
for (const kbRoot of this.config.knowledgeBases) {
|
|
5809
|
-
roots.add(
|
|
5958
|
+
roots.add(path10.resolve(this.projectRoot, kbRoot));
|
|
5810
5959
|
}
|
|
5811
5960
|
return Array.from(roots);
|
|
5812
5961
|
}
|
|
@@ -5815,22 +5964,29 @@ var Indexer = class {
|
|
|
5815
5964
|
if (this.config.scope !== "global") {
|
|
5816
5965
|
return branchName;
|
|
5817
5966
|
}
|
|
5818
|
-
const projectHash = hashContent(
|
|
5967
|
+
const projectHash = hashContent(path10.resolve(this.projectRoot)).slice(0, 16);
|
|
5968
|
+
return `${projectHash}:${branchName}`;
|
|
5969
|
+
}
|
|
5970
|
+
getBranchCatalogKeyFor(branchName) {
|
|
5971
|
+
if (this.config.scope !== "global") {
|
|
5972
|
+
return branchName;
|
|
5973
|
+
}
|
|
5974
|
+
const projectHash = hashContent(path10.resolve(this.projectRoot)).slice(0, 16);
|
|
5819
5975
|
return `${projectHash}:${branchName}`;
|
|
5820
5976
|
}
|
|
5821
5977
|
getLegacyBranchCatalogKey() {
|
|
5822
5978
|
return this.currentBranch || "default";
|
|
5823
5979
|
}
|
|
5824
5980
|
getLegacyMigrationMetadataKey() {
|
|
5825
|
-
const projectHash = hashContent(
|
|
5981
|
+
const projectHash = hashContent(path10.resolve(this.projectRoot)).slice(0, 16);
|
|
5826
5982
|
return `index.globalBranchMigration.${projectHash}`;
|
|
5827
5983
|
}
|
|
5828
5984
|
getProjectEmbeddingStrategyMetadataKey() {
|
|
5829
|
-
const projectHash = hashContent(
|
|
5985
|
+
const projectHash = hashContent(path10.resolve(this.projectRoot)).slice(0, 16);
|
|
5830
5986
|
return `index.embeddingStrategyVersion.${projectHash}`;
|
|
5831
5987
|
}
|
|
5832
5988
|
getProjectForceReembedMetadataKey() {
|
|
5833
|
-
const projectHash = hashContent(
|
|
5989
|
+
const projectHash = hashContent(path10.resolve(this.projectRoot)).slice(0, 16);
|
|
5834
5990
|
return `index.forceReembed.${projectHash}`;
|
|
5835
5991
|
}
|
|
5836
5992
|
hasProjectForceReembedPending() {
|
|
@@ -5924,7 +6080,7 @@ var Indexer = class {
|
|
|
5924
6080
|
if (!this.database) {
|
|
5925
6081
|
return { chunkIds, symbolIds };
|
|
5926
6082
|
}
|
|
5927
|
-
const projectRootPath =
|
|
6083
|
+
const projectRootPath = path10.resolve(this.projectRoot);
|
|
5928
6084
|
const projectLocalFilePaths = /* @__PURE__ */ new Set([
|
|
5929
6085
|
...Array.from(this.fileHashCache.keys()).filter(
|
|
5930
6086
|
(filePath) => this.isFileInCurrentScope(filePath, roots) && isPathWithinRoot(filePath, projectRootPath)
|
|
@@ -5947,7 +6103,7 @@ var Indexer = class {
|
|
|
5947
6103
|
if (this.config.scope !== "global") {
|
|
5948
6104
|
return this.getBranchCatalogCleanupKeys();
|
|
5949
6105
|
}
|
|
5950
|
-
const projectHash = hashContent(
|
|
6106
|
+
const projectHash = hashContent(path10.resolve(this.projectRoot)).slice(0, 16);
|
|
5951
6107
|
const keys = /* @__PURE__ */ new Set();
|
|
5952
6108
|
const projectChunkIdSet = new Set(projectChunkIds);
|
|
5953
6109
|
const projectSymbolIdSet = new Set(projectSymbolIds);
|
|
@@ -6031,7 +6187,7 @@ var Indexer = class {
|
|
|
6031
6187
|
if (!this.database || this.config.scope !== "global") {
|
|
6032
6188
|
return false;
|
|
6033
6189
|
}
|
|
6034
|
-
const projectHash = hashContent(
|
|
6190
|
+
const projectHash = hashContent(path10.resolve(this.projectRoot)).slice(0, 16);
|
|
6035
6191
|
const roots = this.getScopedRoots();
|
|
6036
6192
|
const { chunkIds: projectLocalChunkIds, symbolIds: projectLocalSymbolIds } = this.getProjectLocalScopedOwnershipIds(roots);
|
|
6037
6193
|
return this.database.getAllBranches().some(
|
|
@@ -6065,7 +6221,7 @@ var Indexer = class {
|
|
|
6065
6221
|
...Array.from(this.fileHashCache.keys()).filter((filePath) => this.isFileInCurrentScope(filePath, roots)),
|
|
6066
6222
|
...scopedEntries.map(({ metadata }) => metadata.filePath)
|
|
6067
6223
|
]);
|
|
6068
|
-
const projectRootPath =
|
|
6224
|
+
const projectRootPath = path10.resolve(this.projectRoot);
|
|
6069
6225
|
const projectLocalFilePaths = new Set(
|
|
6070
6226
|
Array.from(filePaths).filter((filePath) => isPathWithinRoot(filePath, projectRootPath))
|
|
6071
6227
|
);
|
|
@@ -6318,7 +6474,7 @@ var Indexer = class {
|
|
|
6318
6474
|
this.logger.search("warn", "External reranker failed; using deterministic order", {
|
|
6319
6475
|
provider: reranker.provider,
|
|
6320
6476
|
model: reranker.model,
|
|
6321
|
-
error:
|
|
6477
|
+
error: getErrorMessage2(error)
|
|
6322
6478
|
});
|
|
6323
6479
|
return candidates;
|
|
6324
6480
|
}
|
|
@@ -6425,13 +6581,13 @@ var Indexer = class {
|
|
|
6425
6581
|
}
|
|
6426
6582
|
await import_fs7.promises.mkdir(this.indexPath, { recursive: true });
|
|
6427
6583
|
const dimensions = this.configuredProviderInfo.modelInfo.dimensions;
|
|
6428
|
-
const storePath =
|
|
6584
|
+
const storePath = path10.join(this.indexPath, "vectors");
|
|
6429
6585
|
this.store = new VectorStore(storePath, dimensions);
|
|
6430
|
-
const indexFilePath =
|
|
6586
|
+
const indexFilePath = path10.join(this.indexPath, "vectors.usearch");
|
|
6431
6587
|
if ((0, import_fs7.existsSync)(indexFilePath)) {
|
|
6432
6588
|
this.store.load();
|
|
6433
6589
|
}
|
|
6434
|
-
const invertedIndexPath =
|
|
6590
|
+
const invertedIndexPath = path10.join(this.indexPath, "inverted-index.json");
|
|
6435
6591
|
this.invertedIndex = new InvertedIndex(invertedIndexPath);
|
|
6436
6592
|
try {
|
|
6437
6593
|
this.invertedIndex.load();
|
|
@@ -6441,7 +6597,7 @@ var Indexer = class {
|
|
|
6441
6597
|
}
|
|
6442
6598
|
this.invertedIndex = new InvertedIndex(invertedIndexPath);
|
|
6443
6599
|
}
|
|
6444
|
-
const dbPath =
|
|
6600
|
+
const dbPath = path10.join(this.indexPath, "codebase.db");
|
|
6445
6601
|
let dbIsNew = !(0, import_fs7.existsSync)(dbPath);
|
|
6446
6602
|
try {
|
|
6447
6603
|
this.database = new Database(dbPath);
|
|
@@ -6522,7 +6678,7 @@ var Indexer = class {
|
|
|
6522
6678
|
if (await this.tryResetCorruptedIndex("running automatic orphan garbage collection", error)) {
|
|
6523
6679
|
return {
|
|
6524
6680
|
resetCorruptedIndex: true,
|
|
6525
|
-
warning: this.getCorruptedIndexWarning(
|
|
6681
|
+
warning: this.getCorruptedIndexWarning(path10.join(this.indexPath, "codebase.db"))
|
|
6526
6682
|
};
|
|
6527
6683
|
}
|
|
6528
6684
|
throw error;
|
|
@@ -6537,7 +6693,7 @@ var Indexer = class {
|
|
|
6537
6693
|
return;
|
|
6538
6694
|
}
|
|
6539
6695
|
const retainedEntries = store.getAllMetadata().filter(({ key }) => !excludedSet.has(key));
|
|
6540
|
-
const storeBasePath =
|
|
6696
|
+
const storeBasePath = path10.join(this.indexPath, "vectors");
|
|
6541
6697
|
const storeIndexPath = `${storeBasePath}.usearch`;
|
|
6542
6698
|
const storeMetadataPath = `${storeBasePath}.meta.json`;
|
|
6543
6699
|
const backupIndexPath = `${storeIndexPath}.bak`;
|
|
@@ -6622,9 +6778,9 @@ var Indexer = class {
|
|
|
6622
6778
|
if (!isSqliteCorruptionError(error)) {
|
|
6623
6779
|
return false;
|
|
6624
6780
|
}
|
|
6625
|
-
const dbPath =
|
|
6781
|
+
const dbPath = path10.join(this.indexPath, "codebase.db");
|
|
6626
6782
|
const warning = this.getCorruptedIndexWarning(dbPath);
|
|
6627
|
-
const errorMessage =
|
|
6783
|
+
const errorMessage = getErrorMessage2(error);
|
|
6628
6784
|
if (this.config.scope === "global") {
|
|
6629
6785
|
this.logger.error("Detected corrupted shared global index database", {
|
|
6630
6786
|
stage,
|
|
@@ -6645,15 +6801,15 @@ var Indexer = class {
|
|
|
6645
6801
|
this.indexCompatibility = null;
|
|
6646
6802
|
this.fileHashCache.clear();
|
|
6647
6803
|
const resetPaths = [
|
|
6648
|
-
|
|
6649
|
-
|
|
6650
|
-
|
|
6651
|
-
|
|
6652
|
-
|
|
6653
|
-
|
|
6654
|
-
|
|
6655
|
-
|
|
6656
|
-
|
|
6804
|
+
path10.join(this.indexPath, "codebase.db"),
|
|
6805
|
+
path10.join(this.indexPath, "codebase.db-shm"),
|
|
6806
|
+
path10.join(this.indexPath, "codebase.db-wal"),
|
|
6807
|
+
path10.join(this.indexPath, "vectors.usearch"),
|
|
6808
|
+
path10.join(this.indexPath, "inverted-index.json"),
|
|
6809
|
+
path10.join(this.indexPath, "file-hashes.json"),
|
|
6810
|
+
path10.join(this.indexPath, "failed-batches.json"),
|
|
6811
|
+
path10.join(this.indexPath, "indexing.lock"),
|
|
6812
|
+
path10.join(this.indexPath, "vectors")
|
|
6657
6813
|
];
|
|
6658
6814
|
await Promise.all(resetPaths.map(async (targetPath) => {
|
|
6659
6815
|
try {
|
|
@@ -6918,7 +7074,7 @@ var Indexer = class {
|
|
|
6918
7074
|
for (const parsed of parsedFiles) {
|
|
6919
7075
|
currentFilePaths.add(parsed.path);
|
|
6920
7076
|
if (parsed.chunks.length === 0) {
|
|
6921
|
-
const relativePath =
|
|
7077
|
+
const relativePath = path10.relative(this.projectRoot, parsed.path);
|
|
6922
7078
|
stats.parseFailures.push(relativePath);
|
|
6923
7079
|
}
|
|
6924
7080
|
let fileChunkCount = 0;
|
|
@@ -7057,6 +7213,7 @@ var Indexer = class {
|
|
|
7057
7213
|
targetName: site.calleeName,
|
|
7058
7214
|
toSymbolId: void 0,
|
|
7059
7215
|
callType: site.callType,
|
|
7216
|
+
confidence: site.confidence,
|
|
7060
7217
|
line: site.line,
|
|
7061
7218
|
col: site.column,
|
|
7062
7219
|
isResolved: false
|
|
@@ -7201,7 +7358,7 @@ var Indexer = class {
|
|
|
7201
7358
|
for (const requestBatch of requestBatches) {
|
|
7202
7359
|
queue.add(async () => {
|
|
7203
7360
|
if (rateLimitBackoffMs > 0) {
|
|
7204
|
-
await new Promise((
|
|
7361
|
+
await new Promise((resolve11) => setTimeout(resolve11, rateLimitBackoffMs));
|
|
7205
7362
|
}
|
|
7206
7363
|
try {
|
|
7207
7364
|
const result = await pRetry(
|
|
@@ -7216,7 +7373,7 @@ var Indexer = class {
|
|
|
7216
7373
|
factor: 2,
|
|
7217
7374
|
shouldRetry: (error) => !(error.error instanceof CustomProviderNonRetryableError),
|
|
7218
7375
|
onFailedAttempt: (error) => {
|
|
7219
|
-
const message =
|
|
7376
|
+
const message = getErrorMessage2(error);
|
|
7220
7377
|
if (isRateLimitError(error)) {
|
|
7221
7378
|
rateLimitBackoffMs = Math.min(providerRateLimits.maxRetryMs, (rateLimitBackoffMs || providerRateLimits.minRetryMs) * 2);
|
|
7222
7379
|
this.logger.embedding("warn", `Rate limited, backing off`, {
|
|
@@ -7323,7 +7480,7 @@ var Indexer = class {
|
|
|
7323
7480
|
});
|
|
7324
7481
|
} catch (error) {
|
|
7325
7482
|
const failedChunks = getUniquePendingChunksFromRequests(requestBatch).filter((chunk) => !completedChunkIds.has(chunk.id));
|
|
7326
|
-
const failureMessage =
|
|
7483
|
+
const failureMessage = getErrorMessage2(error);
|
|
7327
7484
|
const failureTimestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
7328
7485
|
for (const chunk of failedChunks) {
|
|
7329
7486
|
if (!failedChunkIds.has(chunk.id)) {
|
|
@@ -7762,8 +7919,8 @@ var Indexer = class {
|
|
|
7762
7919
|
this.indexCompatibility = compatibility;
|
|
7763
7920
|
return;
|
|
7764
7921
|
}
|
|
7765
|
-
const localProjectIndexPath =
|
|
7766
|
-
if (
|
|
7922
|
+
const localProjectIndexPath = path10.join(this.projectRoot, ".opencode", "index");
|
|
7923
|
+
if (path10.resolve(this.indexPath) !== path10.resolve(localProjectIndexPath)) {
|
|
7767
7924
|
throw new Error(
|
|
7768
7925
|
"Project-scoped force rebuild is unsafe while using an inherited worktree index. Create a local project config boundary before clearing the index."
|
|
7769
7926
|
);
|
|
@@ -7851,7 +8008,7 @@ var Indexer = class {
|
|
|
7851
8008
|
gcOrphanSymbols: 0,
|
|
7852
8009
|
gcOrphanCallEdges: 0,
|
|
7853
8010
|
resetCorruptedIndex: true,
|
|
7854
|
-
warning: this.getCorruptedIndexWarning(
|
|
8011
|
+
warning: this.getCorruptedIndexWarning(path10.join(this.indexPath, "codebase.db"))
|
|
7855
8012
|
};
|
|
7856
8013
|
}
|
|
7857
8014
|
this.logger.recordGc(removedCount, gcOrphanChunks, gcOrphanEmbeddings);
|
|
@@ -8002,7 +8159,7 @@ var Indexer = class {
|
|
|
8002
8159
|
succeeded += successfulResults.length;
|
|
8003
8160
|
stillFailing.push(...failedChunksForBatch.values());
|
|
8004
8161
|
} catch (error) {
|
|
8005
|
-
const failureMessage =
|
|
8162
|
+
const failureMessage = getErrorMessage2(error);
|
|
8006
8163
|
const failureTimestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
8007
8164
|
const unaccountedChunks = batch.chunks.filter(
|
|
8008
8165
|
(chunk) => !failedChunksForBatch.has(chunk.id) && !completedChunkIds.has(chunk.id)
|
|
@@ -8176,12 +8333,12 @@ var Indexer = class {
|
|
|
8176
8333
|
})
|
|
8177
8334
|
);
|
|
8178
8335
|
}
|
|
8179
|
-
async getCallers(targetName) {
|
|
8336
|
+
async getCallers(targetName, callTypeFilter) {
|
|
8180
8337
|
const { database } = await this.ensureInitialized();
|
|
8181
8338
|
const seen = /* @__PURE__ */ new Set();
|
|
8182
8339
|
const results = [];
|
|
8183
8340
|
for (const branchKey of this.getBranchCatalogKeys()) {
|
|
8184
|
-
for (const edge of database.getCallersWithContext(targetName, branchKey)) {
|
|
8341
|
+
for (const edge of database.getCallersWithContext(targetName, branchKey, callTypeFilter)) {
|
|
8185
8342
|
if (!seen.has(edge.id)) {
|
|
8186
8343
|
seen.add(edge.id);
|
|
8187
8344
|
results.push(edge);
|
|
@@ -8190,12 +8347,12 @@ var Indexer = class {
|
|
|
8190
8347
|
}
|
|
8191
8348
|
return results;
|
|
8192
8349
|
}
|
|
8193
|
-
async getCallees(symbolId) {
|
|
8350
|
+
async getCallees(symbolId, callTypeFilter) {
|
|
8194
8351
|
const { database } = await this.ensureInitialized();
|
|
8195
8352
|
const seen = /* @__PURE__ */ new Set();
|
|
8196
8353
|
const results = [];
|
|
8197
8354
|
for (const branchKey of this.getBranchCatalogKeys()) {
|
|
8198
|
-
for (const edge of database.getCallees(symbolId, branchKey)) {
|
|
8355
|
+
for (const edge of database.getCallees(symbolId, branchKey, callTypeFilter)) {
|
|
8199
8356
|
if (!seen.has(edge.id)) {
|
|
8200
8357
|
seen.add(edge.id);
|
|
8201
8358
|
results.push(edge);
|
|
@@ -8204,6 +8361,198 @@ var Indexer = class {
|
|
|
8204
8361
|
}
|
|
8205
8362
|
return results;
|
|
8206
8363
|
}
|
|
8364
|
+
async findCallPath(fromName, toName, maxDepth) {
|
|
8365
|
+
const { database } = await this.ensureInitialized();
|
|
8366
|
+
let shortest = [];
|
|
8367
|
+
for (const branchKey of this.getBranchCatalogKeys()) {
|
|
8368
|
+
const path19 = database.findShortestPath(fromName, toName, branchKey, maxDepth);
|
|
8369
|
+
if (path19.length > 0 && (shortest.length === 0 || path19.length < shortest.length)) {
|
|
8370
|
+
shortest = path19;
|
|
8371
|
+
}
|
|
8372
|
+
}
|
|
8373
|
+
return shortest;
|
|
8374
|
+
}
|
|
8375
|
+
async getSymbolsForBranch(branch) {
|
|
8376
|
+
const { database } = await this.ensureInitialized();
|
|
8377
|
+
const resolvedBranch = branch ?? this.getBranchCatalogKey();
|
|
8378
|
+
return database.getSymbolsForBranch(resolvedBranch);
|
|
8379
|
+
}
|
|
8380
|
+
async getSymbolsForFiles(filePaths, branch) {
|
|
8381
|
+
const { database } = await this.ensureInitialized();
|
|
8382
|
+
const resolvedBranch = branch ?? this.getBranchCatalogKey();
|
|
8383
|
+
return database.getSymbolsForFiles(filePaths, resolvedBranch);
|
|
8384
|
+
}
|
|
8385
|
+
async getTransitiveReachability(rootSymbolIds, direction, maxDepth) {
|
|
8386
|
+
const { database } = await this.ensureInitialized();
|
|
8387
|
+
const branch = this.getBranchCatalogKey();
|
|
8388
|
+
return database.getTransitiveReachability(rootSymbolIds, branch, direction, maxDepth);
|
|
8389
|
+
}
|
|
8390
|
+
async detectCommunities(branch, symbolIds) {
|
|
8391
|
+
const { database } = await this.ensureInitialized();
|
|
8392
|
+
const resolvedBranch = branch ?? this.getBranchCatalogKey();
|
|
8393
|
+
return database.detectCommunities(resolvedBranch, symbolIds);
|
|
8394
|
+
}
|
|
8395
|
+
async computeCentrality(branch) {
|
|
8396
|
+
const { database } = await this.ensureInitialized();
|
|
8397
|
+
const resolvedBranch = branch ?? this.getBranchCatalogKey();
|
|
8398
|
+
return database.computeCentrality(resolvedBranch);
|
|
8399
|
+
}
|
|
8400
|
+
async getPrImpact(opts) {
|
|
8401
|
+
const { database } = await this.ensureInitialized();
|
|
8402
|
+
const execFileAsync2 = (0, import_util2.promisify)(import_child_process2.execFile);
|
|
8403
|
+
const changedFilesResult = await getChangedFiles({
|
|
8404
|
+
pr: opts.pr,
|
|
8405
|
+
branch: opts.branch,
|
|
8406
|
+
projectRoot: this.projectRoot,
|
|
8407
|
+
baseBranch: this.baseBranch
|
|
8408
|
+
});
|
|
8409
|
+
const changedFiles = changedFilesResult.files;
|
|
8410
|
+
const headRefName = changedFilesResult.headRefName;
|
|
8411
|
+
if (opts.pr !== void 0 && headRefName === void 0) {
|
|
8412
|
+
throw new Error(
|
|
8413
|
+
`Could not resolve head branch for PR #${opts.pr}. Run index_codebase on the PR branch first.`
|
|
8414
|
+
);
|
|
8415
|
+
}
|
|
8416
|
+
const resolvedBranch = opts.pr !== void 0 ? headRefName : opts.branch || this.currentBranch;
|
|
8417
|
+
const branchKey = this.getBranchCatalogKeyFor(resolvedBranch || "default");
|
|
8418
|
+
const branchSymbols = database.getSymbolsForBranch(branchKey);
|
|
8419
|
+
if (branchSymbols.length === 0) {
|
|
8420
|
+
throw new Error(
|
|
8421
|
+
"Run index_codebase first to build the call graph and symbol index for this branch."
|
|
8422
|
+
);
|
|
8423
|
+
}
|
|
8424
|
+
const absoluteChangedFiles = changedFiles.map((f) => path10.resolve(this.projectRoot, f));
|
|
8425
|
+
const directSymbols = database.getSymbolsForFiles(absoluteChangedFiles, branchKey);
|
|
8426
|
+
const directIds = directSymbols.map((s) => s.id);
|
|
8427
|
+
const direction = opts.direction ?? "both";
|
|
8428
|
+
const maxDepth = opts.maxDepth ?? 5;
|
|
8429
|
+
const transitiveCallers = database.getTransitiveReachability(
|
|
8430
|
+
directIds,
|
|
8431
|
+
branchKey,
|
|
8432
|
+
direction,
|
|
8433
|
+
maxDepth
|
|
8434
|
+
);
|
|
8435
|
+
const affectedIdsSet = new Set(directIds);
|
|
8436
|
+
for (const caller of transitiveCallers) {
|
|
8437
|
+
affectedIdsSet.add(caller.symbolId);
|
|
8438
|
+
}
|
|
8439
|
+
const allAffectedIds = Array.from(affectedIdsSet);
|
|
8440
|
+
const communitiesData = database.detectCommunities(branchKey, allAffectedIds);
|
|
8441
|
+
const communityMap = /* @__PURE__ */ new Map();
|
|
8442
|
+
for (const c of communitiesData) {
|
|
8443
|
+
if (!communityMap.has(c.communityLabel)) {
|
|
8444
|
+
communityMap.set(c.communityLabel, {
|
|
8445
|
+
label: c.communityLabel,
|
|
8446
|
+
symbolCount: 0,
|
|
8447
|
+
directSymbols: /* @__PURE__ */ new Set()
|
|
8448
|
+
});
|
|
8449
|
+
}
|
|
8450
|
+
const entry = communityMap.get(c.communityLabel);
|
|
8451
|
+
entry.symbolCount++;
|
|
8452
|
+
if (directIds.includes(c.symbolId)) {
|
|
8453
|
+
entry.directSymbols.add(c.symbolId);
|
|
8454
|
+
}
|
|
8455
|
+
}
|
|
8456
|
+
const communities = Array.from(communityMap.values()).map((c) => ({
|
|
8457
|
+
label: c.label,
|
|
8458
|
+
symbolCount: c.symbolCount,
|
|
8459
|
+
directSymbols: Array.from(c.directSymbols)
|
|
8460
|
+
}));
|
|
8461
|
+
const centralityData = database.computeCentrality(branchKey);
|
|
8462
|
+
const hubThreshold = opts.hubThreshold ?? 10;
|
|
8463
|
+
const hubNodes = centralityData.filter((c) => directIds.includes(c.symbolId) && c.callerCount >= hubThreshold).map((c) => ({
|
|
8464
|
+
id: c.symbolId,
|
|
8465
|
+
name: c.symbolName,
|
|
8466
|
+
callerCount: c.callerCount,
|
|
8467
|
+
filePath: c.filePath
|
|
8468
|
+
}));
|
|
8469
|
+
const totalAffected = allAffectedIds.length;
|
|
8470
|
+
let riskLevel;
|
|
8471
|
+
let riskReason;
|
|
8472
|
+
if (totalAffected < 5 && hubNodes.length === 0) {
|
|
8473
|
+
riskLevel = "LOW";
|
|
8474
|
+
riskReason = `Small impact: ${totalAffected} affected symbols, no hub nodes touched.`;
|
|
8475
|
+
} else if (totalAffected > 20 || hubNodes.length > 1) {
|
|
8476
|
+
riskLevel = "HIGH";
|
|
8477
|
+
riskReason = `Large impact: ${totalAffected} affected symbols${hubNodes.length > 0 ? `, ${hubNodes.length} hub nodes touched` : ""}.`;
|
|
8478
|
+
} else {
|
|
8479
|
+
riskLevel = "MEDIUM";
|
|
8480
|
+
riskReason = `Moderate impact: ${totalAffected} affected symbols${hubNodes.length === 1 ? ", 1 hub node touched" : ""}.`;
|
|
8481
|
+
}
|
|
8482
|
+
let conflictingPRs;
|
|
8483
|
+
if (opts.checkConflicts) {
|
|
8484
|
+
conflictingPRs = [];
|
|
8485
|
+
try {
|
|
8486
|
+
const { stdout } = await execFileAsync2(
|
|
8487
|
+
"gh",
|
|
8488
|
+
["pr", "list", "--state", "open", "--json", "number,headRefName", "--limit", "10000"],
|
|
8489
|
+
{ cwd: this.projectRoot, timeout: 3e4 }
|
|
8490
|
+
);
|
|
8491
|
+
const openPRs = JSON.parse(stdout);
|
|
8492
|
+
const currentCommunityLabels = new Set(communities.map((c) => c.label));
|
|
8493
|
+
const allCommunitiesData = database.detectCommunities(branchKey);
|
|
8494
|
+
const symbolToCommunity = /* @__PURE__ */ new Map();
|
|
8495
|
+
const structuralKey = (filePath, name) => `${filePath.toLowerCase()}:${name.toLowerCase()}`;
|
|
8496
|
+
for (const c of allCommunitiesData) {
|
|
8497
|
+
symbolToCommunity.set(structuralKey(c.filePath, c.symbolName), c.communityLabel);
|
|
8498
|
+
}
|
|
8499
|
+
for (const openPr of openPRs) {
|
|
8500
|
+
if (openPr.number === opts.pr) continue;
|
|
8501
|
+
try {
|
|
8502
|
+
const otherChanged = await getChangedFiles({
|
|
8503
|
+
pr: openPr.number,
|
|
8504
|
+
projectRoot: this.projectRoot,
|
|
8505
|
+
baseBranch: this.baseBranch
|
|
8506
|
+
});
|
|
8507
|
+
const otherAbsolute = otherChanged.files.map((f) => path10.resolve(this.projectRoot, f));
|
|
8508
|
+
const otherBranchKey = this.getBranchCatalogKeyFor(openPr.headRefName);
|
|
8509
|
+
const otherSymbols = database.getSymbolsForFiles(otherAbsolute, otherBranchKey);
|
|
8510
|
+
const otherLabels = /* @__PURE__ */ new Set();
|
|
8511
|
+
for (const sym of otherSymbols) {
|
|
8512
|
+
const label = symbolToCommunity.get(structuralKey(sym.filePath, sym.name));
|
|
8513
|
+
if (label) {
|
|
8514
|
+
otherLabels.add(label);
|
|
8515
|
+
}
|
|
8516
|
+
}
|
|
8517
|
+
const overlapping = Array.from(otherLabels).filter(
|
|
8518
|
+
(l) => currentCommunityLabels.has(l)
|
|
8519
|
+
);
|
|
8520
|
+
if (overlapping.length > 0) {
|
|
8521
|
+
conflictingPRs.push({
|
|
8522
|
+
pr: openPr.number,
|
|
8523
|
+
branch: openPr.headRefName,
|
|
8524
|
+
overlappingCommunities: overlapping
|
|
8525
|
+
});
|
|
8526
|
+
}
|
|
8527
|
+
} catch {
|
|
8528
|
+
}
|
|
8529
|
+
}
|
|
8530
|
+
} catch {
|
|
8531
|
+
}
|
|
8532
|
+
}
|
|
8533
|
+
return {
|
|
8534
|
+
changedFiles,
|
|
8535
|
+
directSymbols: directSymbols.map((s) => ({
|
|
8536
|
+
id: s.id,
|
|
8537
|
+
name: s.name,
|
|
8538
|
+
kind: s.kind,
|
|
8539
|
+
filePath: s.filePath
|
|
8540
|
+
})),
|
|
8541
|
+
transitiveCallers: transitiveCallers.map((c) => ({
|
|
8542
|
+
id: c.symbolId,
|
|
8543
|
+
name: c.symbolName,
|
|
8544
|
+
filePath: c.filePath,
|
|
8545
|
+
depth: c.depth
|
|
8546
|
+
})),
|
|
8547
|
+
totalAffected,
|
|
8548
|
+
communities,
|
|
8549
|
+
hubNodes,
|
|
8550
|
+
riskLevel,
|
|
8551
|
+
riskReason,
|
|
8552
|
+
direction,
|
|
8553
|
+
conflictingPRs
|
|
8554
|
+
};
|
|
8555
|
+
}
|
|
8207
8556
|
async close() {
|
|
8208
8557
|
await this.database?.close();
|
|
8209
8558
|
this.database = null;
|
|
@@ -8463,13 +8812,13 @@ function computeEvalMetrics(queries, perQuery, embeddingCallCount, embeddingToke
|
|
|
8463
8812
|
// src/eval/runner-config.ts
|
|
8464
8813
|
var import_fs8 = require("fs");
|
|
8465
8814
|
var os4 = __toESM(require("os"), 1);
|
|
8466
|
-
var
|
|
8815
|
+
var path12 = __toESM(require("path"), 1);
|
|
8467
8816
|
|
|
8468
8817
|
// src/config/rebase.ts
|
|
8469
|
-
var
|
|
8818
|
+
var path11 = __toESM(require("path"), 1);
|
|
8470
8819
|
function isWithinRoot(rootDir, targetPath) {
|
|
8471
|
-
const relativePath =
|
|
8472
|
-
return relativePath === "" || !relativePath.startsWith("..") && !
|
|
8820
|
+
const relativePath = path11.relative(rootDir, targetPath);
|
|
8821
|
+
return relativePath === "" || !relativePath.startsWith("..") && !path11.isAbsolute(relativePath);
|
|
8473
8822
|
}
|
|
8474
8823
|
function rebasePathEntries(values, fromDir, toDir) {
|
|
8475
8824
|
if (!Array.isArray(values)) {
|
|
@@ -8477,10 +8826,10 @@ function rebasePathEntries(values, fromDir, toDir) {
|
|
|
8477
8826
|
}
|
|
8478
8827
|
return values.filter((value) => typeof value === "string").map((value) => {
|
|
8479
8828
|
const trimmed = value.trim();
|
|
8480
|
-
if (!trimmed ||
|
|
8829
|
+
if (!trimmed || path11.isAbsolute(trimmed)) {
|
|
8481
8830
|
return trimmed;
|
|
8482
8831
|
}
|
|
8483
|
-
return normalizePathSeparators(
|
|
8832
|
+
return normalizePathSeparators(path11.normalize(path11.relative(toDir, path11.resolve(fromDir, trimmed))));
|
|
8484
8833
|
}).filter(Boolean);
|
|
8485
8834
|
}
|
|
8486
8835
|
function resolveInheritedKnowledgeBaseEntries(values, sourceRoot, targetRoot) {
|
|
@@ -8492,17 +8841,17 @@ function resolveInheritedKnowledgeBaseEntries(values, sourceRoot, targetRoot) {
|
|
|
8492
8841
|
if (!trimmed) {
|
|
8493
8842
|
return trimmed;
|
|
8494
8843
|
}
|
|
8495
|
-
if (
|
|
8844
|
+
if (path11.isAbsolute(trimmed)) {
|
|
8496
8845
|
if (isWithinRoot(sourceRoot, trimmed)) {
|
|
8497
|
-
return normalizePathSeparators(
|
|
8846
|
+
return normalizePathSeparators(path11.normalize(path11.relative(sourceRoot, trimmed) || "."));
|
|
8498
8847
|
}
|
|
8499
|
-
return
|
|
8848
|
+
return path11.normalize(trimmed);
|
|
8500
8849
|
}
|
|
8501
|
-
const resolvedFromSource =
|
|
8850
|
+
const resolvedFromSource = path11.resolve(sourceRoot, trimmed);
|
|
8502
8851
|
if (isWithinRoot(sourceRoot, resolvedFromSource)) {
|
|
8503
|
-
return normalizePathSeparators(
|
|
8852
|
+
return normalizePathSeparators(path11.normalize(trimmed));
|
|
8504
8853
|
}
|
|
8505
|
-
return normalizePathSeparators(
|
|
8854
|
+
return normalizePathSeparators(path11.normalize(path11.relative(targetRoot, resolvedFromSource)));
|
|
8506
8855
|
}).filter(Boolean);
|
|
8507
8856
|
}
|
|
8508
8857
|
|
|
@@ -8550,20 +8899,20 @@ function parseJsonConfigFile(filePath) {
|
|
|
8550
8899
|
}
|
|
8551
8900
|
}
|
|
8552
8901
|
function toAbsolute(projectRoot, maybeRelative) {
|
|
8553
|
-
return
|
|
8902
|
+
return path12.isAbsolute(maybeRelative) ? maybeRelative : path12.join(projectRoot, maybeRelative);
|
|
8554
8903
|
}
|
|
8555
8904
|
function isProjectScopedConfigPath(configPath) {
|
|
8556
|
-
return
|
|
8905
|
+
return path12.basename(configPath) === "codebase-index.json" && path12.basename(path12.dirname(configPath)) === ".opencode";
|
|
8557
8906
|
}
|
|
8558
8907
|
function normalizeEvalConfigKnowledgeBases(rawConfig, projectRoot, resolvedConfigPath) {
|
|
8559
8908
|
const config = rawConfig && typeof rawConfig === "object" ? { ...rawConfig } : {};
|
|
8560
8909
|
const rebaseEntries = (values) => isProjectScopedConfigPath(resolvedConfigPath) ? resolveInheritedKnowledgeBaseEntries(
|
|
8561
8910
|
values,
|
|
8562
|
-
|
|
8911
|
+
path12.dirname(path12.dirname(resolvedConfigPath)),
|
|
8563
8912
|
projectRoot
|
|
8564
8913
|
) : rebasePathEntries(
|
|
8565
8914
|
values,
|
|
8566
|
-
|
|
8915
|
+
path12.dirname(resolvedConfigPath),
|
|
8567
8916
|
projectRoot
|
|
8568
8917
|
);
|
|
8569
8918
|
if (Array.isArray(config.knowledgeBases)) {
|
|
@@ -8591,7 +8940,7 @@ function loadRawConfig(projectRoot, configPath) {
|
|
|
8591
8940
|
projectConfig
|
|
8592
8941
|
);
|
|
8593
8942
|
}
|
|
8594
|
-
const globalConfig =
|
|
8943
|
+
const globalConfig = path12.join(os4.homedir(), ".config", "opencode", "codebase-index.json");
|
|
8595
8944
|
if ((0, import_fs8.existsSync)(globalConfig)) {
|
|
8596
8945
|
return parseJsonConfigFile(globalConfig);
|
|
8597
8946
|
}
|
|
@@ -8601,10 +8950,10 @@ function getIndexRootPath(projectRoot, scope) {
|
|
|
8601
8950
|
return scope === "global" ? getGlobalIndexPath() : resolveProjectIndexPath(projectRoot, scope);
|
|
8602
8951
|
}
|
|
8603
8952
|
function getLocalProjectIndexRoot(projectRoot) {
|
|
8604
|
-
return
|
|
8953
|
+
return path12.join(projectRoot, ".opencode", "index");
|
|
8605
8954
|
}
|
|
8606
8955
|
function getLocalProjectConfigPath(projectRoot) {
|
|
8607
|
-
return
|
|
8956
|
+
return path12.join(projectRoot, ".opencode", "codebase-index.json");
|
|
8608
8957
|
}
|
|
8609
8958
|
function clearIndexRoot(projectRoot, scope) {
|
|
8610
8959
|
const indexRoot = scope === "global" ? getIndexRootPath(projectRoot, scope) : getLocalProjectIndexRoot(projectRoot);
|
|
@@ -8626,7 +8975,7 @@ function ensureLocalEvalProjectConfig(projectRoot, configPath) {
|
|
|
8626
8975
|
projectRoot,
|
|
8627
8976
|
resolvedConfigPath
|
|
8628
8977
|
);
|
|
8629
|
-
(0, import_fs8.mkdirSync)(
|
|
8978
|
+
(0, import_fs8.mkdirSync)(path12.dirname(localConfigPath), { recursive: true });
|
|
8630
8979
|
(0, import_fs8.writeFileSync)(localConfigPath, JSON.stringify(sourceConfig, null, 2), "utf-8");
|
|
8631
8980
|
return localConfigPath;
|
|
8632
8981
|
}
|
|
@@ -8676,23 +9025,23 @@ function isRecord2(value) {
|
|
|
8676
9025
|
function isStringArray3(value) {
|
|
8677
9026
|
return Array.isArray(value) && value.every((item) => typeof item === "string");
|
|
8678
9027
|
}
|
|
8679
|
-
function asPositiveNumber(value,
|
|
9028
|
+
function asPositiveNumber(value, path19) {
|
|
8680
9029
|
if (typeof value !== "number" || Number.isNaN(value) || value < 0) {
|
|
8681
|
-
throw new Error(`${
|
|
9030
|
+
throw new Error(`${path19} must be a non-negative number`);
|
|
8682
9031
|
}
|
|
8683
9032
|
return value;
|
|
8684
9033
|
}
|
|
8685
|
-
function parseQueryType(value,
|
|
9034
|
+
function parseQueryType(value, path19) {
|
|
8686
9035
|
if (value === "definition" || value === "implementation-intent" || value === "similarity" || value === "keyword-heavy") {
|
|
8687
9036
|
return value;
|
|
8688
9037
|
}
|
|
8689
9038
|
throw new Error(
|
|
8690
|
-
`${
|
|
9039
|
+
`${path19} must be one of: definition, implementation-intent, similarity, keyword-heavy`
|
|
8691
9040
|
);
|
|
8692
9041
|
}
|
|
8693
|
-
function parseExpected(input,
|
|
9042
|
+
function parseExpected(input, path19) {
|
|
8694
9043
|
if (!isRecord2(input)) {
|
|
8695
|
-
throw new Error(`${
|
|
9044
|
+
throw new Error(`${path19} must be an object`);
|
|
8696
9045
|
}
|
|
8697
9046
|
const filePathRaw = input.filePath;
|
|
8698
9047
|
const acceptableFilesRaw = input.acceptableFiles;
|
|
@@ -8701,16 +9050,16 @@ function parseExpected(input, path18) {
|
|
|
8701
9050
|
const filePath = typeof filePathRaw === "string" ? filePathRaw : void 0;
|
|
8702
9051
|
const acceptableFiles = isStringArray3(acceptableFilesRaw) ? acceptableFilesRaw : void 0;
|
|
8703
9052
|
if (!filePath && (!acceptableFiles || acceptableFiles.length === 0)) {
|
|
8704
|
-
throw new Error(`${
|
|
9053
|
+
throw new Error(`${path19} must include either expected.filePath or expected.acceptableFiles`);
|
|
8705
9054
|
}
|
|
8706
9055
|
if (acceptableFilesRaw !== void 0 && !isStringArray3(acceptableFilesRaw)) {
|
|
8707
|
-
throw new Error(`${
|
|
9056
|
+
throw new Error(`${path19}.acceptableFiles must be an array of strings`);
|
|
8708
9057
|
}
|
|
8709
9058
|
if (symbolRaw !== void 0 && typeof symbolRaw !== "string") {
|
|
8710
|
-
throw new Error(`${
|
|
9059
|
+
throw new Error(`${path19}.symbol must be a string when provided`);
|
|
8711
9060
|
}
|
|
8712
9061
|
if (branchRaw !== void 0 && typeof branchRaw !== "string") {
|
|
8713
|
-
throw new Error(`${
|
|
9062
|
+
throw new Error(`${path19}.branch must be a string when provided`);
|
|
8714
9063
|
}
|
|
8715
9064
|
return {
|
|
8716
9065
|
filePath,
|
|
@@ -8720,25 +9069,25 @@ function parseExpected(input, path18) {
|
|
|
8720
9069
|
};
|
|
8721
9070
|
}
|
|
8722
9071
|
function parseQuery(input, index) {
|
|
8723
|
-
const
|
|
9072
|
+
const path19 = `queries[${index}]`;
|
|
8724
9073
|
if (!isRecord2(input)) {
|
|
8725
|
-
throw new Error(`${
|
|
9074
|
+
throw new Error(`${path19} must be an object`);
|
|
8726
9075
|
}
|
|
8727
9076
|
const id = input.id;
|
|
8728
9077
|
const query = input.query;
|
|
8729
9078
|
const queryType = input.queryType;
|
|
8730
9079
|
const expected = input.expected;
|
|
8731
9080
|
if (typeof id !== "string" || id.trim().length === 0) {
|
|
8732
|
-
throw new Error(`${
|
|
9081
|
+
throw new Error(`${path19}.id must be a non-empty string`);
|
|
8733
9082
|
}
|
|
8734
9083
|
if (typeof query !== "string" || query.trim().length === 0) {
|
|
8735
|
-
throw new Error(`${
|
|
9084
|
+
throw new Error(`${path19}.query must be a non-empty string`);
|
|
8736
9085
|
}
|
|
8737
9086
|
return {
|
|
8738
9087
|
id,
|
|
8739
9088
|
query,
|
|
8740
|
-
queryType: parseQueryType(queryType, `${
|
|
8741
|
-
expected: parseExpected(expected, `${
|
|
9089
|
+
queryType: parseQueryType(queryType, `${path19}.queryType`),
|
|
9090
|
+
expected: parseExpected(expected, `${path19}.expected`)
|
|
8742
9091
|
};
|
|
8743
9092
|
}
|
|
8744
9093
|
function parseGoldenDataset(raw, sourceLabel) {
|
|
@@ -8921,13 +9270,13 @@ async function runEvaluation(options) {
|
|
|
8921
9270
|
};
|
|
8922
9271
|
const outputDir = createRunDirectory(toAbsolute(options.projectRoot, options.outputRoot));
|
|
8923
9272
|
const perQueryArtifact = buildPerQueryArtifact(perQuery);
|
|
8924
|
-
writeJson(
|
|
8925
|
-
writeJson(
|
|
9273
|
+
writeJson(path13.join(outputDir, "summary.json"), summary);
|
|
9274
|
+
writeJson(path13.join(outputDir, "per-query.json"), perQueryArtifact);
|
|
8926
9275
|
let comparison;
|
|
8927
9276
|
if (againstPath) {
|
|
8928
9277
|
const baseline = loadSummary(againstPath);
|
|
8929
9278
|
comparison = compareSummaries(summary, baseline, againstPath);
|
|
8930
|
-
writeJson(
|
|
9279
|
+
writeJson(path13.join(outputDir, "compare.json"), comparison);
|
|
8931
9280
|
}
|
|
8932
9281
|
let gate;
|
|
8933
9282
|
if (options.ciMode) {
|
|
@@ -8940,7 +9289,7 @@ async function runEvaluation(options) {
|
|
|
8940
9289
|
if ((0, import_fs10.existsSync)(resolvedBaseline)) {
|
|
8941
9290
|
const baselineSummary = loadSummary(resolvedBaseline);
|
|
8942
9291
|
comparison = compareSummaries(summary, baselineSummary, resolvedBaseline);
|
|
8943
|
-
writeJson(
|
|
9292
|
+
writeJson(path13.join(outputDir, "compare.json"), comparison);
|
|
8944
9293
|
} else if (budget.failOnMissingBaseline) {
|
|
8945
9294
|
throw new Error(
|
|
8946
9295
|
`Budget baseline is missing: ${resolvedBaseline}. Set failOnMissingBaseline=false to allow CI run without baseline.`
|
|
@@ -8950,7 +9299,7 @@ async function runEvaluation(options) {
|
|
|
8950
9299
|
gate = evaluateBudgetGate(budget, summary, comparison);
|
|
8951
9300
|
}
|
|
8952
9301
|
const markdown = createSummaryMarkdown(summary, comparison, gate);
|
|
8953
|
-
writeText(
|
|
9302
|
+
writeText(path13.join(outputDir, "summary.md"), markdown);
|
|
8954
9303
|
return { outputDir, summary, perQuery, comparison, gate };
|
|
8955
9304
|
} finally {
|
|
8956
9305
|
await indexer.close();
|
|
@@ -9008,23 +9357,23 @@ async function runSweep(options, sweep) {
|
|
|
9008
9357
|
bestByMrrAt10,
|
|
9009
9358
|
bestByP95Latency
|
|
9010
9359
|
};
|
|
9011
|
-
writeJson(
|
|
9360
|
+
writeJson(path13.join(outputDir, "compare.json"), aggregate);
|
|
9012
9361
|
const md = createSummaryMarkdown(
|
|
9013
9362
|
bestByHitAt5?.summary ?? runs[0].summary,
|
|
9014
9363
|
bestByHitAt5?.comparison,
|
|
9015
9364
|
void 0,
|
|
9016
9365
|
aggregate
|
|
9017
9366
|
);
|
|
9018
|
-
writeText(
|
|
9019
|
-
writeJson(
|
|
9367
|
+
writeText(path13.join(outputDir, "summary.md"), md);
|
|
9368
|
+
writeJson(path13.join(outputDir, "summary.json"), bestByHitAt5?.summary ?? runs[0].summary);
|
|
9020
9369
|
return { outputDir, aggregate };
|
|
9021
9370
|
}
|
|
9022
9371
|
|
|
9023
9372
|
// src/eval/cli.ts
|
|
9024
|
-
var
|
|
9373
|
+
var path15 = __toESM(require("path"), 1);
|
|
9025
9374
|
|
|
9026
9375
|
// src/eval/cli-parser.ts
|
|
9027
|
-
var
|
|
9376
|
+
var path14 = __toESM(require("path"), 1);
|
|
9028
9377
|
function printUsage() {
|
|
9029
9378
|
console.log(`
|
|
9030
9379
|
Usage:
|
|
@@ -9096,12 +9445,12 @@ function parseEvalArgs(argv, cwd) {
|
|
|
9096
9445
|
const arg = argv[i];
|
|
9097
9446
|
const next = argv[i + 1];
|
|
9098
9447
|
if (arg === "--project" && next) {
|
|
9099
|
-
parsed.projectRoot =
|
|
9448
|
+
parsed.projectRoot = path14.resolve(cwd, next);
|
|
9100
9449
|
i += 1;
|
|
9101
9450
|
continue;
|
|
9102
9451
|
}
|
|
9103
9452
|
if (arg === "--config" && next) {
|
|
9104
|
-
parsed.configPath =
|
|
9453
|
+
parsed.configPath = path14.resolve(cwd, next);
|
|
9105
9454
|
i += 1;
|
|
9106
9455
|
continue;
|
|
9107
9456
|
}
|
|
@@ -9297,22 +9646,22 @@ async function handleEvalCommand(args, cwd) {
|
|
|
9297
9646
|
if (!parsed.againstPath.endsWith(".json")) {
|
|
9298
9647
|
throw new Error("eval diff --against must point to a summary JSON file");
|
|
9299
9648
|
}
|
|
9300
|
-
const currentSummary = loadSummary(
|
|
9649
|
+
const currentSummary = loadSummary(path15.resolve(parsed.projectRoot, currentPath), {
|
|
9301
9650
|
allowLegacyDiversityMetrics: true
|
|
9302
9651
|
});
|
|
9303
|
-
const baselineSummary = loadSummary(
|
|
9652
|
+
const baselineSummary = loadSummary(path15.resolve(parsed.projectRoot, parsed.againstPath), {
|
|
9304
9653
|
allowLegacyDiversityMetrics: true
|
|
9305
9654
|
});
|
|
9306
9655
|
const comparison = compareSummaries(
|
|
9307
9656
|
currentSummary,
|
|
9308
9657
|
baselineSummary,
|
|
9309
|
-
|
|
9658
|
+
path15.resolve(parsed.projectRoot, parsed.againstPath)
|
|
9310
9659
|
);
|
|
9311
|
-
const outputDir = createRunDirectory(
|
|
9660
|
+
const outputDir = createRunDirectory(path15.resolve(parsed.projectRoot, parsed.outputRoot));
|
|
9312
9661
|
const summaryMd = createSummaryMarkdown(currentSummary, comparison);
|
|
9313
|
-
writeJson(
|
|
9314
|
-
writeText(
|
|
9315
|
-
writeJson(
|
|
9662
|
+
writeJson(path15.join(outputDir, "compare.json"), comparison);
|
|
9663
|
+
writeText(path15.join(outputDir, "summary.md"), summaryMd);
|
|
9664
|
+
writeJson(path15.join(outputDir, "summary.json"), currentSummary);
|
|
9316
9665
|
console.log(`Eval diff complete. Artifacts: ${outputDir}`);
|
|
9317
9666
|
return 0;
|
|
9318
9667
|
}
|
|
@@ -9321,7 +9670,7 @@ async function handleEvalCommand(args, cwd) {
|
|
|
9321
9670
|
|
|
9322
9671
|
// src/mcp-server.ts
|
|
9323
9672
|
var import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
|
|
9324
|
-
var
|
|
9673
|
+
var path17 = __toESM(require("path"), 1);
|
|
9325
9674
|
var import_fs12 = require("fs");
|
|
9326
9675
|
|
|
9327
9676
|
// src/mcp-server/register-prompts.ts
|
|
@@ -9420,7 +9769,7 @@ var import_zod2 = require("zod");
|
|
|
9420
9769
|
// src/config/merger.ts
|
|
9421
9770
|
var import_fs11 = require("fs");
|
|
9422
9771
|
var os5 = __toESM(require("os"), 1);
|
|
9423
|
-
var
|
|
9772
|
+
var path16 = __toESM(require("path"), 1);
|
|
9424
9773
|
var PROJECT_OVERRIDE_KEYS = [
|
|
9425
9774
|
"embeddingProvider",
|
|
9426
9775
|
"customProvider",
|
|
@@ -9453,8 +9802,8 @@ function mergeUniqueStringArray(values) {
|
|
|
9453
9802
|
return [...new Set(values.map((value) => String(value).trim()))];
|
|
9454
9803
|
}
|
|
9455
9804
|
function normalizeKnowledgeBasePath(value) {
|
|
9456
|
-
let normalized =
|
|
9457
|
-
const root =
|
|
9805
|
+
let normalized = path16.normalize(String(value).trim());
|
|
9806
|
+
const root = path16.parse(normalized).root;
|
|
9458
9807
|
while (normalized.length > root.length && /[\\/]$/.test(normalized)) {
|
|
9459
9808
|
normalized = normalized.slice(0, -1);
|
|
9460
9809
|
}
|
|
@@ -9504,8 +9853,8 @@ function loadJsonFile(filePath) {
|
|
|
9504
9853
|
return null;
|
|
9505
9854
|
}
|
|
9506
9855
|
function materializeLocalProjectConfig(projectRoot, config) {
|
|
9507
|
-
const localConfigPath =
|
|
9508
|
-
(0, import_fs11.mkdirSync)(
|
|
9856
|
+
const localConfigPath = path16.join(projectRoot, ".opencode", "codebase-index.json");
|
|
9857
|
+
(0, import_fs11.mkdirSync)(path16.dirname(localConfigPath), { recursive: true });
|
|
9509
9858
|
(0, import_fs11.writeFileSync)(localConfigPath, JSON.stringify(config, null, 2), "utf-8");
|
|
9510
9859
|
return localConfigPath;
|
|
9511
9860
|
}
|
|
@@ -9516,7 +9865,7 @@ function loadProjectConfigLayer(projectRoot) {
|
|
|
9516
9865
|
return {};
|
|
9517
9866
|
}
|
|
9518
9867
|
const normalizedConfig = { ...projectConfig };
|
|
9519
|
-
const projectConfigBaseDir =
|
|
9868
|
+
const projectConfigBaseDir = path16.dirname(path16.dirname(projectConfigPath));
|
|
9520
9869
|
if (Array.isArray(normalizedConfig.knowledgeBases)) {
|
|
9521
9870
|
normalizedConfig.knowledgeBases = resolveInheritedKnowledgeBaseEntries(
|
|
9522
9871
|
normalizedConfig.knowledgeBases,
|
|
@@ -9735,6 +10084,57 @@ ${truncateContent(r.content)}
|
|
|
9735
10084
|
return formatted.join("\n\n");
|
|
9736
10085
|
}
|
|
9737
10086
|
|
|
10087
|
+
// src/tools/format-pr-impact.ts
|
|
10088
|
+
function formatPrImpact(result) {
|
|
10089
|
+
const lines = [];
|
|
10090
|
+
lines.push(`\u2192 Files changed: ${result.changedFiles.length}`);
|
|
10091
|
+
for (const file of result.changedFiles) {
|
|
10092
|
+
lines.push(` - ${file}`);
|
|
10093
|
+
}
|
|
10094
|
+
const directCount = result.directSymbols.length;
|
|
10095
|
+
const transitiveCount = result.transitiveCallers.length;
|
|
10096
|
+
const directionLabel = result.direction === "callees" ? "callees" : result.direction === "both" ? "reachable (callers + callees)" : "callers";
|
|
10097
|
+
lines.push(
|
|
10098
|
+
`\u2192 Symbols affected: ${result.totalAffected} (${directCount} direct, ${transitiveCount} transitive ${directionLabel})`
|
|
10099
|
+
);
|
|
10100
|
+
if (directCount > 0) {
|
|
10101
|
+
lines.push(
|
|
10102
|
+
` Direct: ${result.directSymbols.map((s) => `${s.name} (${s.kind})`).join(", ")}`
|
|
10103
|
+
);
|
|
10104
|
+
}
|
|
10105
|
+
if (transitiveCount > 0) {
|
|
10106
|
+
lines.push(
|
|
10107
|
+
` Transitive ${directionLabel}: ${result.transitiveCallers.map((s) => s.name).join(", ")}`
|
|
10108
|
+
);
|
|
10109
|
+
}
|
|
10110
|
+
if (result.communities.length > 0) {
|
|
10111
|
+
lines.push(
|
|
10112
|
+
`\u2192 Communities touched: ${result.communities.map((c) => c.label).join(", ")}`
|
|
10113
|
+
);
|
|
10114
|
+
for (const community of result.communities) {
|
|
10115
|
+
lines.push(
|
|
10116
|
+
` - ${community.label}: ${community.symbolCount} symbols`
|
|
10117
|
+
);
|
|
10118
|
+
}
|
|
10119
|
+
} else {
|
|
10120
|
+
lines.push("\u2192 Communities touched: none");
|
|
10121
|
+
}
|
|
10122
|
+
lines.push(`\u2192 Risk: ${result.riskLevel} \u2014 ${result.riskReason}`);
|
|
10123
|
+
if (result.hubNodes.length > 0) {
|
|
10124
|
+
lines.push(" Hub nodes in change scope:");
|
|
10125
|
+
for (const hub of result.hubNodes) {
|
|
10126
|
+
lines.push(` - ${hub.name} (${hub.callerCount} callers) at ${hub.filePath}`);
|
|
10127
|
+
}
|
|
10128
|
+
}
|
|
10129
|
+
if (result.conflictingPRs && result.conflictingPRs.length > 0) {
|
|
10130
|
+
const conflictList = result.conflictingPRs.map(
|
|
10131
|
+
(c) => `PR #${c.pr} (also touches ${c.overlappingCommunities.join(", ")} community)`
|
|
10132
|
+
);
|
|
10133
|
+
lines.push(`\u2192 Potential conflicts with: ${conflictList.join(", ")}`);
|
|
10134
|
+
}
|
|
10135
|
+
return lines.join("\n");
|
|
10136
|
+
}
|
|
10137
|
+
|
|
9738
10138
|
// src/mcp-server/shared.ts
|
|
9739
10139
|
var MAX_CONTENT_LINES2 = 30;
|
|
9740
10140
|
function truncateContent2(content) {
|
|
@@ -9979,11 +10379,12 @@ ${formatted.join("\n\n")}` }] };
|
|
|
9979
10379
|
);
|
|
9980
10380
|
server.tool(
|
|
9981
10381
|
"call_graph",
|
|
9982
|
-
"Query the call graph to find callers or callees of a function/method. Use to understand code flow and dependencies.",
|
|
10382
|
+
"Query the call graph to find callers or callees of a function/method. Use to understand code flow and dependencies. Supports relationship types: Call, MethodCall, Constructor, Import, Inherits, Implements.",
|
|
9983
10383
|
{
|
|
9984
10384
|
name: import_zod2.z.string().describe("Function or method name to query"),
|
|
9985
10385
|
direction: import_zod2.z.enum(["callers", "callees"]).default("callers").describe("Direction: 'callers' finds who calls this function, 'callees' finds what this function calls"),
|
|
9986
|
-
symbolId: import_zod2.z.string().optional().describe("Symbol ID (required for 'callees' direction)")
|
|
10386
|
+
symbolId: import_zod2.z.string().optional().describe("Symbol ID (required for 'callees' direction)"),
|
|
10387
|
+
relationshipType: import_zod2.z.enum(["Call", "MethodCall", "Constructor", "Import", "Inherits", "Implements"]).optional().describe("Filter by relationship type. Omit to show all.")
|
|
9987
10388
|
},
|
|
9988
10389
|
async (args) => {
|
|
9989
10390
|
await runtime.ensureInitialized();
|
|
@@ -9992,29 +10393,85 @@ ${formatted.join("\n\n")}` }] };
|
|
|
9992
10393
|
if (!args.symbolId) {
|
|
9993
10394
|
return { content: [{ type: "text", text: "Error: 'symbolId' is required when direction is 'callees'." }] };
|
|
9994
10395
|
}
|
|
9995
|
-
const callees = await indexer.getCallees(args.symbolId);
|
|
10396
|
+
const callees = await indexer.getCallees(args.symbolId, args.relationshipType);
|
|
9996
10397
|
if (callees.length === 0) {
|
|
9997
|
-
return { content: [{ type: "text", text: `No callees found for symbol ${args.symbolId}.` }] };
|
|
10398
|
+
return { content: [{ type: "text", text: `No callees found for symbol ${args.symbolId}${args.relationshipType ? ` with type ${args.relationshipType}` : ""}.` }] };
|
|
9998
10399
|
}
|
|
9999
|
-
const formatted2 = callees.map(
|
|
10000
|
-
|
|
10001
|
-
|
|
10400
|
+
const formatted2 = callees.map((e, i) => {
|
|
10401
|
+
const conf = e.confidence !== "Direct" ? ` [${e.confidence.toLowerCase()}]` : "";
|
|
10402
|
+
return `[${i + 1}] \u2192 ${e.targetName} (${e.callType})${conf} at line ${e.line}${e.isResolved ? ` [resolved: ${e.toSymbolId}]` : " [unresolved]"}`;
|
|
10403
|
+
});
|
|
10002
10404
|
return { content: [{ type: "text", text: `Callees (${callees.length}):
|
|
10003
10405
|
|
|
10004
10406
|
${formatted2.join("\n")}` }] };
|
|
10005
10407
|
}
|
|
10006
|
-
const callers = await indexer.getCallers(args.name);
|
|
10408
|
+
const callers = await indexer.getCallers(args.name, args.relationshipType);
|
|
10007
10409
|
if (callers.length === 0) {
|
|
10008
|
-
return { content: [{ type: "text", text: `No callers found for "${args.name}".` }] };
|
|
10410
|
+
return { content: [{ type: "text", text: `No callers found for "${args.name}"${args.relationshipType ? ` with type ${args.relationshipType}` : ""}.` }] };
|
|
10009
10411
|
}
|
|
10010
|
-
const formatted = callers.map(
|
|
10011
|
-
|
|
10012
|
-
|
|
10412
|
+
const formatted = callers.map((e, i) => {
|
|
10413
|
+
const conf = e.confidence !== "Direct" ? ` [${e.confidence.toLowerCase()}]` : "";
|
|
10414
|
+
return `[${i + 1}] \u2190 from ${e.fromSymbolName ?? "<unknown>"} in ${e.fromSymbolFilePath ?? "<unknown file>"} [${e.fromSymbolId}] (${e.callType})${conf} at line ${e.line}${e.isResolved ? " [resolved]" : " [unresolved]"}`;
|
|
10415
|
+
});
|
|
10013
10416
|
return { content: [{ type: "text", text: `"${args.name}" is called by ${callers.length} function(s):
|
|
10014
10417
|
|
|
10015
10418
|
${formatted.join("\n")}` }] };
|
|
10016
10419
|
}
|
|
10017
10420
|
);
|
|
10421
|
+
server.tool(
|
|
10422
|
+
"call_graph_path",
|
|
10423
|
+
"Find the shortest connection path between two symbols in the call graph. Returns the chain of calls connecting them.",
|
|
10424
|
+
{
|
|
10425
|
+
from: import_zod2.z.string().describe("Source function/method name (starting point)"),
|
|
10426
|
+
to: import_zod2.z.string().describe("Target function/method name (destination)"),
|
|
10427
|
+
maxDepth: import_zod2.z.number().optional().default(10).describe("Maximum traversal depth (default: 10)")
|
|
10428
|
+
},
|
|
10429
|
+
async (args) => {
|
|
10430
|
+
await runtime.ensureInitialized();
|
|
10431
|
+
const indexer = runtime.getIndexer();
|
|
10432
|
+
const path19 = await indexer.findCallPath(args.from, args.to, args.maxDepth);
|
|
10433
|
+
if (path19.length === 0) {
|
|
10434
|
+
return { content: [{ type: "text", text: `No path found between "${args.from}" and "${args.to}". They may be in disconnected components, or the call graph index needs updating.` }] };
|
|
10435
|
+
}
|
|
10436
|
+
const formatted = path19.map((hop, i) => {
|
|
10437
|
+
const prefix = i === 0 ? "[start]" : `--${hop.callType}-->`;
|
|
10438
|
+
const location = hop.filePath ? ` (${hop.filePath}:${hop.line})` : "";
|
|
10439
|
+
return `${prefix} ${hop.symbolName}${location}`;
|
|
10440
|
+
});
|
|
10441
|
+
return { content: [{ type: "text", text: `Path (${path19.length} hops):
|
|
10442
|
+
${formatted.join("\n")}` }] };
|
|
10443
|
+
}
|
|
10444
|
+
);
|
|
10445
|
+
server.tool(
|
|
10446
|
+
"pr_impact",
|
|
10447
|
+
"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.",
|
|
10448
|
+
{
|
|
10449
|
+
pr: import_zod2.z.number().optional().describe("Pull request number to analyze"),
|
|
10450
|
+
branch: import_zod2.z.string().optional().describe("Branch name to analyze (defaults to current branch)"),
|
|
10451
|
+
maxDepth: import_zod2.z.number().optional().default(5).describe("Maximum traversal depth for transitive callers (default: 5)"),
|
|
10452
|
+
hubThreshold: import_zod2.z.number().optional().default(10).describe("Minimum caller count to flag a symbol as a hub node (default: 10)"),
|
|
10453
|
+
checkConflicts: import_zod2.z.boolean().optional().default(false).describe("Check for conflicting open PRs touching the same communities (default: false)"),
|
|
10454
|
+
direction: import_zod2.z.enum(["callers", "callees", "both"]).optional().default("both").describe("Call-graph traversal direction: 'callers' for upstream, 'callees' for downstream, 'both' for union (default: both)")
|
|
10455
|
+
},
|
|
10456
|
+
async (args) => {
|
|
10457
|
+
await runtime.ensureInitialized();
|
|
10458
|
+
const indexer = runtime.getIndexer();
|
|
10459
|
+
try {
|
|
10460
|
+
const result = await indexer.getPrImpact({
|
|
10461
|
+
pr: args.pr,
|
|
10462
|
+
branch: args.branch,
|
|
10463
|
+
maxDepth: args.maxDepth,
|
|
10464
|
+
hubThreshold: args.hubThreshold,
|
|
10465
|
+
checkConflicts: args.checkConflicts,
|
|
10466
|
+
direction: args.direction
|
|
10467
|
+
});
|
|
10468
|
+
return { content: [{ type: "text", text: formatPrImpact(result) }] };
|
|
10469
|
+
} catch (error) {
|
|
10470
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
10471
|
+
return { content: [{ type: "text", text: `Error analyzing PR impact: ${message}` }] };
|
|
10472
|
+
}
|
|
10473
|
+
}
|
|
10474
|
+
);
|
|
10018
10475
|
}
|
|
10019
10476
|
|
|
10020
10477
|
// src/mcp-server.ts
|
|
@@ -10033,12 +10490,12 @@ function createMcpServer(projectRoot, config) {
|
|
|
10033
10490
|
if (config.scope !== "project") {
|
|
10034
10491
|
return false;
|
|
10035
10492
|
}
|
|
10036
|
-
const localIndexPath =
|
|
10493
|
+
const localIndexPath = path17.join(projectRoot, ".opencode", "index");
|
|
10037
10494
|
const mainRepoRoot = resolveWorktreeMainRepoRoot(projectRoot);
|
|
10038
10495
|
if (!mainRepoRoot) {
|
|
10039
10496
|
return false;
|
|
10040
10497
|
}
|
|
10041
|
-
const inheritedIndexPath =
|
|
10498
|
+
const inheritedIndexPath = path17.join(mainRepoRoot, ".opencode", "index");
|
|
10042
10499
|
return !(0, import_fs12.existsSync)(localIndexPath) && (0, import_fs12.existsSync)(inheritedIndexPath);
|
|
10043
10500
|
}
|
|
10044
10501
|
async function ensureInitialized() {
|
|
@@ -10064,9 +10521,9 @@ function parseArgs(argv) {
|
|
|
10064
10521
|
let config;
|
|
10065
10522
|
for (let i = 2; i < argv.length; i++) {
|
|
10066
10523
|
if (argv[i] === "--project" && argv[i + 1]) {
|
|
10067
|
-
project =
|
|
10524
|
+
project = path18.resolve(argv[++i]);
|
|
10068
10525
|
} else if (argv[i] === "--config" && argv[i + 1]) {
|
|
10069
|
-
config =
|
|
10526
|
+
config = path18.resolve(argv[++i]);
|
|
10070
10527
|
}
|
|
10071
10528
|
}
|
|
10072
10529
|
return { project, config };
|