flowseeker 0.1.7 → 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/.env.example +7 -0
- package/CHANGELOG.md +143 -108
- package/README.md +288 -221
- package/dist/chat/nativeChatParticipant.js +1 -1
- package/dist/cli/flowCommand.js +175 -0
- package/dist/cli/main.js +1810 -0
- package/dist/cli/mcpServer.js +7 -1
- package/dist/cli/runEvaluation.js +281 -2
- package/dist/config/defaultConfig.js +11 -1
- package/dist/config/env.js +118 -0
- package/dist/config/loadConfig.js +18 -1
- package/dist/config/loadConfigFromPath.js +3 -1
- package/dist/eval/accuracyV2.js +483 -0
- package/dist/eval/goldenTask.js +192 -0
- package/dist/extension.js +23 -0
- package/dist/framework/laravel.js +177 -0
- package/dist/gateway/embeddingProviders.js +852 -0
- package/dist/index/cacheStore.js +43 -0
- package/dist/index/configRouteDiscoveryProbe.js +288 -0
- package/dist/index/embeddingIndex.js +193 -0
- package/dist/index/graphIndex.js +460 -0
- package/dist/index/indexWatcher.js +86 -0
- package/dist/index/semanticChunkIndex.js +388 -0
- package/dist/index/structuredExtractor.js +303 -12
- package/dist/index/treeSitterExtractor.js +264 -0
- package/dist/index/vectorStore.js +41 -0
- package/dist/index/workspaceIndex.js +901 -26
- package/dist/mcp/mcpTools.js +1678 -2
- package/dist/pipeline/contextBlueprint.js +15 -2
- package/dist/pipeline/contextPack.js +3 -3
- package/dist/pipeline/deterministicReranker.js +358 -0
- package/dist/pipeline/evaluationMetrics.js +14 -2
- package/dist/pipeline/fileGroups.js +7 -1
- package/dist/pipeline/fileScanner.js +209 -12
- package/dist/pipeline/fusionTrace.js +149 -0
- package/dist/pipeline/llmReranker.js +151 -0
- package/dist/pipeline/nodeScan.js +102 -12
- package/dist/pipeline/ranker.js +875 -16
- package/dist/pipeline/retrievalFusion.js +41 -0
- package/dist/pipeline/roleRefinement.js +62 -0
- package/dist/pipeline/runHeadless.js +60 -5
- package/dist/pipeline/runPipeline.js +2 -2
- package/dist/pipeline/solvePacket.js +656 -43
- package/dist/pipeline/subsystem.js +21 -0
- package/dist/pipeline/taskUnderstanding.js +2 -2
- package/dist/ui/chatViewProvider.js +1 -1
- package/docs/demo-screenshot-checklist.md +86 -0
- package/docs/marketplace-copy.md +92 -0
- package/docs/mcp-onboarding.md +191 -0
- package/package.json +633 -561
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.fuseRetrievalChannelHits = fuseRetrievalChannelHits;
|
|
4
|
+
const defaultRrfK = 60;
|
|
5
|
+
const defaultChannelWeights = {
|
|
6
|
+
exact_literal: 1.4,
|
|
7
|
+
path: 1.3,
|
|
8
|
+
symbol: 1.25,
|
|
9
|
+
lexical_content: 1,
|
|
10
|
+
semantic_vector: 0.9,
|
|
11
|
+
graph: 0.85,
|
|
12
|
+
dependency: 0.75,
|
|
13
|
+
editor_context: 0.4
|
|
14
|
+
};
|
|
15
|
+
function fuseRetrievalChannelHits(channelHits, options = {}) {
|
|
16
|
+
const rrfK = options.rrfK ?? defaultRrfK;
|
|
17
|
+
const weights = { ...defaultChannelWeights, ...options.channelWeights };
|
|
18
|
+
const candidates = new Map();
|
|
19
|
+
for (const hit of channelHits) {
|
|
20
|
+
const current = candidates.get(hit.relativePath) ?? {
|
|
21
|
+
relativePath: hit.relativePath,
|
|
22
|
+
score: 0,
|
|
23
|
+
channelHits: [],
|
|
24
|
+
topChannel: hit.channel,
|
|
25
|
+
reasons: []
|
|
26
|
+
};
|
|
27
|
+
const channelWeight = weights[hit.channel] ?? 1;
|
|
28
|
+
const previousTopScore = Math.max(0, ...current.channelHits.map((item) => item.score));
|
|
29
|
+
current.score += channelWeight / (rrfK + Math.max(1, hit.rank));
|
|
30
|
+
current.channelHits.push(hit);
|
|
31
|
+
if (hit.reason && !current.reasons.includes(hit.reason)) {
|
|
32
|
+
current.reasons.push(hit.reason);
|
|
33
|
+
}
|
|
34
|
+
if (hit.score > previousTopScore) {
|
|
35
|
+
current.topChannel = hit.channel;
|
|
36
|
+
}
|
|
37
|
+
candidates.set(hit.relativePath, current);
|
|
38
|
+
}
|
|
39
|
+
return Array.from(candidates.values()).sort((a, b) => b.score - a.score);
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=retrievalFusion.js.map
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.applyRoleRefinement = applyRoleRefinement;
|
|
4
|
+
function applyRoleRefinement(units, profile, config) {
|
|
5
|
+
const mode = config.pipeline.roleRefinementMode ?? "off";
|
|
6
|
+
if (mode !== "conservative")
|
|
7
|
+
return { units, demotions: [] };
|
|
8
|
+
const taskLower = profile.normalizedTask || "";
|
|
9
|
+
const taskShape = profile.taskShape || "unknown";
|
|
10
|
+
const taskExplicitlyTargetsTests = /\b(test|spec|verify|assert)\b/i.test(taskLower);
|
|
11
|
+
const taskTargetsRouteConfig = /\b(route|router|endpoint|url|config|env|setting)\b/i.test(taskLower);
|
|
12
|
+
const taskTargetsViewUI = /\b(view|page|layout|render|blade|template|ui|frontend|modal|component)\b/i.test(taskLower);
|
|
13
|
+
const taskNeedsSideEffects = /\b(job|queue|event|listener|notify|notification|email|mail|dispatch|async|webhook|cron|command|console)\b/i.test(taskLower);
|
|
14
|
+
const taskNamedFiles = extractNamedFiles(taskLower);
|
|
15
|
+
const demotions = [];
|
|
16
|
+
const refined = units.map((unit) => {
|
|
17
|
+
if (unit.role !== "edit_candidate")
|
|
18
|
+
return unit;
|
|
19
|
+
const fp = (unit.relativePath || "").toLowerCase().replace(/\\/g, "/");
|
|
20
|
+
const taskMentionsFile = taskNamedFiles.some((n) => fp.includes(n));
|
|
21
|
+
// Rule 1: tests/specs → test
|
|
22
|
+
if (!taskExplicitlyTargetsTests && /(^|\/)(tests?|specs?|__tests__)(\/|$)/.test(fp)) {
|
|
23
|
+
demotions.push({ filePath: unit.relativePath, beforeRole: "edit_candidate", afterRole: "test", reason: "test path demoted — task does not target tests", ruleId: "demote_test", confidence: "high", taskShape });
|
|
24
|
+
return { ...unit, role: "test" };
|
|
25
|
+
}
|
|
26
|
+
// Rule 2: routes/config/views → read_context
|
|
27
|
+
if (!taskTargetsRouteConfig && /(^|\/)(routes?|router|config)(\/|$)/.test(fp)) {
|
|
28
|
+
demotions.push({ filePath: unit.relativePath, beforeRole: "edit_candidate", afterRole: "read_context", reason: "route/config demoted to read_context — task does not target routing/config", ruleId: "demote_route_config", confidence: "high", taskShape });
|
|
29
|
+
return { ...unit, role: "read_context" };
|
|
30
|
+
}
|
|
31
|
+
if (!taskTargetsViewUI && /(^|\/)resources\/views(\/|$)/.test(fp)) {
|
|
32
|
+
demotions.push({ filePath: unit.relativePath, beforeRole: "edit_candidate", afterRole: "read_context", reason: "view demoted to read_context — task does not target UI/view", ruleId: "demote_view", confidence: "high", taskShape });
|
|
33
|
+
return { ...unit, role: "read_context" };
|
|
34
|
+
}
|
|
35
|
+
// Rule 3: helpers/utilities → read_context
|
|
36
|
+
if (!taskMentionsFile && /(^|\/)(helpers?|utils?)(\/|$)/i.test(fp)) {
|
|
37
|
+
demotions.push({ filePath: unit.relativePath, beforeRole: "edit_candidate", afterRole: "read_context", reason: "helper/utility demoted to read_context — not directly named by task", ruleId: "demote_helper", confidence: "medium", taskShape });
|
|
38
|
+
return { ...unit, role: "read_context" };
|
|
39
|
+
}
|
|
40
|
+
// Rule 4: side effects → read_context unless task needs them (includes Console/Commands)
|
|
41
|
+
if (!taskNeedsSideEffects && /(^|\/)(Jobs|Events|Listeners|Mail|Notifications|Console\/Commands)(\/|$)/i.test(fp)) {
|
|
42
|
+
demotions.push({ filePath: unit.relativePath, beforeRole: "edit_candidate", afterRole: "read_context", reason: "side_effect file demoted to read_context — task does not need side effects", ruleId: "demote_side_effect", confidence: "high", taskShape });
|
|
43
|
+
return { ...unit, role: "read_context" };
|
|
44
|
+
}
|
|
45
|
+
// Rule 5: contrast/avoid → read_context
|
|
46
|
+
if (unit.contrastContext || unit.taskAvoidGuardFlag === "demoted" || unit.taskAvoidGuardFlag === "blocked_slot") {
|
|
47
|
+
demotions.push({ filePath: unit.relativePath, beforeRole: "edit_candidate", afterRole: "read_context", reason: "contrast/avoid file demoted — flagged by guard", ruleId: "demote_contrast_avoid", confidence: "high", taskShape });
|
|
48
|
+
return { ...unit, role: "read_context" };
|
|
49
|
+
}
|
|
50
|
+
return unit;
|
|
51
|
+
});
|
|
52
|
+
return { units: refined, demotions };
|
|
53
|
+
}
|
|
54
|
+
function extractNamedFiles(taskLower) {
|
|
55
|
+
const names = [];
|
|
56
|
+
const m = taskLower.match(/['"]?([\w./-]+\.(?:php|ts|js|py|java|rb|go|cs|kt|rs|dart))['"]?/g);
|
|
57
|
+
if (m)
|
|
58
|
+
for (const n of m)
|
|
59
|
+
names.push(n.replace(/['"]/g, "").toLowerCase());
|
|
60
|
+
return names;
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=roleRefinement.js.map
|
|
@@ -7,20 +7,32 @@ const evidenceGraph_1 = require("./evidenceGraph");
|
|
|
7
7
|
const contextBlueprint_1 = require("./contextBlueprint");
|
|
8
8
|
const nodeScan_1 = require("./nodeScan");
|
|
9
9
|
const ranker_1 = require("./ranker");
|
|
10
|
+
const roleRefinement_1 = require("./roleRefinement");
|
|
10
11
|
const solvePacket_1 = require("./solvePacket");
|
|
11
12
|
const taskUnderstanding_1 = require("./taskUnderstanding");
|
|
12
13
|
const tokenSavings_1 = require("./tokenSavings");
|
|
14
|
+
const semanticChunkIndex_1 = require("../index/semanticChunkIndex");
|
|
13
15
|
async function runFlowSeekerHeadless(workspacePath, task, config, options = {}) {
|
|
14
16
|
const log = (stage, detail) => options.onStage?.(stage, detail);
|
|
15
17
|
log("task", task);
|
|
16
|
-
const
|
|
18
|
+
const tTaskStart = performance.now();
|
|
19
|
+
const profile = await (0, taskUnderstanding_1.understandTask)(task, config, workspacePath);
|
|
20
|
+
const taskDurationMs = Math.round(performance.now() - tTaskStart);
|
|
17
21
|
log("scan", `${profile.intent} | ${profile.keywords.length} keywords`);
|
|
22
|
+
const tScanStart = performance.now();
|
|
18
23
|
const scan = await (0, nodeScan_1.nodeScanWorkspace)(workspacePath, profile, config, options);
|
|
24
|
+
const scanLoopDurationMs = Math.round(performance.now() - tScanStart);
|
|
19
25
|
log("rank", `${scan.units.length} raw units from ${scan.stats.scannedFiles} files`);
|
|
26
|
+
const tRank1Start = performance.now();
|
|
20
27
|
const ranked = (0, ranker_1.rankEvidence)(scan.units, config, profile);
|
|
28
|
+
const rankFirstPassDurationMs = Math.round(performance.now() - tRank1Start);
|
|
21
29
|
log("graph", `${ranked.length} units`);
|
|
30
|
+
const tGraphStart = performance.now();
|
|
22
31
|
const graph = (0, evidenceGraph_1.buildEvidenceGraph)(ranked);
|
|
32
|
+
const graphBuildDurationMs = Math.round(performance.now() - tGraphStart);
|
|
33
|
+
const tFrameworkStart = performance.now();
|
|
23
34
|
const frameworkResult = await (0, async_1.withTimeout)((0, evidenceGraph_1.extractFrameworkEdges)(ranked, (msg) => options.onStage?.("framework", msg)), 15000, "framework edge extraction").catch(() => ({ edges: [], newUnits: [] }));
|
|
35
|
+
const frameworkExpansionDurationMs = Math.round(performance.now() - tFrameworkStart);
|
|
24
36
|
if (frameworkResult.edges.length > 0 || frameworkResult.newUnits.length > 0) {
|
|
25
37
|
log("framework", `${frameworkResult.edges.length} edges, ${frameworkResult.newUnits.length} discovered`);
|
|
26
38
|
}
|
|
@@ -31,24 +43,67 @@ async function runFlowSeekerHeadless(workspacePath, task, config, options = {})
|
|
|
31
43
|
if (frameworkResult.newUnits.length > 0) {
|
|
32
44
|
allUnits = [...ranked, ...frameworkResult.newUnits];
|
|
33
45
|
}
|
|
46
|
+
const tBoostStart = performance.now();
|
|
34
47
|
const graphBoosted = (0, evidenceGraph_1.boostWithGraph)(allUnits, graph);
|
|
48
|
+
const graphBoostDurationMs = Math.round(performance.now() - tBoostStart);
|
|
49
|
+
const tRank2Start = performance.now();
|
|
35
50
|
const finalUnits = (0, ranker_1.rankEvidence)(graphBoosted, config, profile);
|
|
36
|
-
const
|
|
51
|
+
const rankFinalPassDurationMs = Math.round(performance.now() - tRank2Start);
|
|
52
|
+
// Conservative role demotion (16B, config-gated, default off)
|
|
53
|
+
const refinement = (0, roleRefinement_1.applyRoleRefinement)(finalUnits, profile, config);
|
|
54
|
+
const refinedUnits = refinement.units;
|
|
55
|
+
const tCtxStart = performance.now();
|
|
56
|
+
const contextPack = (0, contextPack_1.createContextPack)(task, refinedUnits, config);
|
|
57
|
+
const contextPackDurationMs = Math.round(performance.now() - tCtxStart);
|
|
37
58
|
const result = {
|
|
38
59
|
task,
|
|
39
60
|
profile,
|
|
40
|
-
units:
|
|
61
|
+
units: refinedUnits,
|
|
41
62
|
contextPack,
|
|
42
63
|
stats: {
|
|
43
64
|
...scan.stats,
|
|
44
65
|
rankedEvidenceCount: finalUnits.length,
|
|
45
66
|
maxCandidates: config.pipeline.maxCandidates,
|
|
46
|
-
capped: scan.stats.rawEvidenceCount > finalUnits.length
|
|
67
|
+
capped: scan.stats.rawEvidenceCount > finalUnits.length,
|
|
68
|
+
// Role refinement telemetry (16B/16D-R1) — needed before buildSolvePacket
|
|
69
|
+
roleRefinementMode: config.pipeline.roleRefinementMode ?? "off",
|
|
70
|
+
roleDemotions: refinement.demotions.length,
|
|
71
|
+
roleDemotionLog: refinement.demotions.slice(0, 100),
|
|
47
72
|
}
|
|
48
73
|
};
|
|
49
|
-
|
|
74
|
+
const tSolveStart = performance.now();
|
|
75
|
+
result.solvePacket = await (0, solvePacket_1.buildSolvePacket)(result, config, workspacePath);
|
|
76
|
+
const solvePacketDurationMs = Math.round(performance.now() - tSolveStart);
|
|
77
|
+
const tPostStart = performance.now();
|
|
50
78
|
result.contextCoverage = (0, contextBlueprint_1.computeContextCoverage)(profile, finalUnits);
|
|
51
79
|
result.tokenSavings = (0, tokenSavings_1.computeTokenSavings)(result);
|
|
80
|
+
// Semantic chunk diagnostics (18A — report-only, disabled by default)
|
|
81
|
+
result.stats = {
|
|
82
|
+
...result.stats,
|
|
83
|
+
semanticChunkDiagnostic: (0, semanticChunkIndex_1.buildSemanticChunkDiagnostic)(config, {
|
|
84
|
+
chunksConsidered: 0, cacheHits: 0, cacheMisses: 0,
|
|
85
|
+
candidatesFound: 0, candidatesAccepted: 0, candidatesRejected: 0,
|
|
86
|
+
topK: 0, providerReady: false,
|
|
87
|
+
contributionReport: (0, semanticChunkIndex_1.buildContributionReport)(false, false, 0, 0),
|
|
88
|
+
hybridPromotionReport: (0, semanticChunkIndex_1.buildHybridPromotionReport)(false, false, [], [], []),
|
|
89
|
+
}),
|
|
90
|
+
};
|
|
91
|
+
const postProcessDurationMs = Math.round(performance.now() - tPostStart);
|
|
92
|
+
const totalDurationMs = taskDurationMs + scanLoopDurationMs + rankFirstPassDurationMs + graphBuildDurationMs + frameworkExpansionDurationMs + graphBoostDurationMs + rankFinalPassDurationMs + contextPackDurationMs + solvePacketDurationMs + postProcessDurationMs;
|
|
93
|
+
result.stats = {
|
|
94
|
+
...result.stats,
|
|
95
|
+
totalDurationMs,
|
|
96
|
+
taskDurationMs,
|
|
97
|
+
scanLoopDurationMs,
|
|
98
|
+
rankFirstPassDurationMs,
|
|
99
|
+
graphBuildDurationMs,
|
|
100
|
+
frameworkExpansionDurationMs,
|
|
101
|
+
graphBoostDurationMs,
|
|
102
|
+
rankFinalPassDurationMs,
|
|
103
|
+
contextPackDurationMs,
|
|
104
|
+
solvePacketDurationMs,
|
|
105
|
+
postProcessDurationMs
|
|
106
|
+
};
|
|
52
107
|
return result;
|
|
53
108
|
}
|
|
54
109
|
//# sourceMappingURL=runHeadless.js.map
|
|
@@ -47,7 +47,7 @@ const tokenSavings_1 = require("./tokenSavings");
|
|
|
47
47
|
const universalScan_1 = require("./universalScan");
|
|
48
48
|
async function runFlowSeeker(task, options = {}) {
|
|
49
49
|
const config = await (0, loadConfig_1.loadConfig)();
|
|
50
|
-
const profile = (0, taskUnderstanding_1.understandTask)(task, config);
|
|
50
|
+
const profile = await (0, taskUnderstanding_1.understandTask)(task, config, process.cwd());
|
|
51
51
|
// Collect editor context for ranking bias (only in VS Code; null in headless).
|
|
52
52
|
const editorContext = options.editorContext ?? collectEditorContext();
|
|
53
53
|
options.progress?.report({ message: `Scanning workspace for "${task.slice(0, 60)}..."` });
|
|
@@ -85,7 +85,7 @@ async function runFlowSeeker(task, options = {}) {
|
|
|
85
85
|
capped: scan.stats.rawEvidenceCount > finalUnits.length
|
|
86
86
|
}
|
|
87
87
|
};
|
|
88
|
-
result.solvePacket = await (0, solvePacket_1.buildSolvePacket)(result, config);
|
|
88
|
+
result.solvePacket = await (0, solvePacket_1.buildSolvePacket)(result, config, process.cwd());
|
|
89
89
|
result.contextCoverage = (0, contextBlueprint_1.computeContextCoverage)(profile, finalUnits);
|
|
90
90
|
result.tokenSavings = (0, tokenSavings_1.computeTokenSavings)(result);
|
|
91
91
|
return result;
|