flowseeker 0.1.7
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 +111 -0
- package/COMPATIBILITY.md +281 -0
- package/LICENSE +21 -0
- package/README.md +375 -0
- package/dist/adapters/frameworkEdges.js +586 -0
- package/dist/agent/approvalPolicy.js +81 -0
- package/dist/agent/commandRunner.js +166 -0
- package/dist/agent/flowCommandRunner.js +124 -0
- package/dist/agent/mcpToolRunner.js +167 -0
- package/dist/auth/githubAuth.js +71 -0
- package/dist/auth/modelList.js +127 -0
- package/dist/auth/oauthHandler.js +377 -0
- package/dist/chat/nativeChatParticipant.js +616 -0
- package/dist/cli/mcpServer.js +383 -0
- package/dist/cli/runEvaluation.js +789 -0
- package/dist/cli/runReplay.js +481 -0
- package/dist/commands/checkHostCompatibility.js +149 -0
- package/dist/commands/copyAgentPrompt.js +52 -0
- package/dist/commands/copyContext.js +54 -0
- package/dist/commands/explainSelectionRelevance.js +57 -0
- package/dist/commands/findRelevantContext.js +127 -0
- package/dist/commands/openEvidence.js +49 -0
- package/dist/commands/openMcpConfig.js +81 -0
- package/dist/commands/openRelatedTests.js +54 -0
- package/dist/commands/rebuildIndex.js +45 -0
- package/dist/commands/runEvaluationSuite.js +323 -0
- package/dist/commands/runReplaySuite.js +228 -0
- package/dist/config/defaultConfig.js +72 -0
- package/dist/config/loadConfig.js +84 -0
- package/dist/config/loadConfigFromPath.js +60 -0
- package/dist/extension.js +513 -0
- package/dist/gateway/agentPrompts.js +176 -0
- package/dist/gateway/aiGateway.js +1255 -0
- package/dist/gateway/aiProviders.js +901 -0
- package/dist/gateway/contextExpansion.js +331 -0
- package/dist/gateway/editProposalStore.js +238 -0
- package/dist/gateway/planProposalStore.js +28 -0
- package/dist/index/cacheStore.js +51 -0
- package/dist/index/chunker.js +45 -0
- package/dist/index/dependencyExtractor.js +107 -0
- package/dist/index/fileDiscovery.js +177 -0
- package/dist/index/structuredExtractor.js +256 -0
- package/dist/index/workspaceIndex.js +518 -0
- package/dist/mcp/mcpConfig.js +154 -0
- package/dist/mcp/mcpProvider.js +109 -0
- package/dist/mcp/mcpTools.js +215 -0
- package/dist/pipeline/agentPrompt.js +79 -0
- package/dist/pipeline/agentPromptHeadless.js +85 -0
- package/dist/pipeline/contextBlueprint.js +346 -0
- package/dist/pipeline/contextPack.js +80 -0
- package/dist/pipeline/diffPreview.js +79 -0
- package/dist/pipeline/evaluationMetrics.js +154 -0
- package/dist/pipeline/evidenceGraph.js +389 -0
- package/dist/pipeline/feedback.js +215 -0
- package/dist/pipeline/fileGroups.js +84 -0
- package/dist/pipeline/fileScanner.js +866 -0
- package/dist/pipeline/nodeScan.js +219 -0
- package/dist/pipeline/ranker.js +563 -0
- package/dist/pipeline/responseLanguage.js +39 -0
- package/dist/pipeline/retrievalPlan.js +163 -0
- package/dist/pipeline/runHeadless.js +54 -0
- package/dist/pipeline/runPipeline.js +114 -0
- package/dist/pipeline/solvePacket.js +382 -0
- package/dist/pipeline/subsystem.js +257 -0
- package/dist/pipeline/taskUnderstanding.js +453 -0
- package/dist/pipeline/tokenSavings.js +146 -0
- package/dist/pipeline/universalScan.js +216 -0
- package/dist/pipeline/verifyAfterApply.js +233 -0
- package/dist/runtime/capabilities.js +71 -0
- package/dist/runtime/hostDetect.js +98 -0
- package/dist/runtime/hostTier.js +68 -0
- package/dist/runtime/statusBar.js +80 -0
- package/dist/skills/skillRegistry.js +208 -0
- package/dist/types.js +3 -0
- package/dist/ui/chatViewProvider.js +3899 -0
- package/dist/ui/resultTreeProvider.js +174 -0
- package/dist/usage/quotaTracker.js +358 -0
- package/dist/utils/async.js +30 -0
- package/dist/utils/logger.js +64 -0
- package/dist/utils/text.js +364 -0
- package/dist/utils/updateChecker.js +140 -0
- package/package.json +561 -0
|
@@ -0,0 +1,389 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.boostWithGraph = boostWithGraph;
|
|
37
|
+
exports.buildEvidenceGraph = buildEvidenceGraph;
|
|
38
|
+
exports.extractFrameworkEdges = extractFrameworkEdges;
|
|
39
|
+
const fs = __importStar(require("fs/promises"));
|
|
40
|
+
const path = __importStar(require("path"));
|
|
41
|
+
const dependencyExtractor_1 = require("../index/dependencyExtractor");
|
|
42
|
+
const frameworkEdges_1 = require("../adapters/frameworkEdges");
|
|
43
|
+
const contextBlueprint_1 = require("./contextBlueprint");
|
|
44
|
+
const subsystem_1 = require("./subsystem");
|
|
45
|
+
function boostWithGraph(units, graph) {
|
|
46
|
+
return units.map((unit) => {
|
|
47
|
+
const edges = graph.edges.filter((edge) => edge.from === unit.id || edge.to === unit.id);
|
|
48
|
+
const edgeCount = edges.length;
|
|
49
|
+
if (edgeCount === 0) {
|
|
50
|
+
return unit;
|
|
51
|
+
}
|
|
52
|
+
const dependencyEdges = edges.filter((edge) => edge.type === "dependency_relation").length;
|
|
53
|
+
const frameworkEdges = edges.filter((edge) => edge.type.startsWith("framework_")).length;
|
|
54
|
+
const testEdges = edges.filter((edge) => edge.type === "test_relation").length;
|
|
55
|
+
const boost = Math.min(5, edgeCount) + Math.min(6, dependencyEdges * 2) + Math.min(6, frameworkEdges * 2) + Math.min(8, testEdges * 3);
|
|
56
|
+
return {
|
|
57
|
+
...unit,
|
|
58
|
+
score: unit.score + boost,
|
|
59
|
+
reasons: [...unit.reasons, `${edgeCount} evidence graph edge(s)`, dependencyEdges > 0 ? `${dependencyEdges} dependency edge(s)` : "", frameworkEdges > 0 ? `${frameworkEdges} framework edge(s)` : "", testEdges > 0 ? `${testEdges} test relation(s)` : ""].filter(Boolean)
|
|
60
|
+
};
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
function buildEvidenceGraph(units) {
|
|
64
|
+
const edges = [];
|
|
65
|
+
for (let leftIndex = 0; leftIndex < units.length; leftIndex += 1) {
|
|
66
|
+
for (let rightIndex = leftIndex + 1; rightIndex < units.length; rightIndex += 1) {
|
|
67
|
+
const left = units[leftIndex];
|
|
68
|
+
const right = units[rightIndex];
|
|
69
|
+
if (left.relativePath === right.relativePath) {
|
|
70
|
+
edges.push({ from: left.id, to: right.id, type: "same_file", weight: 2 });
|
|
71
|
+
}
|
|
72
|
+
if (importsFile(left, right) || importsFile(right, left)) {
|
|
73
|
+
edges.push({ from: left.id, to: right.id, type: "dependency_relation", weight: 4 });
|
|
74
|
+
}
|
|
75
|
+
const sharedTerms = left.matchedTerms.filter((term) => right.matchedTerms.includes(term));
|
|
76
|
+
if (sharedTerms.length > 0) {
|
|
77
|
+
edges.push({ from: left.id, to: right.id, type: "same_concept", weight: Math.min(5, sharedTerms.length) });
|
|
78
|
+
}
|
|
79
|
+
if ((left.kind === "test" && right.kind !== "test") || (right.kind === "test" && left.kind !== "test")) {
|
|
80
|
+
const sharedPathStem = stem(left.relativePath) === stem(right.relativePath);
|
|
81
|
+
if (sharedPathStem || sharedTerms.length > 0) {
|
|
82
|
+
edges.push({ from: left.id, to: right.id, type: "test_relation", weight: 5 });
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
if ((left.kind === "config" && right.kind !== "config") || (right.kind === "config" && left.kind !== "config")) {
|
|
86
|
+
if (sharedTerms.length > 0) {
|
|
87
|
+
edges.push({ from: left.id, to: right.id, type: "config_relation", weight: 3 });
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return { nodes: units, edges };
|
|
93
|
+
}
|
|
94
|
+
function stem(filePath) {
|
|
95
|
+
const parts = filePath.toLowerCase().split(/[\\/]/);
|
|
96
|
+
const name = parts.at(-1) ?? filePath;
|
|
97
|
+
return name.replace(/\.(test|spec)\./, ".").replace(/\.[^.]+$/, "");
|
|
98
|
+
}
|
|
99
|
+
function deduplicateNewUnits(units) {
|
|
100
|
+
const byId = new Map();
|
|
101
|
+
for (const unit of units) {
|
|
102
|
+
const existing = byId.get(unit.id);
|
|
103
|
+
if (!existing || unit.score > existing.score) {
|
|
104
|
+
byId.set(unit.id, unit);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return Array.from(byId.values());
|
|
108
|
+
}
|
|
109
|
+
function importsFile(source, target) {
|
|
110
|
+
return source.imports.some((importSource) => (0, dependencyExtractor_1.fileMatchesImport)(target.relativePath, importSource));
|
|
111
|
+
}
|
|
112
|
+
async function extractFrameworkEdges(units, onProgress) {
|
|
113
|
+
const startedAt = Date.now();
|
|
114
|
+
const maxFrameworkMs = 8000;
|
|
115
|
+
const timedOut = () => Date.now() - startedAt > maxFrameworkMs;
|
|
116
|
+
const fileMap = new Map();
|
|
117
|
+
for (const unit of units) {
|
|
118
|
+
if (timedOut()) {
|
|
119
|
+
return { edges: [], newUnits: [] };
|
|
120
|
+
}
|
|
121
|
+
const list = fileMap.get(unit.file) ?? [];
|
|
122
|
+
list.push(unit);
|
|
123
|
+
fileMap.set(unit.file, list);
|
|
124
|
+
}
|
|
125
|
+
const frameworkFiles = [];
|
|
126
|
+
const frameworkExtensions = new Set([".php", ".py", ".ts", ".tsx", ".js", ".jsx"]);
|
|
127
|
+
const hasFrameworkFiles = Array.from(fileMap.values()).some((fileUnits) => {
|
|
128
|
+
const ext = fileUnits[0].relativePath.slice(fileUnits[0].relativePath.lastIndexOf(".")).toLowerCase();
|
|
129
|
+
return frameworkExtensions.has(ext);
|
|
130
|
+
});
|
|
131
|
+
if (!hasFrameworkFiles) {
|
|
132
|
+
return { edges: [], newUnits: [] };
|
|
133
|
+
}
|
|
134
|
+
for (const [absolutePath, fileUnits] of fileMap) {
|
|
135
|
+
if (timedOut()) {
|
|
136
|
+
return { edges: [], newUnits: [] };
|
|
137
|
+
}
|
|
138
|
+
const relativePath = fileUnits[0].relativePath;
|
|
139
|
+
let content;
|
|
140
|
+
try {
|
|
141
|
+
const stat = await fs.stat(absolutePath);
|
|
142
|
+
if (stat.size > 500 * 1024) {
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
content = await fs.readFile(absolutePath, "utf8");
|
|
146
|
+
}
|
|
147
|
+
catch {
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
if (content.includes("\0")) {
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
const head = content.slice(0, 80000);
|
|
154
|
+
const framework = (0, frameworkEdges_1.detectFramework)(relativePath, head);
|
|
155
|
+
if (!framework) {
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
const extractor = (0, frameworkEdges_1.getFrameworkExtractor)(framework);
|
|
159
|
+
if (!extractor) {
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
162
|
+
frameworkFiles.push({
|
|
163
|
+
absolutePath,
|
|
164
|
+
relativePath,
|
|
165
|
+
content: head,
|
|
166
|
+
unitIds: fileUnits.map((unit) => unit.id)
|
|
167
|
+
});
|
|
168
|
+
if (frameworkFiles.length % 10 === 0) {
|
|
169
|
+
onProgress?.(`Analyzing ${framework} patterns: ${frameworkFiles.length} files...`);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
const workspaceFiles = new Set(frameworkFiles.map((file) => file.relativePath));
|
|
173
|
+
// Also read files referenced by framework patterns that have no evidence yet.
|
|
174
|
+
const referencedPaths = [];
|
|
175
|
+
for (const file of frameworkFiles) {
|
|
176
|
+
if (timedOut()) {
|
|
177
|
+
return { edges: [], newUnits: [] };
|
|
178
|
+
}
|
|
179
|
+
const framework = (0, frameworkEdges_1.detectFramework)(file.relativePath, file.content);
|
|
180
|
+
if (framework !== "laravel") {
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
const refs = resolveReferencedLaravelFiles(file.content, file.relativePath);
|
|
184
|
+
for (const ref of refs) {
|
|
185
|
+
if (!workspaceFiles.has(ref) && !referencedPaths.includes(ref)) {
|
|
186
|
+
referencedPaths.push(ref);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
if (frameworkFiles.length === 0) {
|
|
191
|
+
return { edges: [], newUnits: [] };
|
|
192
|
+
}
|
|
193
|
+
const firstAbsPath = frameworkFiles[0].absolutePath.replace(/\\/g, "/");
|
|
194
|
+
const firstRelPath = frameworkFiles[0].relativePath;
|
|
195
|
+
const rootPath = firstAbsPath.slice(0, firstAbsPath.lastIndexOf(firstRelPath));
|
|
196
|
+
for (const relativePath of referencedPaths.slice(0, 10)) {
|
|
197
|
+
if (timedOut()) {
|
|
198
|
+
break;
|
|
199
|
+
}
|
|
200
|
+
const absolutePath = path.resolve(rootPath, relativePath);
|
|
201
|
+
try {
|
|
202
|
+
const stat = await fs.stat(absolutePath);
|
|
203
|
+
if (stat.size > 500 * 1024) {
|
|
204
|
+
continue;
|
|
205
|
+
}
|
|
206
|
+
const content = await fs.readFile(absolutePath, "utf8");
|
|
207
|
+
frameworkFiles.push({
|
|
208
|
+
absolutePath,
|
|
209
|
+
relativePath,
|
|
210
|
+
content: content.slice(0, 80000),
|
|
211
|
+
unitIds: []
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
catch {
|
|
215
|
+
// Referenced file does not exist.
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
if (frameworkFiles.length === 0) {
|
|
219
|
+
return { edges: [], newUnits: [] };
|
|
220
|
+
}
|
|
221
|
+
const extractor = (0, frameworkEdges_1.getFrameworkExtractor)("laravel");
|
|
222
|
+
if (!extractor) {
|
|
223
|
+
return { edges: [], newUnits: [] };
|
|
224
|
+
}
|
|
225
|
+
const allUnitIds = new Set(units.map((unit) => unit.id));
|
|
226
|
+
const frameworkEdges = extractor.extractEdges(frameworkFiles);
|
|
227
|
+
if (timedOut()) {
|
|
228
|
+
return { edges: [], newUnits: [] };
|
|
229
|
+
}
|
|
230
|
+
const newUnits = [];
|
|
231
|
+
for (const edge of frameworkEdges) {
|
|
232
|
+
if (timedOut()) {
|
|
233
|
+
break;
|
|
234
|
+
}
|
|
235
|
+
if (!allUnitIds.has(edge.to)) {
|
|
236
|
+
const targetFile = units.find((unit) => unit.id.startsWith(edge.to.split(":")[0] + ":"));
|
|
237
|
+
const sourceUnit = units.find((unit) => unit.id === edge.from);
|
|
238
|
+
if (!targetFile && sourceUnit) {
|
|
239
|
+
const targetRelativePath = edge.to.split(":").slice(0, -2).join(":");
|
|
240
|
+
newUnits.push(createFrameworkDiscoveredUnit(targetRelativePath, edge, sourceUnit));
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
if (!allUnitIds.has(edge.from)) {
|
|
244
|
+
const sourceUnit = units.find((unit) => unit.id === edge.to);
|
|
245
|
+
if (sourceUnit) {
|
|
246
|
+
const sourceRelativePath = edge.from.split(":").slice(0, -2).join(":");
|
|
247
|
+
newUnits.push(createFrameworkDiscoveredUnit(sourceRelativePath, edge, sourceUnit));
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
return { edges: frameworkEdges, newUnits: deduplicateNewUnits(newUnits) };
|
|
252
|
+
}
|
|
253
|
+
function createFrameworkDiscoveredUnit(relativePath, edge, source) {
|
|
254
|
+
const role = findConnectedRole(edge.type);
|
|
255
|
+
const kind = "code_region";
|
|
256
|
+
const subsystemTerms = (0, subsystem_1.inferSubsystemTerms)(relativePath);
|
|
257
|
+
return {
|
|
258
|
+
id: `${relativePath}:1:1:code_region:fw`,
|
|
259
|
+
kind,
|
|
260
|
+
role,
|
|
261
|
+
file: source.file.replace(source.relativePath, relativePath),
|
|
262
|
+
relativePath,
|
|
263
|
+
range: { startLine: 1, endLine: 1 },
|
|
264
|
+
slot: (0, contextBlueprint_1.detectContextSlot)(relativePath, kind, role),
|
|
265
|
+
score: edge.weight * 12 + 6,
|
|
266
|
+
confidence: 0.40,
|
|
267
|
+
tier: "low",
|
|
268
|
+
frameworkEdgeReason: `framework ${edge.type} from ${source.relativePath}`,
|
|
269
|
+
subsystem: subsystemTerms[0],
|
|
270
|
+
subsystemTerms,
|
|
271
|
+
reasons: [`framework ${edge.type} from ${source.relativePath}`],
|
|
272
|
+
snippet: undefined,
|
|
273
|
+
matchedTerms: source.matchedTerms.slice(0, 3),
|
|
274
|
+
retrievalPasses: source.retrievalPasses,
|
|
275
|
+
imports: [source.relativePath]
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
function resolveReferencedLaravelFiles(content, relativePath) {
|
|
279
|
+
const paths = [];
|
|
280
|
+
const imports = parsePhpUseStatements(content);
|
|
281
|
+
// Extract class references from route definitions, event dispatches, DI, etc.
|
|
282
|
+
const classRefs = new Set();
|
|
283
|
+
const routePattern = /\[(\w+)::class\s*,\s*'(\w+)'\]/g;
|
|
284
|
+
let match = routePattern.exec(content);
|
|
285
|
+
while (match) {
|
|
286
|
+
classRefs.add(match[1]);
|
|
287
|
+
match = routePattern.exec(content);
|
|
288
|
+
}
|
|
289
|
+
const eventPattern = /event\s*\(\s*new\s+(\w+)\s*\(/g;
|
|
290
|
+
match = eventPattern.exec(content);
|
|
291
|
+
while (match) {
|
|
292
|
+
classRefs.add(match[1]);
|
|
293
|
+
match = eventPattern.exec(content);
|
|
294
|
+
}
|
|
295
|
+
const dispatchPattern = /(\w+)::dispatch\s*\(/g;
|
|
296
|
+
match = dispatchPattern.exec(content);
|
|
297
|
+
while (match) {
|
|
298
|
+
classRefs.add(match[1]);
|
|
299
|
+
match = dispatchPattern.exec(content);
|
|
300
|
+
}
|
|
301
|
+
const diPattern = /(?:private|protected|public)\s+(?:readonly\s+)?(\w+)\s+\$\w+/g;
|
|
302
|
+
match = diPattern.exec(content);
|
|
303
|
+
while (match) {
|
|
304
|
+
classRefs.add(match[1]);
|
|
305
|
+
match = diPattern.exec(content);
|
|
306
|
+
}
|
|
307
|
+
for (const className of classRefs) {
|
|
308
|
+
const fqcn = imports.get(className) ?? className;
|
|
309
|
+
const resolved = resolveLaravelPsr4Path(fqcn, className);
|
|
310
|
+
if (resolved) {
|
|
311
|
+
paths.push(resolved);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
return [...new Set(paths)];
|
|
315
|
+
}
|
|
316
|
+
function parsePhpUseStatements(content) {
|
|
317
|
+
const imports = new Map();
|
|
318
|
+
const pattern = /^use\s+([A-Za-z0-9_\\]+?)(?:\s+as\s+(\w+))?\s*;/gm;
|
|
319
|
+
let match = pattern.exec(content);
|
|
320
|
+
while (match) {
|
|
321
|
+
const fqcn = match[1];
|
|
322
|
+
const alias = match[2] ?? fqcn.split("\\").pop() ?? fqcn;
|
|
323
|
+
imports.set(alias, fqcn);
|
|
324
|
+
match = pattern.exec(content);
|
|
325
|
+
}
|
|
326
|
+
return imports;
|
|
327
|
+
}
|
|
328
|
+
function resolveLaravelPsr4Path(fqcnOrClass, shortName) {
|
|
329
|
+
const knownMappings = [
|
|
330
|
+
{ pattern: /\\Controllers\\/, path: "app/Http/Controllers" },
|
|
331
|
+
{ pattern: /\\Services\\/, path: "app/Services" },
|
|
332
|
+
{ pattern: /\\Repositories\\/, path: "app/Repositories" },
|
|
333
|
+
{ pattern: /\\Events\\/, path: "app/Events" },
|
|
334
|
+
{ pattern: /\\Listeners\\/, path: "app/Listeners" },
|
|
335
|
+
{ pattern: /\\Jobs\\/, path: "app/Jobs" },
|
|
336
|
+
{ pattern: /\\Mail\\/, path: "app/Mail" },
|
|
337
|
+
{ pattern: /\\Models\\/, path: "app/Models" },
|
|
338
|
+
{ pattern: /\\Middleware\\/, path: "app/Http/Middleware" },
|
|
339
|
+
{ pattern: /\\Policies\\/, path: "app/Policies" },
|
|
340
|
+
{ pattern: /\\Commands\\/, path: "app/Console/Commands" },
|
|
341
|
+
{ pattern: /\\Providers\\/, path: "app/Providers" },
|
|
342
|
+
{ pattern: /\\Exceptions\\/, path: "app/Exceptions" },
|
|
343
|
+
{ pattern: /\\Rules\\/, path: "app/Rules" },
|
|
344
|
+
{ pattern: /\\Requests\\/, path: "app/Http/Requests" },
|
|
345
|
+
{ pattern: /\\Resources\\/, path: "app/Http/Resources" },
|
|
346
|
+
{ pattern: /\\Notifications\\/, path: "app/Notifications" },
|
|
347
|
+
{ pattern: /\\Enums\\/, path: "app/Enums" },
|
|
348
|
+
{ pattern: /Controller$/, path: "app/Http/Controllers" },
|
|
349
|
+
{ pattern: /Service$/, path: "app/Services" },
|
|
350
|
+
{ pattern: /Event$/, path: "app/Events" },
|
|
351
|
+
{ pattern: /Listener$/, path: "app/Listeners" },
|
|
352
|
+
{ pattern: /Job$/, path: "app/Jobs" },
|
|
353
|
+
{ pattern: /Mail$/, path: "app/Mail" },
|
|
354
|
+
{ pattern: /Model$/, path: "app/Models" },
|
|
355
|
+
];
|
|
356
|
+
// Try FQCN-based resolution first
|
|
357
|
+
for (const { pattern, path: folder } of knownMappings) {
|
|
358
|
+
if (pattern.test(fqcnOrClass)) {
|
|
359
|
+
return `${folder}/${shortName}.php`;
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
// Fallback: guess from namespace segments
|
|
363
|
+
const segments = fqcnOrClass.split("\\").filter(Boolean);
|
|
364
|
+
if (segments.length >= 3 && segments[0] === "App") {
|
|
365
|
+
const category = segments[segments.length - 2];
|
|
366
|
+
const guess = `app/${category}/${shortName}.php`;
|
|
367
|
+
return guess;
|
|
368
|
+
}
|
|
369
|
+
return `app/${shortName}.php`;
|
|
370
|
+
}
|
|
371
|
+
function findConnectedRole(edgeType) {
|
|
372
|
+
switch (edgeType) {
|
|
373
|
+
case "framework_route_handler":
|
|
374
|
+
return "impact";
|
|
375
|
+
case "framework_event_dispatch":
|
|
376
|
+
return "read_context";
|
|
377
|
+
case "framework_service_dependency":
|
|
378
|
+
return "read_context";
|
|
379
|
+
case "framework_job_dispatch":
|
|
380
|
+
return "read_context";
|
|
381
|
+
case "framework_config_usage":
|
|
382
|
+
return "read_context";
|
|
383
|
+
case "framework_view_render":
|
|
384
|
+
return "impact";
|
|
385
|
+
default:
|
|
386
|
+
return "unknown";
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
//# sourceMappingURL=evidenceGraph.js.map
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.createFeedbackSnapshot = createFeedbackSnapshot;
|
|
37
|
+
exports.createFeedbackRecord = createFeedbackRecord;
|
|
38
|
+
exports.writeFeedbackRecord = writeFeedbackRecord;
|
|
39
|
+
exports.readFeedbackRecords = readFeedbackRecords;
|
|
40
|
+
exports.feedbackDirectory = feedbackDirectory;
|
|
41
|
+
exports.normalizeFeedbackKind = normalizeFeedbackKind;
|
|
42
|
+
exports.feedbackKindLabel = feedbackKindLabel;
|
|
43
|
+
const crypto = __importStar(require("crypto"));
|
|
44
|
+
const fs = __importStar(require("fs/promises"));
|
|
45
|
+
const path = __importStar(require("path"));
|
|
46
|
+
const fileGroups_1 = require("./fileGroups");
|
|
47
|
+
const canonicalFeedbackKinds = [
|
|
48
|
+
"relevant",
|
|
49
|
+
"missing_file",
|
|
50
|
+
"not_relevant",
|
|
51
|
+
"agent_needed_more_context"
|
|
52
|
+
];
|
|
53
|
+
function createFeedbackSnapshot(result, workspacePath, workspaceName) {
|
|
54
|
+
const topFiles = (0, fileGroups_1.aggregateEvidenceFiles)(result.units)
|
|
55
|
+
.slice(0, 20)
|
|
56
|
+
.map((group) => ({
|
|
57
|
+
relativePath: group.relativePath,
|
|
58
|
+
role: group.role,
|
|
59
|
+
slot: group.slot,
|
|
60
|
+
score: Number(group.score.toFixed(2)),
|
|
61
|
+
evidence: group.unitCount
|
|
62
|
+
}));
|
|
63
|
+
return {
|
|
64
|
+
schemaVersion: 1,
|
|
65
|
+
id: crypto.randomUUID(),
|
|
66
|
+
createdAt: new Date().toISOString(),
|
|
67
|
+
workspacePath,
|
|
68
|
+
workspaceName,
|
|
69
|
+
task: result.task,
|
|
70
|
+
intent: result.profile.intent,
|
|
71
|
+
keywords: result.profile.keywords.slice(0, 40),
|
|
72
|
+
strategies: result.profile.strategies,
|
|
73
|
+
requiredSlots: result.contextCoverage?.requiredSlots ?? result.profile.blueprint.requiredSlots,
|
|
74
|
+
missingRequiredSlots: result.contextCoverage?.missingRequiredSlots ?? [],
|
|
75
|
+
topFiles,
|
|
76
|
+
tokenSavings: result.tokenSavings ? summarizeTokenSavings(result.tokenSavings) : undefined,
|
|
77
|
+
stats: {
|
|
78
|
+
discoveredFiles: result.stats.discoveredFiles,
|
|
79
|
+
scannedFiles: result.stats.scannedFiles,
|
|
80
|
+
skippedFiles: result.stats.skippedFiles,
|
|
81
|
+
rawEvidenceCount: result.stats.rawEvidenceCount,
|
|
82
|
+
rankedEvidenceCount: result.stats.rankedEvidenceCount,
|
|
83
|
+
stoppedEarly: result.stats.stoppedEarly,
|
|
84
|
+
stopReason: result.stats.stopReason
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
function createFeedbackRecord(run, kind, options = { source: "manual" }) {
|
|
89
|
+
return {
|
|
90
|
+
schemaVersion: 1,
|
|
91
|
+
id: crypto.randomUUID(),
|
|
92
|
+
createdAt: new Date().toISOString(),
|
|
93
|
+
source: options.source,
|
|
94
|
+
kind,
|
|
95
|
+
note: options.note?.trim() || undefined,
|
|
96
|
+
run,
|
|
97
|
+
outcome: {
|
|
98
|
+
aiNeededMoreContext: kind === "missing_file" || kind === "agent_needed_more_context"
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
async function writeFeedbackRecord(workspacePath, record) {
|
|
103
|
+
const directoryPath = feedbackDirectory(workspacePath);
|
|
104
|
+
await fs.mkdir(directoryPath, { recursive: true });
|
|
105
|
+
const filePath = path.join(directoryPath, `${safeFilePart(record.createdAt)}-${safeFilePart(record.id)}.json`);
|
|
106
|
+
await fs.writeFile(filePath, `${JSON.stringify(record, null, 2)}\n`, "utf8");
|
|
107
|
+
return filePath;
|
|
108
|
+
}
|
|
109
|
+
async function readFeedbackRecords(workspacePath, directoryPath = feedbackDirectory(workspacePath)) {
|
|
110
|
+
const files = await collectJsonFiles(path.resolve(directoryPath)).catch(() => []);
|
|
111
|
+
const records = [];
|
|
112
|
+
for (const filePath of files) {
|
|
113
|
+
try {
|
|
114
|
+
const text = await fs.readFile(filePath, "utf8");
|
|
115
|
+
const parsed = JSON.parse(text);
|
|
116
|
+
if (isFeedbackRecord(parsed)) {
|
|
117
|
+
const kind = normalizeFeedbackKind(parsed.kind);
|
|
118
|
+
if (!kind) {
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
records.push({
|
|
122
|
+
filePath,
|
|
123
|
+
record: {
|
|
124
|
+
...parsed,
|
|
125
|
+
kind,
|
|
126
|
+
outcome: {
|
|
127
|
+
...parsed.outcome,
|
|
128
|
+
aiNeededMoreContext: parsed.outcome?.aiNeededMoreContext === true ||
|
|
129
|
+
kind === "missing_file" ||
|
|
130
|
+
kind === "agent_needed_more_context"
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
catch {
|
|
137
|
+
// Ignore malformed local feedback files so one bad note does not block replay.
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return records.sort((left, right) => left.record.createdAt.localeCompare(right.record.createdAt));
|
|
141
|
+
}
|
|
142
|
+
function feedbackDirectory(workspacePath) {
|
|
143
|
+
return path.join(workspacePath, ".flowseeker", "feedback");
|
|
144
|
+
}
|
|
145
|
+
function normalizeFeedbackKind(value) {
|
|
146
|
+
if (canonicalFeedbackKinds.includes(value)) {
|
|
147
|
+
return value;
|
|
148
|
+
}
|
|
149
|
+
switch (value) {
|
|
150
|
+
case "missing_files":
|
|
151
|
+
return "missing_file";
|
|
152
|
+
case "irrelevant":
|
|
153
|
+
return "not_relevant";
|
|
154
|
+
case "ai_needs_more":
|
|
155
|
+
return "agent_needed_more_context";
|
|
156
|
+
default:
|
|
157
|
+
return undefined;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
function feedbackKindLabel(kind) {
|
|
161
|
+
switch (kind) {
|
|
162
|
+
case "relevant":
|
|
163
|
+
return "Relevant";
|
|
164
|
+
case "missing_file":
|
|
165
|
+
return "Missing file/context";
|
|
166
|
+
case "not_relevant":
|
|
167
|
+
return "Not relevant";
|
|
168
|
+
case "agent_needed_more_context":
|
|
169
|
+
return "AI needed more context";
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
function summarizeTokenSavings(metrics) {
|
|
173
|
+
return {
|
|
174
|
+
status: metrics.status,
|
|
175
|
+
basis: metrics.basis,
|
|
176
|
+
method: metrics.method,
|
|
177
|
+
tokenizer: metrics.tokenizer,
|
|
178
|
+
workspaceTokensEstimate: metrics.workspaceTokensEstimate,
|
|
179
|
+
candidateTokensEstimate: metrics.candidateTokensEstimate,
|
|
180
|
+
solvePacketTokensEstimate: metrics.solvePacketTokensEstimate,
|
|
181
|
+
reductionPercent: metrics.reductionPercent,
|
|
182
|
+
proofStatus: metrics.proofStatus,
|
|
183
|
+
proofWarnings: metrics.proofWarnings
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
async function collectJsonFiles(directoryPath) {
|
|
187
|
+
const entries = await fs.readdir(directoryPath, { withFileTypes: true });
|
|
188
|
+
const files = [];
|
|
189
|
+
for (const entry of entries) {
|
|
190
|
+
const absolutePath = path.join(directoryPath, entry.name);
|
|
191
|
+
if (entry.isDirectory()) {
|
|
192
|
+
const childFiles = await collectJsonFiles(absolutePath);
|
|
193
|
+
files.push(...childFiles);
|
|
194
|
+
}
|
|
195
|
+
else if (entry.isFile() && entry.name.endsWith(".json")) {
|
|
196
|
+
files.push(absolutePath);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
return files;
|
|
200
|
+
}
|
|
201
|
+
function isFeedbackRecord(value) {
|
|
202
|
+
const candidate = value;
|
|
203
|
+
return Boolean(candidate &&
|
|
204
|
+
candidate.schemaVersion === 1 &&
|
|
205
|
+
typeof candidate.id === "string" &&
|
|
206
|
+
typeof candidate.createdAt === "string" &&
|
|
207
|
+
normalizeFeedbackKind(candidate.kind) &&
|
|
208
|
+
candidate.run &&
|
|
209
|
+
typeof candidate.run.task === "string" &&
|
|
210
|
+
typeof candidate.run.workspacePath === "string");
|
|
211
|
+
}
|
|
212
|
+
function safeFilePart(value) {
|
|
213
|
+
return value.replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 80) || "feedback";
|
|
214
|
+
}
|
|
215
|
+
//# sourceMappingURL=feedback.js.map
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.aggregateEvidenceFiles = aggregateEvidenceFiles;
|
|
4
|
+
exports.selectDiverseEvidenceUnits = selectDiverseEvidenceUnits;
|
|
5
|
+
const rolePriority = {
|
|
6
|
+
edit_candidate: 6,
|
|
7
|
+
test: 5,
|
|
8
|
+
read_context: 4,
|
|
9
|
+
impact: 3,
|
|
10
|
+
verify: 2,
|
|
11
|
+
unknown: 1
|
|
12
|
+
};
|
|
13
|
+
function aggregateEvidenceFiles(units) {
|
|
14
|
+
const byPath = new Map();
|
|
15
|
+
for (const unit of units) {
|
|
16
|
+
byPath.set(unit.relativePath, [...(byPath.get(unit.relativePath) ?? []), unit]);
|
|
17
|
+
}
|
|
18
|
+
return Array.from(byPath.entries())
|
|
19
|
+
.map(([relativePath, fileUnits]) => toGroup(relativePath, fileUnits))
|
|
20
|
+
.sort((left, right) => {
|
|
21
|
+
const roleDelta = rolePriority[right.role] - rolePriority[left.role];
|
|
22
|
+
if (roleDelta !== 0) {
|
|
23
|
+
return roleDelta;
|
|
24
|
+
}
|
|
25
|
+
return right.score - left.score;
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
function selectDiverseEvidenceUnits(units, maxPerFile = 2) {
|
|
29
|
+
const counts = new Map();
|
|
30
|
+
const selected = [];
|
|
31
|
+
for (const unit of units) {
|
|
32
|
+
const count = counts.get(unit.relativePath) ?? 0;
|
|
33
|
+
if (count >= maxPerFile) {
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
counts.set(unit.relativePath, count + 1);
|
|
37
|
+
selected.push(unit);
|
|
38
|
+
}
|
|
39
|
+
return selected;
|
|
40
|
+
}
|
|
41
|
+
function toGroup(relativePath, units) {
|
|
42
|
+
const sortedUnits = [...units].sort((left, right) => right.score - left.score);
|
|
43
|
+
const best = sortedUnits[0];
|
|
44
|
+
const duplicateUnitCount = units.filter((unit) => unit.isDuplicate).length;
|
|
45
|
+
const uniqueEvidenceCount = Math.max(1, units.length - duplicateUnitCount);
|
|
46
|
+
const score = best.score + Math.min(10, uniqueEvidenceCount * 1.5);
|
|
47
|
+
const confidence = Math.max(...units.map((unit) => unit.confidence));
|
|
48
|
+
const role = bestRole(units);
|
|
49
|
+
return {
|
|
50
|
+
file: best.file,
|
|
51
|
+
relativePath,
|
|
52
|
+
role,
|
|
53
|
+
slot: bestSlot(units),
|
|
54
|
+
kinds: unique(units.map((unit) => unit.kind)),
|
|
55
|
+
score,
|
|
56
|
+
confidence,
|
|
57
|
+
tier: confidence >= 0.75 ? "high" : confidence >= 0.45 ? "medium" : "low",
|
|
58
|
+
unitCount: units.length,
|
|
59
|
+
duplicateUnitCount,
|
|
60
|
+
contrastContext: units.length > 0 && units.every((unit) => unit.contrastContext),
|
|
61
|
+
subsystem: best.subsystem,
|
|
62
|
+
subsystemTerms: unique(units.flatMap((unit) => unit.subsystemTerms ?? [])).slice(0, 8),
|
|
63
|
+
symbols: unique(units.flatMap((unit) => unit.symbols ?? [])).slice(0, 12),
|
|
64
|
+
ranges: units.flatMap((unit) => (unit.range ? [unit.range] : [])),
|
|
65
|
+
reasons: unique(units.flatMap((unit) => unit.reasons)).slice(0, 8),
|
|
66
|
+
retrievalPasses: unique(units.flatMap((unit) => unit.retrievalPasses)),
|
|
67
|
+
imports: unique(units.flatMap((unit) => unit.imports)).slice(0, 12),
|
|
68
|
+
units: sortedUnits
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
function bestRole(units) {
|
|
72
|
+
return [...units].sort((left, right) => rolePriority[right.role] - rolePriority[left.role])[0].role;
|
|
73
|
+
}
|
|
74
|
+
function bestSlot(units) {
|
|
75
|
+
const counts = new Map();
|
|
76
|
+
for (const unit of units) {
|
|
77
|
+
counts.set(unit.slot ?? "unknown", (counts.get(unit.slot ?? "unknown") ?? 0) + 1);
|
|
78
|
+
}
|
|
79
|
+
return Array.from(counts.entries()).sort((left, right) => right[1] - left[1])[0]?.[0] ?? "unknown";
|
|
80
|
+
}
|
|
81
|
+
function unique(values) {
|
|
82
|
+
return Array.from(new Set(values));
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=fileGroups.js.map
|