opencode-codebase-index 0.22.2 → 0.22.4
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/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/dist/cli.cjs +668 -96
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +668 -96
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +442 -57
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +442 -57
- package/dist/index.js.map +1 -1
- package/dist/pi-extension.cjs +402 -49
- package/dist/pi-extension.cjs.map +1 -1
- package/dist/pi-extension.js +402 -49
- package/dist/pi-extension.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 +13 -9
package/dist/cli.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
//
|
|
2
|
+
// open-codebase-index - Semantic codebase indexing and search
|
|
3
3
|
"use strict";
|
|
4
4
|
var __create = Object.create;
|
|
5
5
|
var __defProp = Object.defineProperty;
|
|
@@ -1291,6 +1291,7 @@ function createSummaryMarkdown(summary, comparison, gate, sweep) {
|
|
|
1291
1291
|
lines.push(`| Hit@10 | ${formatPct(summary.metrics.hitAt10)} |`);
|
|
1292
1292
|
lines.push(`| MRR@10 | ${summary.metrics.mrrAt10.toFixed(4)} |`);
|
|
1293
1293
|
lines.push(`| nDCG@10 | ${summary.metrics.ndcgAt10.toFixed(4)} |`);
|
|
1294
|
+
lines.push(`| Graph-neighbor recall | ${(summary.metrics.graphNeighborRecall ?? 0).toFixed(4)} |`);
|
|
1294
1295
|
lines.push(`| Distinct Top@3 | ${formatPct(summary.metrics.distinctTop3Ratio)} |`);
|
|
1295
1296
|
lines.push(`| Raw Distinct Top@3 | ${formatPct(summary.metrics.rawDistinctTop3Ratio)} |`);
|
|
1296
1297
|
lines.push(`| Latency p50 | ${formatMs(summary.metrics.latencyMs.p50)} |`);
|
|
@@ -6655,12 +6656,12 @@ function diversifyGroupBySymbol(entries, getCandidate) {
|
|
|
6655
6656
|
return [...primary, ...remainder];
|
|
6656
6657
|
}
|
|
6657
6658
|
function buildDiversityKey(metadata) {
|
|
6658
|
-
const
|
|
6659
|
+
const normalizedPath3 = metadata.filePath.toLowerCase();
|
|
6659
6660
|
const normalizedName = (metadata.name ?? "").trim().toLowerCase();
|
|
6660
6661
|
if (normalizedName.length > 0) {
|
|
6661
|
-
return `${
|
|
6662
|
+
return `${normalizedPath3}#${normalizedName}`;
|
|
6662
6663
|
}
|
|
6663
|
-
return
|
|
6664
|
+
return normalizedPath3;
|
|
6664
6665
|
}
|
|
6665
6666
|
function rankHybridResults(query, semanticResults, keywordResults, options) {
|
|
6666
6667
|
const prioritizeSourcePaths = options.prioritizeSourcePaths ?? classifyQueryIntentRaw(query) === "source";
|
|
@@ -7109,9 +7110,9 @@ function normalizeFilePathForHintMatch(filePath) {
|
|
|
7109
7110
|
return filePath.replace(/\\/g, "/").toLowerCase().replace(/^\.\//, "");
|
|
7110
7111
|
}
|
|
7111
7112
|
function pathMatchesHint(filePath, hint) {
|
|
7112
|
-
const
|
|
7113
|
+
const normalizedPath3 = normalizeFilePathForHintMatch(filePath);
|
|
7113
7114
|
const normalizedHint = normalizeFilePathForHintMatch(hint);
|
|
7114
|
-
return
|
|
7115
|
+
return normalizedPath3.endsWith(normalizedHint) || normalizedPath3.includes(`/${normalizedHint}`) || normalizedPath3.includes(normalizedHint);
|
|
7115
7116
|
}
|
|
7116
7117
|
function extractFilePathHint(query) {
|
|
7117
7118
|
const match = query.match(FILE_PATH_HINT_SUFFIX_REGEX);
|
|
@@ -11375,6 +11376,20 @@ var Indexer = class _Indexer {
|
|
|
11375
11376
|
requestedLimit = nextLimit;
|
|
11376
11377
|
}
|
|
11377
11378
|
}
|
|
11379
|
+
buildCandidateSnapshot(candidate) {
|
|
11380
|
+
return {
|
|
11381
|
+
id: candidate.id,
|
|
11382
|
+
filePath: candidate.metadata.filePath,
|
|
11383
|
+
startLine: candidate.metadata.startLine,
|
|
11384
|
+
endLine: candidate.metadata.endLine,
|
|
11385
|
+
score: candidate.score,
|
|
11386
|
+
chunkType: candidate.metadata.chunkType,
|
|
11387
|
+
name: candidate.metadata.name
|
|
11388
|
+
};
|
|
11389
|
+
}
|
|
11390
|
+
buildCandidateSnapshotList(candidates) {
|
|
11391
|
+
return candidates.map((candidate) => this.buildCandidateSnapshot(candidate));
|
|
11392
|
+
}
|
|
11378
11393
|
searchSemanticCandidates(store, embedding, initialLimit, branchChunkIds, shouldPrefilterByBranch) {
|
|
11379
11394
|
return this.searchCandidatesWithBranchPrefilter(
|
|
11380
11395
|
initialLimit,
|
|
@@ -11566,6 +11581,16 @@ var Indexer = class _Indexer {
|
|
|
11566
11581
|
prefilterMs: Math.round(prefilterMs * 100) / 100,
|
|
11567
11582
|
fusionMs: Math.round(fusionMs * 100) / 100
|
|
11568
11583
|
});
|
|
11584
|
+
if (options?.trace) {
|
|
11585
|
+
options.trace({
|
|
11586
|
+
semanticCandidates: this.buildCandidateSnapshotList(scopedSemanticCandidates),
|
|
11587
|
+
keywordCandidates: this.buildCandidateSnapshotList(scopedKeywordCandidates),
|
|
11588
|
+
hybridCandidates: this.buildCandidateSnapshotList(combined),
|
|
11589
|
+
postExternalRerankCandidates: this.buildCandidateSnapshotList(rerankedCombined),
|
|
11590
|
+
tieredCandidates: this.buildCandidateSnapshotList(tiered),
|
|
11591
|
+
finalCandidates: this.buildCandidateSnapshotList(finalResults)
|
|
11592
|
+
});
|
|
11593
|
+
}
|
|
11569
11594
|
const metadataOnly = options?.metadataOnly ?? false;
|
|
11570
11595
|
return Promise.all(
|
|
11571
11596
|
finalResults.map(async (r) => {
|
|
@@ -12614,6 +12639,42 @@ var Indexer = class _Indexer {
|
|
|
12614
12639
|
}
|
|
12615
12640
|
};
|
|
12616
12641
|
|
|
12642
|
+
// src/tools/contracts.ts
|
|
12643
|
+
var CHUNK_TYPES = [
|
|
12644
|
+
"function",
|
|
12645
|
+
"class",
|
|
12646
|
+
"method",
|
|
12647
|
+
"interface",
|
|
12648
|
+
"type",
|
|
12649
|
+
"enum",
|
|
12650
|
+
"struct",
|
|
12651
|
+
"impl",
|
|
12652
|
+
"trait",
|
|
12653
|
+
"module",
|
|
12654
|
+
"other"
|
|
12655
|
+
];
|
|
12656
|
+
var CALL_GRAPH_DIRECTIONS = ["callers", "callees"];
|
|
12657
|
+
var RELATIONSHIP_TYPES = [
|
|
12658
|
+
"Call",
|
|
12659
|
+
"MethodCall",
|
|
12660
|
+
"Constructor",
|
|
12661
|
+
"Import",
|
|
12662
|
+
"Inherits",
|
|
12663
|
+
"Implements"
|
|
12664
|
+
];
|
|
12665
|
+
var INDEX_LOG_CATEGORIES = ["search", "embedding", "cache", "gc", "branch", "general"];
|
|
12666
|
+
var INDEX_LOG_LEVELS = ["error", "warn", "info", "debug"];
|
|
12667
|
+
var MIN_CODEBASE_EDIT_CONTEXT_EDGE_LIMIT = 1;
|
|
12668
|
+
var MAX_CODEBASE_EDIT_CONTEXT_EDGE_LIMIT = 20;
|
|
12669
|
+
var DEFAULT_CODEBASE_EDIT_CONTEXT_EDGE_LIMIT = 5;
|
|
12670
|
+
var CODE_COMMUNITIES_MIN_SIZE = 1;
|
|
12671
|
+
var CODE_COMMUNITIES_DEFAULT_LIMIT = 20;
|
|
12672
|
+
var CODE_COMMUNITIES_MAX_LIMIT = 100;
|
|
12673
|
+
var CODE_COMMUNITIES_DEFAULT_HUB_THRESHOLD = 5;
|
|
12674
|
+
var CODE_COMMUNITIES_MIN_COUPLING = 1;
|
|
12675
|
+
var CODE_COMMUNITIES_DEFAULT_COUPLING_LIMIT = 20;
|
|
12676
|
+
var CODE_COMMUNITIES_MAX_COUPLING_LIMIT = 100;
|
|
12677
|
+
|
|
12617
12678
|
// src/tools/operations.ts
|
|
12618
12679
|
var import_fs14 = require("fs");
|
|
12619
12680
|
var path21 = __toESM(require("path"), 1);
|
|
@@ -12742,39 +12803,6 @@ function formatCodeCommunities(result) {
|
|
|
12742
12803
|
return lines.join("\n");
|
|
12743
12804
|
}
|
|
12744
12805
|
|
|
12745
|
-
// src/tools/contracts.ts
|
|
12746
|
-
var CHUNK_TYPES = [
|
|
12747
|
-
"function",
|
|
12748
|
-
"class",
|
|
12749
|
-
"method",
|
|
12750
|
-
"interface",
|
|
12751
|
-
"type",
|
|
12752
|
-
"enum",
|
|
12753
|
-
"struct",
|
|
12754
|
-
"impl",
|
|
12755
|
-
"trait",
|
|
12756
|
-
"module",
|
|
12757
|
-
"other"
|
|
12758
|
-
];
|
|
12759
|
-
var CALL_GRAPH_DIRECTIONS = ["callers", "callees"];
|
|
12760
|
-
var RELATIONSHIP_TYPES = [
|
|
12761
|
-
"Call",
|
|
12762
|
-
"MethodCall",
|
|
12763
|
-
"Constructor",
|
|
12764
|
-
"Import",
|
|
12765
|
-
"Inherits",
|
|
12766
|
-
"Implements"
|
|
12767
|
-
];
|
|
12768
|
-
var INDEX_LOG_CATEGORIES = ["search", "embedding", "cache", "gc", "branch", "general"];
|
|
12769
|
-
var INDEX_LOG_LEVELS = ["error", "warn", "info", "debug"];
|
|
12770
|
-
var CODE_COMMUNITIES_MIN_SIZE = 1;
|
|
12771
|
-
var CODE_COMMUNITIES_DEFAULT_LIMIT = 20;
|
|
12772
|
-
var CODE_COMMUNITIES_MAX_LIMIT = 100;
|
|
12773
|
-
var CODE_COMMUNITIES_DEFAULT_HUB_THRESHOLD = 5;
|
|
12774
|
-
var CODE_COMMUNITIES_MIN_COUPLING = 1;
|
|
12775
|
-
var CODE_COMMUNITIES_DEFAULT_COUPLING_LIMIT = 20;
|
|
12776
|
-
var CODE_COMMUNITIES_MAX_COUPLING_LIMIT = 100;
|
|
12777
|
-
|
|
12778
12806
|
// src/tools/context-pack.ts
|
|
12779
12807
|
var import_tiktoken = require("tiktoken");
|
|
12780
12808
|
var MIN_CONTEXT_PACK_TOKEN_BUDGET = 128;
|
|
@@ -12876,6 +12904,16 @@ function compactEvidenceValue(value, maxChars) {
|
|
|
12876
12904
|
}
|
|
12877
12905
|
var MAX_EXACT_SEARCH_HANDOFF_NAMES = 3;
|
|
12878
12906
|
var MAX_EXACT_SEARCH_HANDOFF_NAME_CHARS = 64;
|
|
12907
|
+
function toContextPackTraceCandidate(result) {
|
|
12908
|
+
return {
|
|
12909
|
+
filePath: result.filePath,
|
|
12910
|
+
startLine: result.startLine,
|
|
12911
|
+
endLine: result.endLine,
|
|
12912
|
+
score: result.score,
|
|
12913
|
+
chunkType: result.chunkType,
|
|
12914
|
+
name: result.name
|
|
12915
|
+
};
|
|
12916
|
+
}
|
|
12879
12917
|
function formatExactSearchHandoff(results) {
|
|
12880
12918
|
const suggestedNames = [];
|
|
12881
12919
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -12924,13 +12962,13 @@ function buildContextPack(results, options = {}) {
|
|
|
12924
12962
|
const maxResults = Math.max(0, Math.floor(options.maxResults ?? results.length));
|
|
12925
12963
|
const includeExactSearchHandoff = options.includeExactSearchHandoff ?? false;
|
|
12926
12964
|
const candidateCount = results.length;
|
|
12927
|
-
const
|
|
12928
|
-
|
|
12929
|
-
|
|
12930
|
-
|
|
12931
|
-
|
|
12932
|
-
);
|
|
12933
|
-
const
|
|
12965
|
+
const preserveInputOrder = options.preserveInputOrder ?? false;
|
|
12966
|
+
const ranked = preserveInputOrder ? results.map((result, originalIndex) => ({ result, originalIndex })) : rankContextCandidates(results, options.preferImplementationPaths ?? false);
|
|
12967
|
+
const rankedCandidates = ranked.map((entry) => toContextPackTraceCandidate(entry.result));
|
|
12968
|
+
const deduplicated = deduplicateContextCandidates(ranked);
|
|
12969
|
+
const deduplicatedCandidates = deduplicated.map((result) => toContextPackTraceCandidate(result));
|
|
12970
|
+
const diversified = preserveInputOrder ? deduplicated : diversifyContextCandidates(deduplicated);
|
|
12971
|
+
const diversifiedCandidates = diversified.map((result) => toContextPackTraceCandidate(result));
|
|
12934
12972
|
const duplicateCount = candidateCount - deduplicated.length;
|
|
12935
12973
|
const selectable = diversified.slice(0, maxResults);
|
|
12936
12974
|
const limitOmittedCount = deduplicated.length - selectable.length;
|
|
@@ -12963,6 +13001,15 @@ function buildContextPack(results, options = {}) {
|
|
|
12963
13001
|
const fitted = fitTextToContextBudget(text, tokenBudget);
|
|
12964
13002
|
const budgetOmittedCount = selectable.length - selected.length;
|
|
12965
13003
|
const omittedCount = candidateCount - selected.length;
|
|
13004
|
+
if (options.trace) {
|
|
13005
|
+
options.trace({
|
|
13006
|
+
inputCandidates: results.map(toContextPackTraceCandidate),
|
|
13007
|
+
rankedCandidates,
|
|
13008
|
+
deduplicatedCandidates,
|
|
13009
|
+
diversifiedCandidates,
|
|
13010
|
+
selectedCandidates: selected.map(toContextPackTraceCandidate)
|
|
13011
|
+
});
|
|
13012
|
+
}
|
|
12966
13013
|
return {
|
|
12967
13014
|
requestedTokenBudget,
|
|
12968
13015
|
tokenBudget,
|
|
@@ -14710,7 +14757,8 @@ async function searchCodebase(projectRoot, host, query, options = {}) {
|
|
|
14710
14757
|
definitionIntent: options.definitionIntent,
|
|
14711
14758
|
blameAuthor: options.blameAuthor,
|
|
14712
14759
|
blameSha: options.blameSha,
|
|
14713
|
-
blameSince: options.blameSince
|
|
14760
|
+
blameSince: options.blameSince,
|
|
14761
|
+
trace: options.trace
|
|
14714
14762
|
});
|
|
14715
14763
|
}
|
|
14716
14764
|
async function searchCodebaseWithEffectiveness(projectRoot, host, route, query, options, render) {
|
|
@@ -14764,15 +14812,19 @@ async function implementationLookup(projectRoot, host, query, options = {}) {
|
|
|
14764
14812
|
return indexer.search(query, options.limit, {
|
|
14765
14813
|
fileType: options.fileType,
|
|
14766
14814
|
directory: options.directory,
|
|
14767
|
-
definitionIntent: true
|
|
14815
|
+
definitionIntent: true,
|
|
14816
|
+
trace: options.trace
|
|
14768
14817
|
});
|
|
14769
14818
|
}
|
|
14770
14819
|
async function getCallGraphData(projectRoot, host, params) {
|
|
14771
14820
|
await ensureAutoIndexReadyForRetrieval(projectRoot, host);
|
|
14772
14821
|
const root = getProjectRoot(projectRoot, host);
|
|
14773
14822
|
const indexer = getIndexerForProject(root, host);
|
|
14823
|
+
return getCallGraphDataForIndexer(indexer, root, params);
|
|
14824
|
+
}
|
|
14825
|
+
async function getCallGraphDataForIndexer(indexer, projectRoot, params) {
|
|
14774
14826
|
const symbols = await indexer.getCallGraphSymbols();
|
|
14775
|
-
const resolution = resolveCallGraphSymbol(symbols,
|
|
14827
|
+
const resolution = resolveCallGraphSymbol(symbols, projectRoot, params.name, params.filePath, params.symbolId);
|
|
14776
14828
|
const direction = params.direction === "callees" ? "callees" : "callers";
|
|
14777
14829
|
if (resolution.status !== "resolved") {
|
|
14778
14830
|
return { direction, resolution, callers: [], callees: [], relationshipType: params.relationshipType };
|
|
@@ -15100,6 +15152,27 @@ function buildRecoveryDetails(attempts, successIndex) {
|
|
|
15100
15152
|
successfulAttemptIndex: successIndex
|
|
15101
15153
|
};
|
|
15102
15154
|
}
|
|
15155
|
+
function serializeAttempts(attempts) {
|
|
15156
|
+
return attempts.map((attempt) => ({
|
|
15157
|
+
kind: attempt.kind,
|
|
15158
|
+
scope: attempt.scope,
|
|
15159
|
+
resultCount: attempt.resultCount,
|
|
15160
|
+
relaxedFields: attempt.relaxedFields
|
|
15161
|
+
}));
|
|
15162
|
+
}
|
|
15163
|
+
function buildSearchDiagnostic(attempt) {
|
|
15164
|
+
if (!attempt) {
|
|
15165
|
+
return void 0;
|
|
15166
|
+
}
|
|
15167
|
+
return {
|
|
15168
|
+
route: attempt.kind,
|
|
15169
|
+
routedQuery: attempt.query,
|
|
15170
|
+
searchQuery: attempt.query,
|
|
15171
|
+
searchScope: attempt.scopeFilter,
|
|
15172
|
+
searchTrace: attempt.searchTrace,
|
|
15173
|
+
contextPackTrace: attempt.contextPackTrace
|
|
15174
|
+
};
|
|
15175
|
+
}
|
|
15103
15176
|
function trimOrUndefined2(value) {
|
|
15104
15177
|
const normalized = value?.trim();
|
|
15105
15178
|
if (!normalized) {
|
|
@@ -15139,6 +15212,7 @@ async function resolveSearchContext(input, operations) {
|
|
|
15139
15212
|
const hasFilters = Boolean(fileType || directory);
|
|
15140
15213
|
const relaxedFields = relaxedHintFields(fileType, directory);
|
|
15141
15214
|
const attempts = [];
|
|
15215
|
+
const attemptStates = [];
|
|
15142
15216
|
const decisions = {
|
|
15143
15217
|
inferredDefinitionMiss: false,
|
|
15144
15218
|
fallbackFromOriginalConceptualToInferred: false,
|
|
@@ -15167,8 +15241,20 @@ async function resolveSearchContext(input, operations) {
|
|
|
15167
15241
|
if (seenAttempts.has(key)) {
|
|
15168
15242
|
return [];
|
|
15169
15243
|
}
|
|
15170
|
-
const
|
|
15244
|
+
const attemptState = {
|
|
15245
|
+
kind,
|
|
15246
|
+
scope: describeScope(scope.fileType, scope.directory),
|
|
15247
|
+
resultCount: 0,
|
|
15248
|
+
relaxedFields: [...relaxedFieldsForAttempt],
|
|
15249
|
+
query: attemptQuery,
|
|
15250
|
+
scopeFilter: scope
|
|
15251
|
+
};
|
|
15252
|
+
const results = await runAttempt((trace) => {
|
|
15253
|
+
attemptState.searchTrace = trace;
|
|
15254
|
+
});
|
|
15255
|
+
attemptState.resultCount = results.length;
|
|
15171
15256
|
seenAttempts.add(key);
|
|
15257
|
+
attemptStates.push(attemptState);
|
|
15172
15258
|
attempts.push({
|
|
15173
15259
|
kind,
|
|
15174
15260
|
scope: describeScope(scope.fileType, scope.directory),
|
|
@@ -15188,7 +15274,7 @@ async function resolveSearchContext(input, operations) {
|
|
|
15188
15274
|
symbol,
|
|
15189
15275
|
scope,
|
|
15190
15276
|
relaxedFieldsForAttempt,
|
|
15191
|
-
() => operations.lookup(symbol, MAX_CONTEXT_RESULT_LIMIT, scope)
|
|
15277
|
+
(trace) => operations.lookup(symbol, MAX_CONTEXT_RESULT_LIMIT, scope, input.diagnostic ? trace : void 0)
|
|
15192
15278
|
);
|
|
15193
15279
|
};
|
|
15194
15280
|
const tryConceptualSearch = async (searchQuery, scope, relaxedFieldsForAttempt) => {
|
|
@@ -15197,13 +15283,23 @@ async function resolveSearchContext(input, operations) {
|
|
|
15197
15283
|
searchQuery,
|
|
15198
15284
|
scope,
|
|
15199
15285
|
relaxedFieldsForAttempt,
|
|
15200
|
-
() => operations.search(searchQuery, MAX_CONTEXT_RESULT_LIMIT, scope)
|
|
15286
|
+
(trace) => operations.search(searchQuery, MAX_CONTEXT_RESULT_LIMIT, scope, input.diagnostic ? trace : void 0)
|
|
15201
15287
|
);
|
|
15202
15288
|
};
|
|
15203
|
-
const
|
|
15289
|
+
const findSuccessfulAttemptState = (route) => {
|
|
15290
|
+
for (let index = attemptStates.length - 1; index >= 0; index -= 1) {
|
|
15291
|
+
const attempt = attemptStates[index];
|
|
15292
|
+
if (attempt.kind === route && attempt.resultCount > 0) {
|
|
15293
|
+
return attempt;
|
|
15294
|
+
}
|
|
15295
|
+
}
|
|
15296
|
+
return void 0;
|
|
15297
|
+
};
|
|
15298
|
+
const toResult = (route, routedQuery, pack, successfulAttempt) => {
|
|
15204
15299
|
const base = packedResult(route, routedQuery, pack);
|
|
15205
15300
|
const baseDetails = base.details;
|
|
15206
15301
|
const successIndex = findSuccessfulAttemptIndex(route, attempts);
|
|
15302
|
+
const successState = successfulAttempt ?? findSuccessfulAttemptState(route);
|
|
15207
15303
|
return {
|
|
15208
15304
|
text: base.text,
|
|
15209
15305
|
details: {
|
|
@@ -15211,7 +15307,10 @@ async function resolveSearchContext(input, operations) {
|
|
|
15211
15307
|
tokenBudget: baseDetails.tokenBudget,
|
|
15212
15308
|
tokenEstimate: baseDetails.tokenEstimate,
|
|
15213
15309
|
truncated: false,
|
|
15214
|
-
recovery: buildRecoveryDetails(
|
|
15310
|
+
recovery: buildRecoveryDetails(serializeAttempts(attemptStates), successIndex),
|
|
15311
|
+
...input.diagnostic && {
|
|
15312
|
+
diagnostic: buildSearchDiagnostic(successState)
|
|
15313
|
+
}
|
|
15215
15314
|
}
|
|
15216
15315
|
};
|
|
15217
15316
|
};
|
|
@@ -15225,8 +15324,18 @@ async function resolveSearchContext(input, operations) {
|
|
|
15225
15324
|
buildContextPack(scopedDefinitionResults, {
|
|
15226
15325
|
tokenBudget,
|
|
15227
15326
|
maxResults: limit,
|
|
15228
|
-
heading
|
|
15229
|
-
|
|
15327
|
+
heading,
|
|
15328
|
+
preserveInputOrder: true,
|
|
15329
|
+
...input.diagnostic ? {
|
|
15330
|
+
trace: (trace) => {
|
|
15331
|
+
const attemptState = findSuccessfulAttemptState("definition");
|
|
15332
|
+
if (attemptState) {
|
|
15333
|
+
attemptState.contextPackTrace = trace;
|
|
15334
|
+
}
|
|
15335
|
+
}
|
|
15336
|
+
} : void 0
|
|
15337
|
+
}),
|
|
15338
|
+
findSuccessfulAttemptState("definition")
|
|
15230
15339
|
);
|
|
15231
15340
|
}
|
|
15232
15341
|
if (explicitSymbol) {
|
|
@@ -15244,8 +15353,18 @@ async function resolveSearchContext(input, operations) {
|
|
|
15244
15353
|
buildContextPack(unscopedDefinitionResults, {
|
|
15245
15354
|
tokenBudget,
|
|
15246
15355
|
maxResults: limit,
|
|
15247
|
-
heading: heading2
|
|
15248
|
-
|
|
15356
|
+
heading: heading2,
|
|
15357
|
+
preserveInputOrder: true,
|
|
15358
|
+
...input.diagnostic ? {
|
|
15359
|
+
trace: (trace) => {
|
|
15360
|
+
const attemptState = findSuccessfulAttemptState("definition");
|
|
15361
|
+
if (attemptState) {
|
|
15362
|
+
attemptState.contextPackTrace = trace;
|
|
15363
|
+
}
|
|
15364
|
+
}
|
|
15365
|
+
} : void 0
|
|
15366
|
+
}),
|
|
15367
|
+
findSuccessfulAttemptState("definition")
|
|
15249
15368
|
);
|
|
15250
15369
|
}
|
|
15251
15370
|
}
|
|
@@ -15264,7 +15383,10 @@ Explicit symbol lookup only; conceptual search was not attempted.`
|
|
|
15264
15383
|
tokenBudget: heading.tokenBudget,
|
|
15265
15384
|
tokenEstimate: heading.tokenEstimate,
|
|
15266
15385
|
truncated: heading.truncated,
|
|
15267
|
-
recovery: buildRecoveryDetails(
|
|
15386
|
+
recovery: buildRecoveryDetails(serializeAttempts(attemptStates), null),
|
|
15387
|
+
...input.diagnostic && {
|
|
15388
|
+
diagnostic: buildSearchDiagnostic(attemptStates[attemptStates.length - 1])
|
|
15389
|
+
}
|
|
15268
15390
|
}
|
|
15269
15391
|
};
|
|
15270
15392
|
}
|
|
@@ -15304,8 +15426,17 @@ Explicit symbol lookup only; conceptual search was not attempted.`
|
|
|
15304
15426
|
maxResults: limit,
|
|
15305
15427
|
heading,
|
|
15306
15428
|
includeExactSearchHandoff: true,
|
|
15307
|
-
preferImplementationPaths: intent.preferSourcePaths || intent.primary !== "docs" && intent.primary !== "test"
|
|
15308
|
-
|
|
15429
|
+
preferImplementationPaths: intent.preferSourcePaths || intent.primary !== "docs" && intent.primary !== "test",
|
|
15430
|
+
...input.diagnostic ? {
|
|
15431
|
+
trace: (trace) => {
|
|
15432
|
+
const attemptState = findSuccessfulAttemptState("conceptual");
|
|
15433
|
+
if (attemptState) {
|
|
15434
|
+
attemptState.contextPackTrace = trace;
|
|
15435
|
+
}
|
|
15436
|
+
}
|
|
15437
|
+
} : void 0
|
|
15438
|
+
}),
|
|
15439
|
+
findSuccessfulAttemptState("conceptual")
|
|
15309
15440
|
);
|
|
15310
15441
|
}
|
|
15311
15442
|
}
|
|
@@ -15321,7 +15452,10 @@ Explicit symbol lookup only; conceptual search was not attempted.`
|
|
|
15321
15452
|
tokenBudget: fallbackText.tokenBudget,
|
|
15322
15453
|
tokenEstimate: fallbackText.tokenEstimate,
|
|
15323
15454
|
truncated: fallbackText.truncated,
|
|
15324
|
-
recovery: buildRecoveryDetails(
|
|
15455
|
+
recovery: buildRecoveryDetails(serializeAttempts(attemptStates), null),
|
|
15456
|
+
...input.diagnostic && {
|
|
15457
|
+
diagnostic: buildSearchDiagnostic(attemptStates[attemptStates.length - 1])
|
|
15458
|
+
}
|
|
15325
15459
|
}
|
|
15326
15460
|
};
|
|
15327
15461
|
}
|
|
@@ -15402,17 +15536,27 @@ async function resolveCodebaseContextUnmeasured(projectRoot, host, input) {
|
|
|
15402
15536
|
details: fittedDetails("path", fitted, 0)
|
|
15403
15537
|
};
|
|
15404
15538
|
}
|
|
15405
|
-
return resolveSearchContext({
|
|
15406
|
-
|
|
15539
|
+
return resolveSearchContext({
|
|
15540
|
+
query: input.query,
|
|
15541
|
+
symbol,
|
|
15542
|
+
limit,
|
|
15543
|
+
tokenBudget,
|
|
15544
|
+
fileType,
|
|
15545
|
+
directory,
|
|
15546
|
+
diagnostic: input.diagnostic
|
|
15547
|
+
}, {
|
|
15548
|
+
lookup: (lookupSymbol, retrievalLimit, scope, trace) => implementationLookup(projectRoot, host, lookupSymbol, {
|
|
15407
15549
|
limit: retrievalLimit,
|
|
15408
15550
|
fileType: scope.fileType,
|
|
15409
|
-
directory: scope.directory
|
|
15551
|
+
directory: scope.directory,
|
|
15552
|
+
trace
|
|
15410
15553
|
}),
|
|
15411
|
-
search: (queryText, retrievalLimit, scope) => searchCodebase(projectRoot, host, queryText, {
|
|
15554
|
+
search: (queryText, retrievalLimit, scope, trace) => searchCodebase(projectRoot, host, queryText, {
|
|
15412
15555
|
limit: retrievalLimit,
|
|
15413
15556
|
fileType: scope.fileType,
|
|
15414
15557
|
directory: scope.directory,
|
|
15415
|
-
metadataOnly: true
|
|
15558
|
+
metadataOnly: true,
|
|
15559
|
+
trace
|
|
15416
15560
|
})
|
|
15417
15561
|
});
|
|
15418
15562
|
}
|
|
@@ -15477,6 +15621,181 @@ async function resolveCodebaseContext(projectRoot, host, input) {
|
|
|
15477
15621
|
}
|
|
15478
15622
|
}
|
|
15479
15623
|
|
|
15624
|
+
// src/tools/edit-context.ts
|
|
15625
|
+
function edgeLimit(value) {
|
|
15626
|
+
if (value === null || value === void 0 || !Number.isFinite(value)) {
|
|
15627
|
+
return DEFAULT_CODEBASE_EDIT_CONTEXT_EDGE_LIMIT;
|
|
15628
|
+
}
|
|
15629
|
+
return Math.min(
|
|
15630
|
+
MAX_CODEBASE_EDIT_CONTEXT_EDGE_LIMIT,
|
|
15631
|
+
Math.max(MIN_CODEBASE_EDIT_CONTEXT_EDGE_LIMIT, Math.floor(value))
|
|
15632
|
+
);
|
|
15633
|
+
}
|
|
15634
|
+
function normalizedPath(value) {
|
|
15635
|
+
return value.replaceAll("\\", "/").replace(/^\.\//, "");
|
|
15636
|
+
}
|
|
15637
|
+
function pathsMatch(left, right) {
|
|
15638
|
+
const normalizedLeft = normalizedPath(left);
|
|
15639
|
+
const normalizedRight = normalizedPath(right);
|
|
15640
|
+
return normalizedLeft === normalizedRight || normalizedLeft.endsWith(`/${normalizedRight}`) || normalizedRight.endsWith(`/${normalizedLeft}`);
|
|
15641
|
+
}
|
|
15642
|
+
function targetSource(results, resolution) {
|
|
15643
|
+
return results.find((result) => pathsMatch(result.filePath, resolution.filePath) && result.startLine <= resolution.startLine && result.endLine >= resolution.startLine) ?? results.find((result) => pathsMatch(result.filePath, resolution.filePath) && result.name === resolution.name);
|
|
15644
|
+
}
|
|
15645
|
+
function formatSource(result) {
|
|
15646
|
+
const name = result.name ? ` ${result.name}` : "";
|
|
15647
|
+
return [
|
|
15648
|
+
"## Target implementation",
|
|
15649
|
+
`${result.filePath}:${result.startLine}-${result.endLine} (${result.chunkType}${name})`,
|
|
15650
|
+
"```",
|
|
15651
|
+
result.content,
|
|
15652
|
+
"```"
|
|
15653
|
+
].join("\n");
|
|
15654
|
+
}
|
|
15655
|
+
function formatCallers(edges) {
|
|
15656
|
+
if (edges.length === 0) return "## Direct callers\nNone found.";
|
|
15657
|
+
return [
|
|
15658
|
+
"## Direct callers",
|
|
15659
|
+
...edges.map((edge) => `- ${edge.fromSymbolName ?? "<unknown>"} at ${edge.fromSymbolFilePath ?? "<unknown file>"}:${edge.line} (${edge.callType}, ${edge.isResolved ? "resolved" : "unresolved"})`)
|
|
15660
|
+
].join("\n");
|
|
15661
|
+
}
|
|
15662
|
+
function formatCallees(edges, sourceFilePath) {
|
|
15663
|
+
if (edges.length === 0) return "## Direct callees\nNone found.";
|
|
15664
|
+
return [
|
|
15665
|
+
"## Direct callees",
|
|
15666
|
+
...edges.map((edge) => `- ${edge.targetName} from ${sourceFilePath}:${edge.line} (${edge.callType}, ${edge.isResolved ? "resolved" : "unresolved"})`)
|
|
15667
|
+
].join("\n");
|
|
15668
|
+
}
|
|
15669
|
+
function formatResolutionRisk(resolution) {
|
|
15670
|
+
if (resolution.status === "ambiguous") {
|
|
15671
|
+
const candidates = resolution.candidates.map((candidate) => `${candidate.filePath}:${candidate.startLine}`).join(", ");
|
|
15672
|
+
return `Risk: symbol "${resolution.name}" is ambiguous. Pass filePath to select a target.${candidates ? ` Candidates: ${candidates}.` : ""}`;
|
|
15673
|
+
}
|
|
15674
|
+
if (resolution.filePath && resolution.totalCandidates > 0) {
|
|
15675
|
+
return `Risk: symbol "${resolution.name}" did not resolve at filePath="${resolution.filePath}". Review the conceptual evidence before editing.`;
|
|
15676
|
+
}
|
|
15677
|
+
return `Risk: symbol "${resolution.name}" could not be resolved. Review the conceptual evidence before editing.`;
|
|
15678
|
+
}
|
|
15679
|
+
async function fallbackPack(dependencies, query, risk, resolution, tokenBudget, candidateSource = []) {
|
|
15680
|
+
const conceptual = await dependencies.searchCodebase(query, { limit: 5 });
|
|
15681
|
+
const pack = buildContextPack([...candidateSource, ...conceptual], {
|
|
15682
|
+
tokenBudget: tokenBudget ?? void 0,
|
|
15683
|
+
heading: "## Conceptual evidence",
|
|
15684
|
+
maxResults: 5,
|
|
15685
|
+
includeExactSearchHandoff: false,
|
|
15686
|
+
preferImplementationPaths: true
|
|
15687
|
+
});
|
|
15688
|
+
const fitted = fitTextToContextBudget(`${risk}
|
|
15689
|
+
|
|
15690
|
+
${pack.text}`, tokenBudget ?? void 0);
|
|
15691
|
+
return {
|
|
15692
|
+
text: fitted.text,
|
|
15693
|
+
details: {
|
|
15694
|
+
resolution,
|
|
15695
|
+
tokenBudget: fitted.tokenBudget,
|
|
15696
|
+
tokenEstimate: fitted.tokenEstimate,
|
|
15697
|
+
truncated: fitted.truncated,
|
|
15698
|
+
sourceIncluded: candidateSource.length > 0,
|
|
15699
|
+
callerCount: 0,
|
|
15700
|
+
calleeCount: 0
|
|
15701
|
+
}
|
|
15702
|
+
};
|
|
15703
|
+
}
|
|
15704
|
+
async function resolveCodebaseEditContextWithDependencies(input, dependencies) {
|
|
15705
|
+
const symbol = input.symbol?.trim();
|
|
15706
|
+
if (!symbol) {
|
|
15707
|
+
return fallbackPack(
|
|
15708
|
+
dependencies,
|
|
15709
|
+
input.query,
|
|
15710
|
+
"Risk: no authoritative symbol was supplied. Review the conceptual evidence before editing.",
|
|
15711
|
+
"not_requested",
|
|
15712
|
+
input.tokenBudget
|
|
15713
|
+
);
|
|
15714
|
+
}
|
|
15715
|
+
let callersResult;
|
|
15716
|
+
try {
|
|
15717
|
+
callersResult = await dependencies.getCallGraphData({
|
|
15718
|
+
name: symbol,
|
|
15719
|
+
filePath: input.filePath ?? void 0,
|
|
15720
|
+
direction: "callers"
|
|
15721
|
+
});
|
|
15722
|
+
} catch (error) {
|
|
15723
|
+
const candidates = await dependencies.implementationLookup(symbol, { limit: 5 });
|
|
15724
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
15725
|
+
return fallbackPack(
|
|
15726
|
+
dependencies,
|
|
15727
|
+
input.query,
|
|
15728
|
+
`Risk: graph data is unavailable (${message}). Target and dependencies are not graph-verified.`,
|
|
15729
|
+
"graph_unavailable",
|
|
15730
|
+
input.tokenBudget,
|
|
15731
|
+
candidates
|
|
15732
|
+
);
|
|
15733
|
+
}
|
|
15734
|
+
if (callersResult.resolution.status !== "resolved") {
|
|
15735
|
+
return fallbackPack(
|
|
15736
|
+
dependencies,
|
|
15737
|
+
input.query,
|
|
15738
|
+
formatResolutionRisk(callersResult.resolution),
|
|
15739
|
+
callersResult.resolution.status,
|
|
15740
|
+
input.tokenBudget
|
|
15741
|
+
);
|
|
15742
|
+
}
|
|
15743
|
+
const resolution = callersResult.resolution;
|
|
15744
|
+
const [definitionsResult, calleesResult] = await Promise.allSettled([
|
|
15745
|
+
dependencies.implementationLookup(symbol, { limit: 10 }),
|
|
15746
|
+
dependencies.getCallGraphData({
|
|
15747
|
+
name: symbol,
|
|
15748
|
+
filePath: input.filePath ?? resolution.filePath,
|
|
15749
|
+
direction: "callees"
|
|
15750
|
+
})
|
|
15751
|
+
]);
|
|
15752
|
+
if (definitionsResult.status === "rejected") throw definitionsResult.reason;
|
|
15753
|
+
let graphRisk;
|
|
15754
|
+
let callees = [];
|
|
15755
|
+
if (calleesResult.status === "rejected") {
|
|
15756
|
+
const message = calleesResult.reason instanceof Error ? calleesResult.reason.message : String(calleesResult.reason);
|
|
15757
|
+
graphRisk = `Risk: callee graph data is unavailable (${message}). Dependency evidence is incomplete.`;
|
|
15758
|
+
} else if (calleesResult.value.resolution.status !== "resolved") {
|
|
15759
|
+
graphRisk = "Risk: the target resolved for callers but not callees. Dependency evidence is incomplete.";
|
|
15760
|
+
} else {
|
|
15761
|
+
callees = calleesResult.value.callees.slice(0, edgeLimit(input.calleeLimit));
|
|
15762
|
+
}
|
|
15763
|
+
const source = targetSource(definitionsResult.value, resolution);
|
|
15764
|
+
const callers = callersResult.callers.slice(0, edgeLimit(input.callerLimit));
|
|
15765
|
+
const sourceBudget = Math.max(
|
|
15766
|
+
MIN_CONTEXT_PACK_TOKEN_BUDGET,
|
|
15767
|
+
Math.floor((input.tokenBudget ?? DEFAULT_CONTEXT_PACK_TOKEN_BUDGET) * 0.6)
|
|
15768
|
+
);
|
|
15769
|
+
const sourceText = source ? fitTextToContextBudget(formatSource(source), sourceBudget).text : `## Target implementation
|
|
15770
|
+
Risk: no implementation source matched the resolved target at ${resolution.filePath}:${resolution.startLine}.`;
|
|
15771
|
+
const fitted = fitTextToContextBudget([
|
|
15772
|
+
`# Pre-edit context for ${resolution.name}`,
|
|
15773
|
+
graphRisk,
|
|
15774
|
+
sourceText,
|
|
15775
|
+
formatCallers(callers),
|
|
15776
|
+
formatCallees(callees, resolution.filePath)
|
|
15777
|
+
].filter((section) => section !== void 0).join("\n\n"), input.tokenBudget ?? void 0);
|
|
15778
|
+
return {
|
|
15779
|
+
text: fitted.text,
|
|
15780
|
+
details: {
|
|
15781
|
+
resolution: "resolved",
|
|
15782
|
+
tokenBudget: fitted.tokenBudget,
|
|
15783
|
+
tokenEstimate: fitted.tokenEstimate,
|
|
15784
|
+
truncated: fitted.truncated,
|
|
15785
|
+
sourceIncluded: source !== void 0,
|
|
15786
|
+
callerCount: callers.length,
|
|
15787
|
+
calleeCount: callees.length
|
|
15788
|
+
}
|
|
15789
|
+
};
|
|
15790
|
+
}
|
|
15791
|
+
async function resolveCodebaseEditContext(projectRoot, host, input) {
|
|
15792
|
+
return resolveCodebaseEditContextWithDependencies(input, {
|
|
15793
|
+
searchCodebase: (query, options) => searchCodebase(projectRoot, host, query, options),
|
|
15794
|
+
implementationLookup: (query, options) => implementationLookup(projectRoot, host, query, options),
|
|
15795
|
+
getCallGraphData: (params) => getCallGraphData(projectRoot, host, params)
|
|
15796
|
+
});
|
|
15797
|
+
}
|
|
15798
|
+
|
|
15480
15799
|
// src/eval/budget.ts
|
|
15481
15800
|
function evaluateBudgetGate(budget, summary, comparison) {
|
|
15482
15801
|
const BASELINE_P95_EPSILON_MS = 1e-3;
|
|
@@ -15500,6 +15819,24 @@ function evaluateBudgetGate(budget, summary, comparison) {
|
|
|
15500
15819
|
message: `Raw Distinct Top@3 ${summary.metrics.rawDistinctTop3Ratio.toFixed(4)} is below minimum ${thresholds.minRawDistinctTop3Ratio.toFixed(4)}`
|
|
15501
15820
|
});
|
|
15502
15821
|
}
|
|
15822
|
+
if (thresholds.minGraphNeighborRecall !== void 0 && summary.metrics.graphNeighborRecall !== void 0 && summary.metrics.graphNeighborRecall < thresholds.minGraphNeighborRecall) {
|
|
15823
|
+
violations.push({
|
|
15824
|
+
metric: "minGraphNeighborRecall",
|
|
15825
|
+
message: `Graph-neighbor recall ${summary.metrics.graphNeighborRecall.toFixed(4)} is below minimum ${thresholds.minGraphNeighborRecall.toFixed(4)}`
|
|
15826
|
+
});
|
|
15827
|
+
}
|
|
15828
|
+
if (thresholds.minRouteAccuracy !== void 0 && summary.metrics.routeAccuracy < thresholds.minRouteAccuracy) {
|
|
15829
|
+
violations.push({
|
|
15830
|
+
metric: "minRouteAccuracy",
|
|
15831
|
+
message: `Route accuracy ${summary.metrics.routeAccuracy.toFixed(4)} is below minimum ${thresholds.minRouteAccuracy.toFixed(4)}`
|
|
15832
|
+
});
|
|
15833
|
+
}
|
|
15834
|
+
if (thresholds.minOutcomeAccuracy !== void 0 && summary.metrics.outcomeAccuracy < thresholds.minOutcomeAccuracy) {
|
|
15835
|
+
violations.push({
|
|
15836
|
+
metric: "minOutcomeAccuracy",
|
|
15837
|
+
message: `Outcome accuracy ${summary.metrics.outcomeAccuracy.toFixed(4)} is below minimum ${thresholds.minOutcomeAccuracy.toFixed(4)}`
|
|
15838
|
+
});
|
|
15839
|
+
}
|
|
15503
15840
|
if (comparison) {
|
|
15504
15841
|
if (thresholds.hitAt5MaxDrop !== void 0 && comparison.deltas.hitAt5.absolute < -thresholds.hitAt5MaxDrop) {
|
|
15505
15842
|
violations.push({
|
|
@@ -15677,6 +16014,14 @@ function isSymbolIntended(query) {
|
|
|
15677
16014
|
function isExpectedFile(filePath, relevant) {
|
|
15678
16015
|
return relevant.some((entry) => pathMatchesExpected(filePath, entry.path));
|
|
15679
16016
|
}
|
|
16017
|
+
function graphNeighborMatches(query, result) {
|
|
16018
|
+
const expected = query.expected.graphNeighbor;
|
|
16019
|
+
if (!expected || result.graphDirection !== expected.direction) return false;
|
|
16020
|
+
if (expected.filePath !== void 0 && !pathMatchesExpected(result.filePath, expected.filePath)) {
|
|
16021
|
+
return false;
|
|
16022
|
+
}
|
|
16023
|
+
return expected.symbol === void 0 || result.name === expected.symbol;
|
|
16024
|
+
}
|
|
15680
16025
|
function resultRelevance(filePath, symbol, relevant, isSymbolIntendedQuery) {
|
|
15681
16026
|
let relevance = 0;
|
|
15682
16027
|
for (const entry of relevant) {
|
|
@@ -15824,6 +16169,7 @@ function buildPerQueryResult(query, results, latencyMs, k, route = {
|
|
|
15824
16169
|
routeMatched: query.expected.expectedRoute ? query.expected.expectedRoute === route.resolvedRoute : void 0,
|
|
15825
16170
|
outcomeMatched: query.expected.expectedOutcome === void 0 ? void 0 : query.expected.expectedOutcome === "results" ? deduped.length > 0 : deduped.length === 0,
|
|
15826
16171
|
recoveryMatched: query.expected.recoveryExpectation === void 0 ? void 0 : query.expected.recoveryExpectation === "filter-relaxed" ? context?.recoveryRelaxed === true : context?.recoveryUsed !== true,
|
|
16172
|
+
graphNeighborMatched: query.expected.graphNeighbor === void 0 ? void 0 : results.some((result) => graphNeighborMatches(query, result)),
|
|
15827
16173
|
language: query.language,
|
|
15828
16174
|
difficulty: query.difficulty,
|
|
15829
16175
|
tags: query.tags,
|
|
@@ -15873,7 +16219,7 @@ function computeEvalMetrics(queries, perQuery, embeddingCallCount, embeddingToke
|
|
|
15873
16219
|
"no-relevant-hit-top-k": 0
|
|
15874
16220
|
};
|
|
15875
16221
|
const latencies = perQuery.map((item) => item.latencyMs);
|
|
15876
|
-
const contextQueries = perQuery.filter((item) => item.retrievalMode
|
|
16222
|
+
const contextQueries = perQuery.filter((item) => item.retrievalMode !== "search");
|
|
15877
16223
|
const contextResponseTokens = contextQueries.map((item) => item.responseTokens);
|
|
15878
16224
|
const totalContextResponseTokens = contextResponseTokens.reduce((sum2, value) => sum2 + value, 0);
|
|
15879
16225
|
const contextTokenUnits = totalContextResponseTokens / 1e3;
|
|
@@ -15883,6 +16229,8 @@ function computeEvalMetrics(queries, perQuery, embeddingCallCount, embeddingToke
|
|
|
15883
16229
|
let outcomeExpectedCount = 0;
|
|
15884
16230
|
let recoveryMatchedCount = 0;
|
|
15885
16231
|
let recoveryExpectedCount = 0;
|
|
16232
|
+
let graphNeighborMatchedCount = 0;
|
|
16233
|
+
let graphNeighborExpectedCount = 0;
|
|
15886
16234
|
for (const query of perQuery) {
|
|
15887
16235
|
if (positiveQueryIds.has(query.id)) {
|
|
15888
16236
|
if (query.hitAt1) sum.hitAt1 += 1;
|
|
@@ -15911,6 +16259,10 @@ function computeEvalMetrics(queries, perQuery, embeddingCallCount, embeddingToke
|
|
|
15911
16259
|
recoveryExpectedCount += 1;
|
|
15912
16260
|
if (query.recoveryMatched) recoveryMatchedCount += 1;
|
|
15913
16261
|
}
|
|
16262
|
+
if (query.graphNeighborMatched !== void 0) {
|
|
16263
|
+
graphNeighborExpectedCount += 1;
|
|
16264
|
+
if (query.graphNeighborMatched) graphNeighborMatchedCount += 1;
|
|
16265
|
+
}
|
|
15914
16266
|
}
|
|
15915
16267
|
const queryTokens = queries.reduce((acc, q) => acc + estimateTokens(q.query), 0);
|
|
15916
16268
|
return {
|
|
@@ -15923,6 +16275,7 @@ function computeEvalMetrics(queries, perQuery, embeddingCallCount, embeddingToke
|
|
|
15923
16275
|
routeAccuracy: routeExpectedCount === 0 ? 0 : routeMatchedCount / routeExpectedCount,
|
|
15924
16276
|
outcomeAccuracy: outcomeExpectedCount === 0 ? 0 : outcomeMatchedCount / outcomeExpectedCount,
|
|
15925
16277
|
recoveryAccuracy: recoveryExpectedCount === 0 ? 0 : recoveryMatchedCount / recoveryExpectedCount,
|
|
16278
|
+
graphNeighborRecall: graphNeighborExpectedCount === 0 ? 0 : graphNeighborMatchedCount / graphNeighborExpectedCount,
|
|
15926
16279
|
distinctTop3Ratio: safeDiv(sum.distinctTop3Ratio),
|
|
15927
16280
|
rawDistinctTop3Ratio: safeDiv(sum.rawDistinctTop3Ratio),
|
|
15928
16281
|
latencyMs: {
|
|
@@ -16188,14 +16541,29 @@ function parseQueryArgs(value, path31) {
|
|
|
16188
16541
|
throw new Error(`${path31} must be an object`);
|
|
16189
16542
|
}
|
|
16190
16543
|
const symbol = parseStringOrUndefined(value.symbol, `${path31}.symbol`);
|
|
16544
|
+
const filePath = parseStringOrUndefined(value.filePath, `${path31}.filePath`);
|
|
16191
16545
|
const fileType = parseStringOrUndefined(value.fileType, `${path31}.fileType`);
|
|
16192
16546
|
const directory = parseStringOrUndefined(value.directory, `${path31}.directory`);
|
|
16547
|
+
const callerLimit = parsePositiveIntegerOrUndefined(value.callerLimit, `${path31}.callerLimit`);
|
|
16548
|
+
const calleeLimit = parsePositiveIntegerOrUndefined(value.calleeLimit, `${path31}.calleeLimit`);
|
|
16549
|
+
const tokenBudget = parsePositiveIntegerOrUndefined(value.tokenBudget, `${path31}.tokenBudget`);
|
|
16193
16550
|
return {
|
|
16194
16551
|
...symbol !== void 0 ? { symbol } : {},
|
|
16552
|
+
...filePath !== void 0 ? { filePath } : {},
|
|
16195
16553
|
...fileType !== void 0 ? { fileType } : {},
|
|
16196
|
-
...directory !== void 0 ? { directory } : {}
|
|
16554
|
+
...directory !== void 0 ? { directory } : {},
|
|
16555
|
+
...callerLimit !== void 0 ? { callerLimit } : {},
|
|
16556
|
+
...calleeLimit !== void 0 ? { calleeLimit } : {},
|
|
16557
|
+
...tokenBudget !== void 0 ? { tokenBudget } : {}
|
|
16197
16558
|
};
|
|
16198
16559
|
}
|
|
16560
|
+
function parsePositiveIntegerOrUndefined(value, path31) {
|
|
16561
|
+
if (value === void 0 || value === null) return void 0;
|
|
16562
|
+
if (typeof value !== "number" || !Number.isInteger(value) || value <= 0) {
|
|
16563
|
+
throw new Error(`${path31} must be a positive integer`);
|
|
16564
|
+
}
|
|
16565
|
+
return value;
|
|
16566
|
+
}
|
|
16199
16567
|
var SEMVER_VERSION_PATTERN = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/;
|
|
16200
16568
|
function parseSemanticVersion(value, path31) {
|
|
16201
16569
|
if (!isNonEmptyString(value)) {
|
|
@@ -16208,8 +16576,8 @@ function parseSemanticVersion(value, path31) {
|
|
|
16208
16576
|
}
|
|
16209
16577
|
function parseRetrievalMode(value, path31) {
|
|
16210
16578
|
if (value === void 0 || value === "search") return "search";
|
|
16211
|
-
if (value === "context") return value;
|
|
16212
|
-
throw new Error(`${path31} must be one of: search, context`);
|
|
16579
|
+
if (value === "context" || value === "edit-context") return value;
|
|
16580
|
+
throw new Error(`${path31} must be one of: search, context, edit-context`);
|
|
16213
16581
|
}
|
|
16214
16582
|
function parseStringOrUndefined(value, path31) {
|
|
16215
16583
|
if (value === void 0 || value === null) return void 0;
|
|
@@ -16249,6 +16617,25 @@ function parseEvidenceRelevance(value, path31) {
|
|
|
16249
16617
|
}
|
|
16250
16618
|
return value;
|
|
16251
16619
|
}
|
|
16620
|
+
function parseExpectedGraphNeighbor(value, path31) {
|
|
16621
|
+
if (value === void 0) return void 0;
|
|
16622
|
+
if (!isRecord3(value)) {
|
|
16623
|
+
throw new Error(`${path31} must be an object`);
|
|
16624
|
+
}
|
|
16625
|
+
if (value.direction !== "caller" && value.direction !== "callee") {
|
|
16626
|
+
throw new Error(`${path31}.direction must be one of: caller, callee`);
|
|
16627
|
+
}
|
|
16628
|
+
const filePath = parseStringOrUndefined(value.filePath, `${path31}.filePath`);
|
|
16629
|
+
const symbol = parseStringOrUndefined(value.symbol, `${path31}.symbol`);
|
|
16630
|
+
if (filePath === void 0 && symbol === void 0) {
|
|
16631
|
+
throw new Error(`${path31} must include filePath or symbol`);
|
|
16632
|
+
}
|
|
16633
|
+
return {
|
|
16634
|
+
direction: value.direction,
|
|
16635
|
+
...filePath !== void 0 ? { filePath } : {},
|
|
16636
|
+
...symbol !== void 0 ? { symbol } : {}
|
|
16637
|
+
};
|
|
16638
|
+
}
|
|
16252
16639
|
function parseExpected(input, path31) {
|
|
16253
16640
|
if (!isRecord3(input)) {
|
|
16254
16641
|
throw new Error(`${path31} must be an object`);
|
|
@@ -16261,9 +16648,11 @@ function parseExpected(input, path31) {
|
|
|
16261
16648
|
const expectedOutcomeRaw = input.expectedOutcome;
|
|
16262
16649
|
const recoveryExpectationRaw = input.recoveryExpectation;
|
|
16263
16650
|
const gradedEvidenceRaw = input.gradedEvidence;
|
|
16651
|
+
const graphNeighborRaw = input.graphNeighbor;
|
|
16264
16652
|
const filePath = parseStringOrUndefined(filePathRaw, `${path31}.filePath`);
|
|
16265
16653
|
const acceptableFiles = isStringArray4(acceptableFilesRaw) ? acceptableFilesRaw : void 0;
|
|
16266
16654
|
const gradedEvidence = parseGradedEvidence(gradedEvidenceRaw, `${path31}.gradedEvidence`);
|
|
16655
|
+
const graphNeighbor = parseExpectedGraphNeighbor(graphNeighborRaw, `${path31}.graphNeighbor`);
|
|
16267
16656
|
const expectedOutcome = parseExpectedOutcome(expectedOutcomeRaw, `${path31}.expectedOutcome`);
|
|
16268
16657
|
if (expectedOutcome !== "no-results" && !filePath && (!acceptableFiles || acceptableFiles.length === 0) && gradedEvidence.length === 0) {
|
|
16269
16658
|
throw new Error(
|
|
@@ -16292,7 +16681,8 @@ function parseExpected(input, path31) {
|
|
|
16292
16681
|
expectedRoute,
|
|
16293
16682
|
expectedOutcome,
|
|
16294
16683
|
recoveryExpectation,
|
|
16295
|
-
...gradedEvidence.length > 0 ? { gradedEvidence } : {}
|
|
16684
|
+
...gradedEvidence.length > 0 ? { gradedEvidence } : {},
|
|
16685
|
+
...graphNeighbor !== void 0 ? { graphNeighbor } : {}
|
|
16296
16686
|
};
|
|
16297
16687
|
}
|
|
16298
16688
|
function parseQueryLanguage(value, path31) {
|
|
@@ -16427,6 +16817,21 @@ function parseBudget(raw, sourceLabel) {
|
|
|
16427
16817
|
"minRawDistinctTop3Ratio",
|
|
16428
16818
|
sourceLabel
|
|
16429
16819
|
),
|
|
16820
|
+
minGraphNeighborRecall: parseThresholdValue(
|
|
16821
|
+
thresholds.minGraphNeighborRecall,
|
|
16822
|
+
"minGraphNeighborRecall",
|
|
16823
|
+
sourceLabel
|
|
16824
|
+
),
|
|
16825
|
+
minRouteAccuracy: parseThresholdValue(
|
|
16826
|
+
thresholds.minRouteAccuracy,
|
|
16827
|
+
"minRouteAccuracy",
|
|
16828
|
+
sourceLabel
|
|
16829
|
+
),
|
|
16830
|
+
minOutcomeAccuracy: parseThresholdValue(
|
|
16831
|
+
thresholds.minOutcomeAccuracy,
|
|
16832
|
+
"minOutcomeAccuracy",
|
|
16833
|
+
sourceLabel
|
|
16834
|
+
),
|
|
16430
16835
|
maxContextResponseTokensAverage: parseThresholdValue(
|
|
16431
16836
|
thresholds.maxContextResponseTokensAverage,
|
|
16432
16837
|
"maxContextResponseTokensAverage",
|
|
@@ -16491,6 +16896,120 @@ function buildDatasetFingerprint(dataset) {
|
|
|
16491
16896
|
const canonical = JSON.stringify(normalizeForFingerprint(dataset));
|
|
16492
16897
|
return crypto2.createHash("sha256").update(canonical).digest("hex");
|
|
16493
16898
|
}
|
|
16899
|
+
function normalizedPath2(value) {
|
|
16900
|
+
return value.replaceAll("\\", "/").replace(/^\.\//, "");
|
|
16901
|
+
}
|
|
16902
|
+
function pathsMatch2(left, right) {
|
|
16903
|
+
const normalizedLeft = normalizedPath2(left);
|
|
16904
|
+
const normalizedRight = normalizedPath2(right);
|
|
16905
|
+
return normalizedLeft === normalizedRight || normalizedLeft.endsWith(`/${normalizedRight}`) || normalizedRight.endsWith(`/${normalizedLeft}`);
|
|
16906
|
+
}
|
|
16907
|
+
function toEvalSearchResult(result) {
|
|
16908
|
+
return {
|
|
16909
|
+
filePath: result.filePath,
|
|
16910
|
+
startLine: result.startLine,
|
|
16911
|
+
endLine: result.endLine,
|
|
16912
|
+
score: result.score,
|
|
16913
|
+
chunkType: result.chunkType,
|
|
16914
|
+
name: result.name
|
|
16915
|
+
};
|
|
16916
|
+
}
|
|
16917
|
+
function selectResolvedTarget(definitions, resolution) {
|
|
16918
|
+
if (resolution?.status !== "resolved") return definitions[0];
|
|
16919
|
+
return definitions.find((result) => pathsMatch2(result.filePath, resolution.filePath) && result.startLine <= resolution.startLine && result.endLine >= resolution.startLine) ?? definitions.find((result) => pathsMatch2(result.filePath, resolution.filePath) && result.name === resolution.name) ?? definitions[0];
|
|
16920
|
+
}
|
|
16921
|
+
function callerResult(edge) {
|
|
16922
|
+
if (!edge.fromSymbolFilePath) return void 0;
|
|
16923
|
+
return {
|
|
16924
|
+
filePath: edge.fromSymbolFilePath,
|
|
16925
|
+
startLine: edge.line,
|
|
16926
|
+
endLine: edge.line,
|
|
16927
|
+
score: 0,
|
|
16928
|
+
chunkType: "graph-caller",
|
|
16929
|
+
name: edge.fromSymbolName,
|
|
16930
|
+
graphDirection: "caller"
|
|
16931
|
+
};
|
|
16932
|
+
}
|
|
16933
|
+
function calleeResult(edge, symbols) {
|
|
16934
|
+
const symbol = edge.toSymbolId ? symbols.find((candidate) => candidate.id === edge.toSymbolId) : symbols.filter((candidate) => candidate.name === edge.targetName).length === 1 ? symbols.find((candidate) => candidate.name === edge.targetName) : void 0;
|
|
16935
|
+
if (!symbol) return void 0;
|
|
16936
|
+
return {
|
|
16937
|
+
filePath: symbol.filePath,
|
|
16938
|
+
startLine: symbol.startLine,
|
|
16939
|
+
endLine: symbol.endLine,
|
|
16940
|
+
score: 0,
|
|
16941
|
+
chunkType: "graph-callee",
|
|
16942
|
+
name: symbol.name,
|
|
16943
|
+
graphDirection: "callee"
|
|
16944
|
+
};
|
|
16945
|
+
}
|
|
16946
|
+
async function runEditContextQuery(indexer, projectRoot, query) {
|
|
16947
|
+
let definitions = [];
|
|
16948
|
+
let conceptual = [];
|
|
16949
|
+
let callers;
|
|
16950
|
+
let callees;
|
|
16951
|
+
const editContext = await resolveCodebaseEditContextWithDependencies({
|
|
16952
|
+
query: query.query,
|
|
16953
|
+
symbol: query.args?.symbol,
|
|
16954
|
+
filePath: query.args?.filePath ?? query.expected.filePath,
|
|
16955
|
+
callerLimit: query.args?.callerLimit,
|
|
16956
|
+
calleeLimit: query.args?.calleeLimit,
|
|
16957
|
+
tokenBudget: query.args?.tokenBudget
|
|
16958
|
+
}, {
|
|
16959
|
+
searchCodebase: async (searchQuery, options) => {
|
|
16960
|
+
conceptual = await indexer.search(searchQuery, options?.limit, {
|
|
16961
|
+
filterByBranch: !!query.expected.branch
|
|
16962
|
+
});
|
|
16963
|
+
return conceptual;
|
|
16964
|
+
},
|
|
16965
|
+
implementationLookup: async (symbol, options) => {
|
|
16966
|
+
definitions = await indexer.search(symbol, options?.limit, {
|
|
16967
|
+
filterByBranch: !!query.expected.branch,
|
|
16968
|
+
definitionIntent: true
|
|
16969
|
+
});
|
|
16970
|
+
return definitions;
|
|
16971
|
+
},
|
|
16972
|
+
getCallGraphData: async (params) => {
|
|
16973
|
+
const result = await getCallGraphDataForIndexer(indexer, projectRoot, params);
|
|
16974
|
+
if (params.direction === "callers") callers = result;
|
|
16975
|
+
else callees = result;
|
|
16976
|
+
return result;
|
|
16977
|
+
}
|
|
16978
|
+
});
|
|
16979
|
+
const resolution = callers?.resolution;
|
|
16980
|
+
const target = selectResolvedTarget(definitions, resolution);
|
|
16981
|
+
const targetCandidates = target ? [target] : [...definitions, ...conceptual];
|
|
16982
|
+
const results = targetCandidates.filter((candidate) => (resolution?.status !== "resolved" || editContext.details.sourceIncluded) && editContext.text.includes(
|
|
16983
|
+
`${candidate.filePath}:${candidate.startLine}-${candidate.endLine}`
|
|
16984
|
+
)).map(toEvalSearchResult);
|
|
16985
|
+
if (query.expected.graphNeighbor) {
|
|
16986
|
+
const symbols = await indexer.getCallGraphSymbols();
|
|
16987
|
+
const callerLimit = query.args?.callerLimit ?? DEFAULT_CODEBASE_EDIT_CONTEXT_EDGE_LIMIT;
|
|
16988
|
+
const calleeLimit = query.args?.calleeLimit ?? DEFAULT_CODEBASE_EDIT_CONTEXT_EDGE_LIMIT;
|
|
16989
|
+
const publishedCallers = (callers?.callers ?? []).slice(0, callerLimit).filter((edge) => editContext.text.includes(
|
|
16990
|
+
`${edge.fromSymbolName ?? "<unknown>"} at ${edge.fromSymbolFilePath ?? "<unknown file>"}:${edge.line} (${edge.callType}, ${edge.isResolved ? "resolved" : "unresolved"})`
|
|
16991
|
+
));
|
|
16992
|
+
const publishedCallees = (callees?.callees ?? []).slice(0, calleeLimit).filter((edge) => resolution?.status === "resolved" && editContext.text.includes(
|
|
16993
|
+
`${edge.targetName} from ${resolution.filePath}:${edge.line} (${edge.callType}, ${edge.isResolved ? "resolved" : "unresolved"})`
|
|
16994
|
+
));
|
|
16995
|
+
results.push(
|
|
16996
|
+
...publishedCallers.map(callerResult).filter((item) => item !== void 0),
|
|
16997
|
+
...publishedCallees.map((edge) => calleeResult(edge, symbols)).filter((item) => item !== void 0)
|
|
16998
|
+
);
|
|
16999
|
+
}
|
|
17000
|
+
return {
|
|
17001
|
+
results,
|
|
17002
|
+
resolvedRoute: resolution?.status === "resolved" ? "definition" : "search",
|
|
17003
|
+
routedQuery: query.args?.symbol ?? query.query,
|
|
17004
|
+
context: {
|
|
17005
|
+
tokenBudget: editContext.details.tokenBudget,
|
|
17006
|
+
responseTokens: editContext.details.tokenEstimate,
|
|
17007
|
+
candidateCount: results.length,
|
|
17008
|
+
deduplicatedCount: results.length,
|
|
17009
|
+
omittedCount: 0
|
|
17010
|
+
}
|
|
17011
|
+
};
|
|
17012
|
+
}
|
|
16494
17013
|
async function runEvaluation(options) {
|
|
16495
17014
|
const datasetPath = toAbsolute(options.projectRoot, options.datasetPath);
|
|
16496
17015
|
const againstPath = options.againstPath ? toAbsolute(options.projectRoot, options.againstPath) : void 0;
|
|
@@ -16522,6 +17041,7 @@ async function runEvaluation(options) {
|
|
|
16522
17041
|
);
|
|
16523
17042
|
}
|
|
16524
17043
|
const start = import_perf_hooks2.performance.now();
|
|
17044
|
+
const editContextResult = query.retrievalMode === "edit-context" ? await runEditContextQuery(indexer, options.projectRoot, query) : void 0;
|
|
16525
17045
|
const contextResult = query.retrievalMode === "context" ? await resolveSearchContext({
|
|
16526
17046
|
query: query.query,
|
|
16527
17047
|
symbol: query.args?.symbol,
|
|
@@ -16545,31 +17065,32 @@ async function runEvaluation(options) {
|
|
|
16545
17065
|
directory: scope.directory
|
|
16546
17066
|
})
|
|
16547
17067
|
}) : void 0;
|
|
16548
|
-
const result = contextResult?.details?.results ?? await indexer.search(query.query, 10, {
|
|
17068
|
+
const result = editContextResult?.results ?? contextResult?.details?.results ?? await indexer.search(query.query, 10, {
|
|
16549
17069
|
metadataOnly: true,
|
|
16550
17070
|
filterByBranch: !!query.expected.branch,
|
|
16551
17071
|
fileType: query.args?.fileType,
|
|
16552
17072
|
directory: query.args?.directory
|
|
16553
17073
|
});
|
|
16554
17074
|
const elapsed = import_perf_hooks2.performance.now() - start;
|
|
16555
|
-
const resolvedRoute = contextResult?.details?.route === "definition" ? "definition" : "search";
|
|
16556
|
-
const routedQuery = contextResult?.details?.routedQuery ?? query.query;
|
|
17075
|
+
const resolvedRoute = editContextResult?.resolvedRoute ?? (contextResult?.details?.route === "definition" ? "definition" : "search");
|
|
17076
|
+
const routedQuery = editContextResult?.routedQuery ?? contextResult?.details?.routedQuery ?? query.query;
|
|
16557
17077
|
const successfulRecoveryAttempt = contextResult?.details?.recovery?.successfulAttemptIndex;
|
|
16558
17078
|
const recoveryAttempts = contextResult?.details?.recovery?.attempts ?? [];
|
|
16559
17079
|
const recoveryRelaxed = successfulRecoveryAttempt === void 0 ? false : (recoveryAttempts[successfulRecoveryAttempt]?.relaxedFields.length ?? 0) > 0;
|
|
16560
17080
|
const recoveryUsed = recoveryAttempts.length > 1 || recoveryAttempts.some((attempt) => attempt.relaxedFields.length > 0);
|
|
16561
|
-
const materialized = result.map((item) =>
|
|
16562
|
-
|
|
16563
|
-
|
|
16564
|
-
|
|
16565
|
-
|
|
16566
|
-
|
|
16567
|
-
|
|
16568
|
-
|
|
16569
|
-
|
|
16570
|
-
|
|
16571
|
-
|
|
16572
|
-
}
|
|
17081
|
+
const materialized = result.map((item) => {
|
|
17082
|
+
const graphDirection = "graphDirection" in item && (item.graphDirection === "caller" || item.graphDirection === "callee") ? item.graphDirection : void 0;
|
|
17083
|
+
return {
|
|
17084
|
+
filePath: item.filePath,
|
|
17085
|
+
startLine: item.startLine,
|
|
17086
|
+
endLine: item.endLine,
|
|
17087
|
+
score: item.score,
|
|
17088
|
+
chunkType: item.chunkType,
|
|
17089
|
+
name: item.name,
|
|
17090
|
+
graphDirection
|
|
17091
|
+
};
|
|
17092
|
+
});
|
|
17093
|
+
const contextMeasurement = editContextResult?.context ?? (contextResult?.details ? {
|
|
16573
17094
|
tokenBudget: contextResult.details.tokenBudget,
|
|
16574
17095
|
responseTokens: contextResult.details.tokenEstimate,
|
|
16575
17096
|
candidateCount: contextResult.details.candidateCount ?? 0,
|
|
@@ -16577,7 +17098,11 @@ async function runEvaluation(options) {
|
|
|
16577
17098
|
omittedCount: contextResult.details.omittedCount ?? 0,
|
|
16578
17099
|
recoveryUsed,
|
|
16579
17100
|
recoveryRelaxed
|
|
16580
|
-
} : void 0)
|
|
17101
|
+
} : void 0);
|
|
17102
|
+
perQuery.push(buildPerQueryResult(query, materialized, elapsed, 10, {
|
|
17103
|
+
resolvedRoute,
|
|
17104
|
+
routedQuery
|
|
17105
|
+
}, contextMeasurement));
|
|
16581
17106
|
}
|
|
16582
17107
|
const logger = indexer.getLogger();
|
|
16583
17108
|
const metricSnapshot = logger.getMetrics();
|
|
@@ -17114,7 +17639,11 @@ var import_zod2 = require("zod");
|
|
|
17114
17639
|
|
|
17115
17640
|
// src/tools/execute-common.ts
|
|
17116
17641
|
async function executeCodebaseContext(projectRoot, host, args) {
|
|
17117
|
-
|
|
17642
|
+
const result = await resolveCodebaseContext(projectRoot, host, args);
|
|
17643
|
+
return { text: result.text, details: result.details };
|
|
17644
|
+
}
|
|
17645
|
+
async function executeCodebaseEditContext(projectRoot, host, args) {
|
|
17646
|
+
return { text: (await resolveCodebaseEditContext(projectRoot, host, args)).text };
|
|
17118
17647
|
}
|
|
17119
17648
|
async function executeIndexCodebase(projectRoot, host, args, onProgress) {
|
|
17120
17649
|
const result = await runIndexCodebase(projectRoot, host, args, onProgress);
|
|
@@ -17230,6 +17759,7 @@ function formatPrImpact(result) {
|
|
|
17230
17759
|
// src/tools/tool-names.ts
|
|
17231
17760
|
var TOOL_NAME = {
|
|
17232
17761
|
CODEBASE_CONTEXT: "codebase_context",
|
|
17762
|
+
CODEBASE_EDIT_CONTEXT: "codebase_edit_context",
|
|
17233
17763
|
CODEBASE_SEARCH: "codebase_search",
|
|
17234
17764
|
CODEBASE_PEEK: "codebase_peek",
|
|
17235
17765
|
FIND_SIMILAR: "find_similar",
|
|
@@ -17253,6 +17783,7 @@ var TOOL_NAME = {
|
|
|
17253
17783
|
};
|
|
17254
17784
|
var PORTABLE_TOOL_NAMES = [
|
|
17255
17785
|
TOOL_NAME.CODEBASE_CONTEXT,
|
|
17786
|
+
TOOL_NAME.CODEBASE_EDIT_CONTEXT,
|
|
17256
17787
|
TOOL_NAME.CODEBASE_SEARCH,
|
|
17257
17788
|
TOOL_NAME.CODEBASE_PEEK,
|
|
17258
17789
|
TOOL_NAME.INDEX_CODEBASE,
|
|
@@ -17269,6 +17800,7 @@ var PORTABLE_TOOL_NAMES = [
|
|
|
17269
17800
|
];
|
|
17270
17801
|
var OPENCODE_TOOL_NAMES = [
|
|
17271
17802
|
TOOL_NAME.CODEBASE_CONTEXT,
|
|
17803
|
+
TOOL_NAME.CODEBASE_EDIT_CONTEXT,
|
|
17272
17804
|
TOOL_NAME.CODEBASE_SEARCH,
|
|
17273
17805
|
TOOL_NAME.CODEBASE_PEEK,
|
|
17274
17806
|
TOOL_NAME.INDEX_CODEBASE,
|
|
@@ -17289,6 +17821,7 @@ var OPENCODE_TOOL_NAMES = [
|
|
|
17289
17821
|
];
|
|
17290
17822
|
var PI_TOOL_NAMES = [
|
|
17291
17823
|
TOOL_NAME.CODEBASE_CONTEXT,
|
|
17824
|
+
TOOL_NAME.CODEBASE_EDIT_CONTEXT,
|
|
17292
17825
|
TOOL_NAME.CODEBASE_SEARCH,
|
|
17293
17826
|
TOOL_NAME.CODEBASE_PEEK,
|
|
17294
17827
|
TOOL_NAME.FIND_SIMILAR,
|
|
@@ -17332,10 +17865,30 @@ function registerMcpTools(server, runtime) {
|
|
|
17332
17865
|
directory: allowNullAsUndefined(import_zod2.z.string().optional()).describe("Filter by directory path (e.g., 'src/utils', 'lib')"),
|
|
17333
17866
|
tokenBudget: allowNullAsUndefined(
|
|
17334
17867
|
import_zod2.z.number().int().min(MIN_CONTEXT_PACK_TOKEN_BUDGET).max(MAX_CONTEXT_PACK_TOKEN_BUDGET).optional().default(DEFAULT_CONTEXT_PACK_TOKEN_BUDGET)
|
|
17335
|
-
).describe(`Maximum response tokens for this context pack (${MIN_CONTEXT_PACK_TOKEN_BUDGET}-${MAX_CONTEXT_PACK_TOKEN_BUDGET})`)
|
|
17868
|
+
).describe(`Maximum response tokens for this context pack (${MIN_CONTEXT_PACK_TOKEN_BUDGET}-${MAX_CONTEXT_PACK_TOKEN_BUDGET})`),
|
|
17869
|
+
diagnostic: import_zod2.z.boolean().optional().describe("Collect diagnostic routing and search traces without changing normal text output.")
|
|
17336
17870
|
},
|
|
17337
17871
|
async (args) => {
|
|
17338
17872
|
const result = await executeCodebaseContext(runtime.projectRoot, runtime.host, args);
|
|
17873
|
+
return {
|
|
17874
|
+
content: [{ type: "text", text: result.text }],
|
|
17875
|
+
...args.diagnostic ? { structuredContent: result.details } : {}
|
|
17876
|
+
};
|
|
17877
|
+
}
|
|
17878
|
+
);
|
|
17879
|
+
server.tool(
|
|
17880
|
+
TOOL_NAME.CODEBASE_EDIT_CONTEXT,
|
|
17881
|
+
"PRE-EDIT TOOL for a known or suspected symbol. Returns token-bounded target source, direct callers and callees, or a risk-marked conceptual fallback when resolution is unsafe.",
|
|
17882
|
+
{
|
|
17883
|
+
query: import_zod2.z.string().describe("The requested change or target behavior."),
|
|
17884
|
+
symbol: allowNullAsUndefined(import_zod2.z.string().optional()).describe("Authoritative target symbol when known."),
|
|
17885
|
+
filePath: allowNullAsUndefined(import_zod2.z.string().optional()).describe("Optional file path used to disambiguate duplicate symbol names."),
|
|
17886
|
+
callerLimit: allowNullAsUndefined(import_zod2.z.number().int().min(MIN_CODEBASE_EDIT_CONTEXT_EDGE_LIMIT).max(MAX_CODEBASE_EDIT_CONTEXT_EDGE_LIMIT).optional().default(DEFAULT_CODEBASE_EDIT_CONTEXT_EDGE_LIMIT)),
|
|
17887
|
+
calleeLimit: allowNullAsUndefined(import_zod2.z.number().int().min(MIN_CODEBASE_EDIT_CONTEXT_EDGE_LIMIT).max(MAX_CODEBASE_EDIT_CONTEXT_EDGE_LIMIT).optional().default(DEFAULT_CODEBASE_EDIT_CONTEXT_EDGE_LIMIT)),
|
|
17888
|
+
tokenBudget: allowNullAsUndefined(import_zod2.z.number().int().min(MIN_CONTEXT_PACK_TOKEN_BUDGET).max(MAX_CONTEXT_PACK_TOKEN_BUDGET).optional().default(DEFAULT_CONTEXT_PACK_TOKEN_BUDGET))
|
|
17889
|
+
},
|
|
17890
|
+
async (args) => {
|
|
17891
|
+
const result = await executeCodebaseEditContext(runtime.projectRoot, runtime.host, args);
|
|
17339
17892
|
return { content: [{ type: "text", text: result.text }] };
|
|
17340
17893
|
}
|
|
17341
17894
|
);
|
|
@@ -19598,6 +20151,8 @@ var GitHeadWatcher = class {
|
|
|
19598
20151
|
debounceTimer = null;
|
|
19599
20152
|
debounceMs = 100;
|
|
19600
20153
|
// Short debounce for git operations
|
|
20154
|
+
readyPromise = Promise.resolve();
|
|
20155
|
+
resolveReady = null;
|
|
19601
20156
|
constructor(projectRoot) {
|
|
19602
20157
|
this.projectRoot = projectRoot;
|
|
19603
20158
|
}
|
|
@@ -19606,8 +20161,12 @@ var GitHeadWatcher = class {
|
|
|
19606
20161
|
return;
|
|
19607
20162
|
}
|
|
19608
20163
|
if (!isGitRepo(this.projectRoot)) {
|
|
20164
|
+
this.readyPromise = Promise.resolve();
|
|
19609
20165
|
return;
|
|
19610
20166
|
}
|
|
20167
|
+
this.readyPromise = new Promise((resolve18) => {
|
|
20168
|
+
this.resolveReady = resolve18;
|
|
20169
|
+
});
|
|
19611
20170
|
this.onBranchChange = handler;
|
|
19612
20171
|
this.currentBranch = getCurrentBranch(this.projectRoot);
|
|
19613
20172
|
const headPath = getHeadPath(this.projectRoot);
|
|
@@ -19622,6 +20181,10 @@ var GitHeadWatcher = class {
|
|
|
19622
20181
|
});
|
|
19623
20182
|
this.watcher.on("change", () => this.handleHeadChange());
|
|
19624
20183
|
this.watcher.on("add", () => this.handleHeadChange());
|
|
20184
|
+
this.watcher.once("ready", () => {
|
|
20185
|
+
this.resolveReady?.();
|
|
20186
|
+
this.resolveReady = null;
|
|
20187
|
+
});
|
|
19625
20188
|
}
|
|
19626
20189
|
handleHeadChange() {
|
|
19627
20190
|
if (this.debounceTimer) {
|
|
@@ -19659,10 +20222,16 @@ var GitHeadWatcher = class {
|
|
|
19659
20222
|
await watcher.close();
|
|
19660
20223
|
}
|
|
19661
20224
|
this.onBranchChange = null;
|
|
20225
|
+
this.resolveReady?.();
|
|
20226
|
+
this.resolveReady = null;
|
|
20227
|
+
this.readyPromise = Promise.resolve();
|
|
19662
20228
|
}
|
|
19663
20229
|
isRunning() {
|
|
19664
20230
|
return this.watcher !== null;
|
|
19665
20231
|
}
|
|
20232
|
+
async waitUntilReady() {
|
|
20233
|
+
await this.readyPromise;
|
|
20234
|
+
}
|
|
19666
20235
|
};
|
|
19667
20236
|
|
|
19668
20237
|
// src/watcher/index.ts
|
|
@@ -19712,7 +20281,10 @@ function createWatcherWithIndexer(getIndexer, projectRoot, config, host, options
|
|
|
19712
20281
|
fileWatcher,
|
|
19713
20282
|
gitWatcher,
|
|
19714
20283
|
whenReady() {
|
|
19715
|
-
return
|
|
20284
|
+
return Promise.all([
|
|
20285
|
+
fileWatcher.waitUntilReady(),
|
|
20286
|
+
gitWatcher?.waitUntilReady()
|
|
20287
|
+
]).then(() => void 0);
|
|
19716
20288
|
},
|
|
19717
20289
|
async stop() {
|
|
19718
20290
|
stopped = true;
|
|
@@ -19773,9 +20345,9 @@ function parseGitActivity(output) {
|
|
|
19773
20345
|
if (!filePath || addedRaw === "-" || deletedRaw === "-") continue;
|
|
19774
20346
|
const churn = Number(addedRaw) + Number(deletedRaw);
|
|
19775
20347
|
if (!Number.isFinite(churn) || churn <= 0) continue;
|
|
19776
|
-
const
|
|
19777
|
-
const previous = activity.get(
|
|
19778
|
-
activity.set(
|
|
20348
|
+
const normalizedPath3 = normalizePath4(filePath);
|
|
20349
|
+
const previous = activity.get(normalizedPath3);
|
|
20350
|
+
activity.set(normalizedPath3, {
|
|
19779
20351
|
churn: (previous?.churn ?? 0) + churn,
|
|
19780
20352
|
commits: (previous?.commits ?? 0) + 1,
|
|
19781
20353
|
latestDate: previous?.latestDate ?? latestDate,
|
|
@@ -20365,8 +20937,8 @@ function transformForVisualization(symbols, edges, options = {}) {
|
|
|
20365
20937
|
const normalizedAbsoluteSuffix = `/${normalizedDirWithSlash}`;
|
|
20366
20938
|
filteredSymbols = symbols.filter(
|
|
20367
20939
|
(s) => {
|
|
20368
|
-
const
|
|
20369
|
-
return
|
|
20940
|
+
const normalizedPath3 = s.filePath.replace(/\\/g, "/");
|
|
20941
|
+
return normalizedPath3 === normalizedDir || normalizedPath3.startsWith(normalizedDirWithSlash) || normalizedPath3.endsWith(`/${normalizedDir}`) || normalizedPath3.includes(normalizedAbsoluteSuffix);
|
|
20370
20942
|
}
|
|
20371
20943
|
);
|
|
20372
20944
|
}
|