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.
Files changed (50) hide show
  1. package/.env.example +7 -0
  2. package/CHANGELOG.md +143 -108
  3. package/README.md +288 -221
  4. package/dist/chat/nativeChatParticipant.js +1 -1
  5. package/dist/cli/flowCommand.js +175 -0
  6. package/dist/cli/main.js +1810 -0
  7. package/dist/cli/mcpServer.js +7 -1
  8. package/dist/cli/runEvaluation.js +281 -2
  9. package/dist/config/defaultConfig.js +11 -1
  10. package/dist/config/env.js +118 -0
  11. package/dist/config/loadConfig.js +18 -1
  12. package/dist/config/loadConfigFromPath.js +3 -1
  13. package/dist/eval/accuracyV2.js +483 -0
  14. package/dist/eval/goldenTask.js +192 -0
  15. package/dist/extension.js +23 -0
  16. package/dist/framework/laravel.js +177 -0
  17. package/dist/gateway/embeddingProviders.js +852 -0
  18. package/dist/index/cacheStore.js +43 -0
  19. package/dist/index/configRouteDiscoveryProbe.js +288 -0
  20. package/dist/index/embeddingIndex.js +193 -0
  21. package/dist/index/graphIndex.js +460 -0
  22. package/dist/index/indexWatcher.js +86 -0
  23. package/dist/index/semanticChunkIndex.js +388 -0
  24. package/dist/index/structuredExtractor.js +303 -12
  25. package/dist/index/treeSitterExtractor.js +264 -0
  26. package/dist/index/vectorStore.js +41 -0
  27. package/dist/index/workspaceIndex.js +901 -26
  28. package/dist/mcp/mcpTools.js +1678 -2
  29. package/dist/pipeline/contextBlueprint.js +15 -2
  30. package/dist/pipeline/contextPack.js +3 -3
  31. package/dist/pipeline/deterministicReranker.js +358 -0
  32. package/dist/pipeline/evaluationMetrics.js +14 -2
  33. package/dist/pipeline/fileGroups.js +7 -1
  34. package/dist/pipeline/fileScanner.js +209 -12
  35. package/dist/pipeline/fusionTrace.js +149 -0
  36. package/dist/pipeline/llmReranker.js +151 -0
  37. package/dist/pipeline/nodeScan.js +102 -12
  38. package/dist/pipeline/ranker.js +875 -16
  39. package/dist/pipeline/retrievalFusion.js +41 -0
  40. package/dist/pipeline/roleRefinement.js +62 -0
  41. package/dist/pipeline/runHeadless.js +60 -5
  42. package/dist/pipeline/runPipeline.js +2 -2
  43. package/dist/pipeline/solvePacket.js +656 -43
  44. package/dist/pipeline/subsystem.js +21 -0
  45. package/dist/pipeline/taskUnderstanding.js +2 -2
  46. package/dist/ui/chatViewProvider.js +1 -1
  47. package/docs/demo-screenshot-checklist.md +86 -0
  48. package/docs/marketplace-copy.md +92 -0
  49. package/docs/mcp-onboarding.md +191 -0
  50. 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 profile = (0, taskUnderstanding_1.understandTask)(task, config);
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 contextPack = (0, contextPack_1.createContextPack)(task, finalUnits, config);
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: finalUnits,
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
- result.solvePacket = await (0, solvePacket_1.buildSolvePacket)(result, config);
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;