flowseeker 0.1.8 → 0.1.9
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/CHANGELOG.md +12 -0
- package/dist/chat/nativeChatParticipant.js +1 -1
- package/dist/cli/main.js +19 -3
- package/dist/cli/runEvaluation.js +103 -0
- package/dist/eval/accuracyV2.js +483 -0
- package/dist/eval/goldenTask.js +192 -0
- package/dist/framework/laravel.js +177 -0
- package/dist/index/semanticChunkIndex.js +388 -0
- package/dist/index/workspaceIndex.js +339 -29
- package/dist/mcp/mcpTools.js +1627 -2
- package/dist/pipeline/contextBlueprint.js +15 -2
- package/dist/pipeline/fileScanner.js +116 -1
- package/dist/pipeline/fusionTrace.js +149 -0
- package/dist/pipeline/nodeScan.js +13 -2
- package/dist/pipeline/roleRefinement.js +62 -0
- package/dist/pipeline/runHeadless.js +25 -5
- package/dist/pipeline/runPipeline.js +2 -2
- package/dist/pipeline/solvePacket.js +112 -6
- package/dist/pipeline/taskUnderstanding.js +2 -2
- package/dist/ui/chatViewProvider.js +1 -1
- package/package.json +2 -2
|
@@ -38,13 +38,15 @@ exports.renderSolvePacketMarkdown = renderSolvePacketMarkdown;
|
|
|
38
38
|
const fs = __importStar(require("fs/promises"));
|
|
39
39
|
const fileGroups_1 = require("./fileGroups");
|
|
40
40
|
const contextBlueprint_1 = require("./contextBlueprint");
|
|
41
|
+
const workspaceIndex_1 = require("../index/workspaceIndex");
|
|
42
|
+
const fusionTrace_1 = require("./fusionTrace");
|
|
41
43
|
const maxPrimaryEditFiles = 7;
|
|
42
44
|
const maxReadOnlyFiles = 7;
|
|
43
45
|
const maxVerificationFiles = 6;
|
|
44
46
|
const maxNoiseRiskFiles = 6;
|
|
45
47
|
const maxSnippetChars = 850;
|
|
46
48
|
const minSnippetBudgetChars = 9000;
|
|
47
|
-
async function buildSolvePacket(result, config) {
|
|
49
|
+
async function buildSolvePacket(result, config, workspaceRoot) {
|
|
48
50
|
const groups = (0, fileGroups_1.aggregateEvidenceFiles)(result.units);
|
|
49
51
|
const coverageGroups = selectCoverageGroups(groups, result);
|
|
50
52
|
const primaryEditCandidates = coverageGroups.filter((group) => group.role === "edit_candidate" && group.slot !== "tests" && !group.contrastContext);
|
|
@@ -73,15 +75,42 @@ async function buildSolvePacket(result, config) {
|
|
|
73
75
|
const snippetBudget = {
|
|
74
76
|
remainingChars: Math.max(minSnippetBudgetChars, config.pipeline.maxContextTokens * 4)
|
|
75
77
|
};
|
|
78
|
+
// Build packet files
|
|
79
|
+
const primaryFiles = await toPacketFiles(primaryEditCandidates.slice(0, Math.min(maxPrimaryEditFiles, config.pipeline.maxCandidates)), snippetBudget, "primary", result);
|
|
80
|
+
const readOnlyFiles = await toPacketFiles(readOnlyContext.slice(0, Math.min(maxReadOnlyFiles, config.pipeline.maxCandidates)), snippetBudget, "context", result);
|
|
81
|
+
const verificationFiles = await toPacketFiles(verificationTargets.slice(0, Math.min(maxVerificationFiles, config.pipeline.maxCandidates)), snippetBudget, "verification", result);
|
|
82
|
+
const noiseFiles = await toPacketFiles(noiseRisk.slice(0, Math.min(maxNoiseRiskFiles, config.pipeline.maxCandidates)), snippetBudget, "noise", result);
|
|
83
|
+
// Chunk scoring (15E, report-only)
|
|
84
|
+
const allFilePaths = [
|
|
85
|
+
...primaryFiles.map((f) => f.relativePath),
|
|
86
|
+
...readOnlyFiles.map((f) => f.relativePath),
|
|
87
|
+
];
|
|
88
|
+
const topChunks = (0, workspaceIndex_1.scoreChunksForTask)(allFilePaths, result.profile, workspaceRoot, { maxChunksPerFile: 3, maxTotalChunks: 30 });
|
|
89
|
+
const chunksByFile = new Map();
|
|
90
|
+
for (const tc of topChunks) {
|
|
91
|
+
const existing = chunksByFile.get(tc.filePath) || [];
|
|
92
|
+
existing.push(tc);
|
|
93
|
+
chunksByFile.set(tc.filePath, existing);
|
|
94
|
+
}
|
|
95
|
+
// Attach top chunks to packet files
|
|
96
|
+
const attachChunks = (files) => {
|
|
97
|
+
for (const f of files) {
|
|
98
|
+
const matches = chunksByFile.get(f.relativePath);
|
|
99
|
+
if (matches && matches.length > 0)
|
|
100
|
+
f.topChunks = matches.slice(0, 3);
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
attachChunks(primaryFiles);
|
|
104
|
+
attachChunks(readOnlyFiles);
|
|
76
105
|
return {
|
|
77
106
|
userTask: result.task,
|
|
78
107
|
taskShape: result.profile.taskShape,
|
|
79
108
|
problemHypothesis: buildHypothesis(result, groups),
|
|
80
109
|
candidateFlow: buildCandidateFlow(coverageGroups, result),
|
|
81
|
-
primaryEditCandidates:
|
|
82
|
-
readOnlyContext:
|
|
83
|
-
verificationTargets:
|
|
84
|
-
noiseRisk:
|
|
110
|
+
primaryEditCandidates: primaryFiles,
|
|
111
|
+
readOnlyContext: readOnlyFiles,
|
|
112
|
+
verificationTargets: verificationFiles,
|
|
113
|
+
noiseRisk: noiseFiles,
|
|
85
114
|
missingLinks: detectMissingLinks(coverageGroups, result),
|
|
86
115
|
verificationCommands: suggestVerificationCommands(result),
|
|
87
116
|
retrievalTrace: buildRetrievalTrace(groups),
|
|
@@ -90,7 +119,16 @@ async function buildSolvePacket(result, config) {
|
|
|
90
119
|
semanticDiagnostic: buildSemanticDiagnostic(result.stats, config),
|
|
91
120
|
firstThreeFiles: buildFirstThree(primaryEditCandidates, readOnlyContext, verificationTargets, noiseRisk, groups, result),
|
|
92
121
|
whyTheseFiles: buildWhyTheseFiles(result, groups),
|
|
93
|
-
doNotEditYet: buildDoNotEditYet(readOnlyContext, noiseRisk, coverageGroups, result)
|
|
122
|
+
doNotEditYet: buildDoNotEditYet(readOnlyContext, noiseRisk, coverageGroups, result),
|
|
123
|
+
// Chunk-assisted evidence
|
|
124
|
+
chunkAssistedMode: "file_level_current",
|
|
125
|
+
topChunksSummary: topChunks.slice(0, 15),
|
|
126
|
+
// RRF fusion trace (report-only)
|
|
127
|
+
rrfComparison: (0, fusionTrace_1.computeRrfTrace)(result) ?? undefined,
|
|
128
|
+
// Role refinement trace (16D-R1)
|
|
129
|
+
roleRefinementMode: result.stats.roleRefinementMode ?? "off",
|
|
130
|
+
roleDemotions: result.stats.roleDemotions ?? 0,
|
|
131
|
+
roleDemotionLog: result.stats.roleDemotionLog ?? [],
|
|
94
132
|
};
|
|
95
133
|
}
|
|
96
134
|
function buildFirstThree(primary, readOnly, verification, noise, allGroups, result) {
|
|
@@ -326,9 +364,56 @@ function renderSolvePacketMarkdown(packet) {
|
|
|
326
364
|
}
|
|
327
365
|
lines.push("");
|
|
328
366
|
}
|
|
367
|
+
// Role Decision Trace (16D-R1)
|
|
368
|
+
var rMode = packet.roleRefinementMode || "off";
|
|
369
|
+
var demotions = packet.roleDemotions || 0;
|
|
370
|
+
var demotionLog = packet.roleDemotionLog || [];
|
|
371
|
+
lines.push("## Role Decision Trace", "", `Role refinement: ${rMode}` + (rMode !== "conservative" ? " (opt-in conservative available via config pipeline.roleRefinementMode)" : ""), "");
|
|
372
|
+
// Edit candidates
|
|
373
|
+
for (var ed = 0; ed < Math.min(packet.primaryEditCandidates.length, 5); ed++) {
|
|
374
|
+
var f = packet.primaryEditCandidates[ed];
|
|
375
|
+
var topChunks = f.topChunks || [];
|
|
376
|
+
var chunkInfo = topChunks.length > 0 ? " chunks=" + topChunks.slice(0, 2).map(function (c) { return c.kind + "@L" + c.startLine; }).join(",") : "";
|
|
377
|
+
lines.push(` edit: ${f.relativePath} score=${f.score.toFixed(0)} role=${f.role} tier=${f.tier}${chunkInfo}`);
|
|
378
|
+
}
|
|
379
|
+
// Demoted files (conservative mode)
|
|
380
|
+
if (rMode === "conservative" && demotions > 0) {
|
|
381
|
+
lines.push("", " Demoted (not edit targets in conservative mode):");
|
|
382
|
+
for (var dm = 0; dm < Math.min(demotionLog.length, 5); dm++) {
|
|
383
|
+
var d = demotionLog[dm];
|
|
384
|
+
lines.push(` ${d.beforeRole}→${d.afterRole}: ${d.filePath} rule=${d.ruleId} confidence=${d.confidence} reason="${d.reason}"`);
|
|
385
|
+
}
|
|
386
|
+
if (demotionLog.length > 5)
|
|
387
|
+
lines.push(` ... and ${demotionLog.length - 5} more demotions`);
|
|
388
|
+
}
|
|
389
|
+
// Read-only context
|
|
390
|
+
if (packet.readOnlyContext.length > 0) {
|
|
391
|
+
lines.push("", " Read-only context:");
|
|
392
|
+
for (var rc = 0; rc < Math.min(packet.readOnlyContext.length, 3); rc++) {
|
|
393
|
+
var rcf = packet.readOnlyContext[rc];
|
|
394
|
+
lines.push(` read: ${rcf.relativePath} role=${rcf.role}`);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
lines.push("");
|
|
398
|
+
if (rMode !== "conservative") {
|
|
399
|
+
lines.push(" Caution: role refinement is off/opt-in. AI agents should validate edit candidates before editing.", "");
|
|
400
|
+
}
|
|
401
|
+
lines.push(` Default role decisions are from the current ranker.${packet.primaryEditCandidates.length > 0 ? " Verify relevance of each edit candidate." : ""}`, "");
|
|
329
402
|
if (packet.tokenBudgetWarning) {
|
|
330
403
|
lines.push("## Token Budget Warning", packet.tokenBudgetWarning, "");
|
|
331
404
|
}
|
|
405
|
+
// RRF Fusion Trace (15G-R1, report-only)
|
|
406
|
+
if (packet.rrfComparison) {
|
|
407
|
+
const r = packet.rrfComparison;
|
|
408
|
+
lines.push("## RRF Fusion Trace", "", `Mode: report-only | Default ranking changed: ${r.defaultRankingChanged} | k: ${r.k}`, `Overlap: @3=${r.overlapAt3}/${Math.min(3, r.currentTopFiles.length, r.rrfTopFiles.length)} @5=${r.overlapAt5}/${Math.min(5, r.currentTopFiles.length, r.rrfTopFiles.length)} @10=${r.overlapAt10}/${Math.min(10, r.currentTopFiles.length, r.rrfTopFiles.length)}`, `Active channels: ${r.channelTraces.filter((c) => c.active).map((c) => c.channel).join(", ") || "none"}`, `Inactive channels: ${r.channelTraces.filter((c) => !c.active).map((c) => c.channel).join(", ") || "none"}`, "", `Current top files (${r.currentTopFiles.length}): ${r.currentTopFiles.slice(0, 8).join(", ")}`, `RRF top files (${r.rrfTopFiles.length}): ${r.rrfTopFiles.slice(0, 8).join(", ")}`, ...(r.promotedByRrf.length > 0 ? [`Promoted by RRF: ${r.promotedByRrf.join(", ")}`] : ["Promoted by RRF: none"]), ...(r.demotedByRrf.length > 0 ? [`Demoted by RRF: ${r.demotedByRrf.join(", ")}`] : ["Demoted by RRF: none"]), "", "Top RRF channel contributions:");
|
|
409
|
+
for (const entry of r.topRrfEntries.slice(0, 5)) {
|
|
410
|
+
const contribs = Object.entries(entry.channelContributions)
|
|
411
|
+
.map(([ch, val]) => `${ch}=${val.toFixed(4)}`)
|
|
412
|
+
.join(", ");
|
|
413
|
+
lines.push(` ${entry.relativePath} rrfScore=${entry.rrfScore.toFixed(4)} [${contribs}]`);
|
|
414
|
+
}
|
|
415
|
+
lines.push("");
|
|
416
|
+
}
|
|
332
417
|
lines.push("## Agent Instructions", ...packet.agentInstructions.map((instruction, index) => `${index + 1}. ${instruction}`), "");
|
|
333
418
|
return lines.join("\n");
|
|
334
419
|
}
|
|
@@ -469,11 +554,26 @@ function renderPacketFiles(files, isReadOnly, placeholderText) {
|
|
|
469
554
|
...(file.connectedContext && file.connectedContext.length > 0 ? [` ${connectedLabel}: ${file.connectedContext.join(", ")}`] : []),
|
|
470
555
|
...(file.retrievalTrace && file.retrievalTrace.length > 0 ? [` Selected by: ${file.retrievalTrace.join("; ")}`] : []),
|
|
471
556
|
...(file.reasons.length > 0 ? [` Reasons: ${file.reasons.join("; ")}`] : []),
|
|
557
|
+
...(file.topChunks && file.topChunks.length > 0 ? renderTopChunks(file.topChunks) : []),
|
|
472
558
|
...(file.snippet ? ["", "```", file.snippet, "```"] : []),
|
|
473
559
|
""
|
|
474
560
|
];
|
|
475
561
|
});
|
|
476
562
|
}
|
|
563
|
+
function renderTopChunks(chunks) {
|
|
564
|
+
if (!chunks || chunks.length === 0)
|
|
565
|
+
return [];
|
|
566
|
+
const lines = [" Top Chunks:"];
|
|
567
|
+
for (const tc of chunks) {
|
|
568
|
+
const preview = (tc.contentPreview || "").slice(0, 120).replace(/\n/g, "\\n");
|
|
569
|
+
const symbol = tc.symbolName ? ` symbol=${tc.symbolName}` : "";
|
|
570
|
+
lines.push(` L${tc.startLine}-${tc.endLine} ${tc.kind}/${tc.slot} score=${tc.score}${symbol}`);
|
|
571
|
+
lines.push(` → ${tc.reasons.join(", ")}`);
|
|
572
|
+
if (preview)
|
|
573
|
+
lines.push(` \`${preview}\``);
|
|
574
|
+
}
|
|
575
|
+
return lines;
|
|
576
|
+
}
|
|
477
577
|
function sectionLabel(section) {
|
|
478
578
|
return (0, contextBlueprint_1.contextSlotLabel)(section);
|
|
479
579
|
}
|
|
@@ -821,6 +921,12 @@ function detectMissingLinks(groups, result) {
|
|
|
821
921
|
missing.push(`Scan stopped early (${result.stats.stopReason ?? "unknown reason"}). Lower-ranked context may exist outside the scan window. Do not convert this scan-cap uncertainty into a missing-slot claim — only the slots listed above are verified missing from the scanned evidence.`);
|
|
822
922
|
}
|
|
823
923
|
}
|
|
924
|
+
// Append framework-specific hints when slots are missing and framework hints exist
|
|
925
|
+
if (missing.length > 0 && result.profile.blueprint.frameworkHints?.length) {
|
|
926
|
+
for (const hint of result.profile.blueprint.frameworkHints) {
|
|
927
|
+
missing.push(hint);
|
|
928
|
+
}
|
|
929
|
+
}
|
|
824
930
|
return missing;
|
|
825
931
|
}
|
|
826
932
|
function computeCoveredSlots(groups, result) {
|
|
@@ -106,7 +106,7 @@ const lowSignalInstructionTerms = new Set([
|
|
|
106
106
|
"function",
|
|
107
107
|
"functionality"
|
|
108
108
|
]);
|
|
109
|
-
function understandTask(task, config) {
|
|
109
|
+
async function understandTask(task, config, workspaceRoot) {
|
|
110
110
|
const normalizedTask = (0, text_1.normalizeText)(task);
|
|
111
111
|
const rawTokens = (0, text_1.splitIdentifier)(task);
|
|
112
112
|
const literalCodeTerms = detectLiteralCodeTerms(task);
|
|
@@ -118,7 +118,7 @@ function understandTask(task, config) {
|
|
|
118
118
|
const keywords = (0, text_1.uniq)([...literalCodeTerms, ...concepts, ...synonymTerms, ...actionTerms]).filter((term) => term.length > 1);
|
|
119
119
|
const intent = detectIntent(normalizedTask);
|
|
120
120
|
const negativeTerms = detectNegativeTerms(normalizedTask);
|
|
121
|
-
const blueprint = (0, contextBlueprint_1.buildTaskBlueprint)({ intent: intent.intent, normalizedTask, concepts, actions, keywords, negativeTerms, confidence: intent.confidence });
|
|
121
|
+
const blueprint = await (0, contextBlueprint_1.buildTaskBlueprint)({ intent: intent.intent, normalizedTask, concepts, actions, keywords, negativeTerms, confidence: intent.confidence, workspaceRoot });
|
|
122
122
|
const retrieval = (0, retrievalPlan_1.createRetrievalPlan)({ rawTask: task, normalizedTask, concepts, actions, keywords, blueprint }, config);
|
|
123
123
|
return {
|
|
124
124
|
rawTask: task,
|
|
@@ -1102,7 +1102,7 @@ class ChatViewProvider {
|
|
|
1102
1102
|
}
|
|
1103
1103
|
renderHtml(webview) {
|
|
1104
1104
|
const nonce = getNonce();
|
|
1105
|
-
const logoUri = webview.asWebviewUri(vscode.Uri.joinPath(this.context.extensionUri, "resources", "flowseeker-logo
|
|
1105
|
+
const logoUri = webview.asWebviewUri(vscode.Uri.joinPath(this.context.extensionUri, "resources", "flowseeker-logo.png"));
|
|
1106
1106
|
const providerOptions = aiProviders_1.AI_PROVIDER_DEFINITIONS.map((provider) => `<option value="${provider.id}">${escapeHtml(provider.label)}</option>`).join("");
|
|
1107
1107
|
const providerMeta = JSON.stringify(Object.fromEntries(aiProviders_1.AI_PROVIDER_DEFINITIONS.map((provider) => [
|
|
1108
1108
|
provider.id,
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "flowseeker",
|
|
3
3
|
"displayName": "FlowSeeker",
|
|
4
4
|
"description": "Find relevant codebase context for large-project tasks without loading the whole repo into AI. Measures Solve Packet token savings alongside retrieval quality.",
|
|
5
|
-
"version": "0.1.
|
|
5
|
+
"version": "0.1.9",
|
|
6
6
|
"publisher": "everestt2806",
|
|
7
7
|
"license": "MIT",
|
|
8
8
|
"icon": "resources/flowseeker.png",
|
|
@@ -235,7 +235,7 @@
|
|
|
235
235
|
{
|
|
236
236
|
"id": "flowseeker",
|
|
237
237
|
"title": "FlowSeeker",
|
|
238
|
-
"icon": "resources/flowseeker-sidebar
|
|
238
|
+
"icon": "resources/flowseeker-sidebar.png"
|
|
239
239
|
}
|
|
240
240
|
]
|
|
241
241
|
},
|