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.
@@ -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: await toPacketFiles(primaryEditCandidates.slice(0, Math.min(maxPrimaryEditFiles, config.pipeline.maxCandidates)), snippetBudget, "primary", result),
82
- readOnlyContext: await toPacketFiles(readOnlyContext.slice(0, Math.min(maxReadOnlyFiles, config.pipeline.maxCandidates)), snippetBudget, "context", result),
83
- verificationTargets: await toPacketFiles(verificationTargets.slice(0, Math.min(maxVerificationFiles, config.pipeline.maxCandidates)), snippetBudget, "verification", result),
84
- noiseRisk: await toPacketFiles(noiseRisk.slice(0, Math.min(maxNoiseRiskFiles, config.pipeline.maxCandidates)), snippetBudget, "noise", result),
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-vector-v2.svg"));
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.8",
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-outline.svg"
238
+ "icon": "resources/flowseeker-sidebar.png"
239
239
  }
240
240
  ]
241
241
  },