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,586 @@
|
|
|
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.detectFramework = detectFramework;
|
|
37
|
+
exports.getFrameworkExtractor = getFrameworkExtractor;
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
const text_1 = require("../utils/text");
|
|
40
|
+
function detectFramework(relativePath, content) {
|
|
41
|
+
const ext = path.extname(relativePath).toLowerCase();
|
|
42
|
+
if (ext === ".php") {
|
|
43
|
+
return "laravel";
|
|
44
|
+
}
|
|
45
|
+
if (ext === ".py") {
|
|
46
|
+
return "django";
|
|
47
|
+
}
|
|
48
|
+
if ((ext === ".ts" || ext === ".js") && content) {
|
|
49
|
+
if (/@(?:Controller|Module|Injectable|Get|Post|Put|Delete|Patch)\s*\(/.test(content)) {
|
|
50
|
+
return "nestjs";
|
|
51
|
+
}
|
|
52
|
+
if (/\b(?:express|router\.(?:get|post|put|delete|patch)|app\.(?:get|post|put|delete|patch))\s*\(/.test(content)) {
|
|
53
|
+
return "express";
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
if (ext === ".py" && content) {
|
|
57
|
+
if (/\b(?:urlpatterns|path\s*\(|re_path\s*\()/.test(content)) {
|
|
58
|
+
return "django";
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return undefined;
|
|
62
|
+
}
|
|
63
|
+
function getFrameworkExtractor(framework) {
|
|
64
|
+
return extractors[framework];
|
|
65
|
+
}
|
|
66
|
+
const extractors = {
|
|
67
|
+
laravel: createLaravelExtractor(),
|
|
68
|
+
nestjs: createNestJsExtractor(),
|
|
69
|
+
express: createExpressExtractor(),
|
|
70
|
+
django: createDjangoExtractor()
|
|
71
|
+
};
|
|
72
|
+
function createLaravelExtractor() {
|
|
73
|
+
return {
|
|
74
|
+
name: "laravel",
|
|
75
|
+
extensions: [".php"],
|
|
76
|
+
extractEdges(files) {
|
|
77
|
+
const edges = [];
|
|
78
|
+
const classToPath = new Map();
|
|
79
|
+
const pathToUnitIds = new Map();
|
|
80
|
+
for (const file of files) {
|
|
81
|
+
pathToUnitIds.set(file.relativePath, file.unitIds);
|
|
82
|
+
const ns = extractNamespace(file.content);
|
|
83
|
+
const className = extractClassName(file.content);
|
|
84
|
+
if (ns && className) {
|
|
85
|
+
classToPath.set(`${ns}\\${className}`, file.relativePath);
|
|
86
|
+
classToPath.set(className, file.relativePath);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
for (const file of files) {
|
|
90
|
+
const fileUnitIds = file.unitIds;
|
|
91
|
+
function addEdges(targetPath, edgeType, weight) {
|
|
92
|
+
if (!targetPath || targetPath === file.relativePath) {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
const targetIds = pathToUnitIds.get(targetPath);
|
|
96
|
+
if (targetIds && targetIds.length > 0) {
|
|
97
|
+
for (const fromId of fileUnitIds) {
|
|
98
|
+
for (const toId of targetIds) {
|
|
99
|
+
edges.push({ from: fromId, to: toId, type: edgeType, weight });
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
else if (fileUnitIds.length > 0) {
|
|
104
|
+
const syntheticTargetId = `${targetPath}:1:1:code_region:fw`;
|
|
105
|
+
for (const fromId of fileUnitIds) {
|
|
106
|
+
edges.push({ from: fromId, to: syntheticTargetId, type: edgeType, weight });
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
// Route → Controller edges
|
|
111
|
+
const controllerRefs = extractRouteControllers(file.content);
|
|
112
|
+
for (const ref of controllerRefs) {
|
|
113
|
+
addEdges(resolvePhpClass(ref, classToPath, file.relativePath, ["Controller"]), "framework_route_handler", 5);
|
|
114
|
+
}
|
|
115
|
+
// Event → Listener/Event edges
|
|
116
|
+
const eventDispatches = extractEventDispatches(file.content);
|
|
117
|
+
for (const eventClass of eventDispatches) {
|
|
118
|
+
addEdges(resolvePhpClass(eventClass, classToPath, file.relativePath, ["Event"]), "framework_event_dispatch", 4);
|
|
119
|
+
}
|
|
120
|
+
// Controller → Service (DI injection)
|
|
121
|
+
const injectedServices = extractConstructorInjection(file.content);
|
|
122
|
+
for (const serviceClass of injectedServices) {
|
|
123
|
+
addEdges(resolvePhpClass(serviceClass, classToPath, file.relativePath, ["Service", "Repository"]), "framework_service_dependency", 3);
|
|
124
|
+
}
|
|
125
|
+
// Job::dispatch → Job class
|
|
126
|
+
const jobDispatches = extractJobDispatches(file.content);
|
|
127
|
+
for (const jobClass of jobDispatches) {
|
|
128
|
+
addEdges(resolvePhpClass(jobClass, classToPath, file.relativePath, ["Job"]), "framework_job_dispatch", 4);
|
|
129
|
+
}
|
|
130
|
+
// Config usage → config files
|
|
131
|
+
const configKeys = extractConfigKeys(file.content);
|
|
132
|
+
for (const key of configKeys) {
|
|
133
|
+
const configFile = resolveConfigFile(key);
|
|
134
|
+
addEdges(configFile, "framework_config_usage", 3);
|
|
135
|
+
}
|
|
136
|
+
// View rendering → template files
|
|
137
|
+
const viewRefs = extractViewRenders(file.content);
|
|
138
|
+
for (const viewName of viewRefs) {
|
|
139
|
+
const templatePath = resolveViewPath(viewName);
|
|
140
|
+
addEdges(templatePath, "framework_view_render", 5);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return edges;
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
function extractNamespace(content) {
|
|
148
|
+
const match = content.match(/namespace\s+([A-Za-z0-9_\\]+)\s*;/);
|
|
149
|
+
return match?.[1] ?? undefined;
|
|
150
|
+
}
|
|
151
|
+
function extractClassName(content) {
|
|
152
|
+
const match = content.match(/class\s+(\w+)\s*(?:extends|implements|\{|$)/m);
|
|
153
|
+
return match?.[1] ?? undefined;
|
|
154
|
+
}
|
|
155
|
+
function extractRouteControllers(content) {
|
|
156
|
+
const refs = [];
|
|
157
|
+
const pattern = /\[(\w+)::class\s*,\s*'(\w+)'\]/g;
|
|
158
|
+
let match = pattern.exec(content);
|
|
159
|
+
while (match) {
|
|
160
|
+
refs.push(match[1]);
|
|
161
|
+
match = pattern.exec(content);
|
|
162
|
+
}
|
|
163
|
+
return refs;
|
|
164
|
+
}
|
|
165
|
+
function extractEventDispatches(content) {
|
|
166
|
+
const refs = [];
|
|
167
|
+
const pattern = /event\s*\(\s*new\s+(\w+)\s*\(/g;
|
|
168
|
+
let match = pattern.exec(content);
|
|
169
|
+
while (match) {
|
|
170
|
+
refs.push(match[1]);
|
|
171
|
+
match = pattern.exec(content);
|
|
172
|
+
}
|
|
173
|
+
return refs;
|
|
174
|
+
}
|
|
175
|
+
function extractConstructorInjection(content) {
|
|
176
|
+
const refs = [];
|
|
177
|
+
const pattern = /(?:private|protected|public)\s+(?:readonly\s+)?(\w+)\s+\$\w+/g;
|
|
178
|
+
let match = pattern.exec(content);
|
|
179
|
+
while (match) {
|
|
180
|
+
refs.push(match[1]);
|
|
181
|
+
match = pattern.exec(content);
|
|
182
|
+
}
|
|
183
|
+
return refs;
|
|
184
|
+
}
|
|
185
|
+
function extractJobDispatches(content) {
|
|
186
|
+
const refs = [];
|
|
187
|
+
const pattern = /(\w+)::dispatch\s*\(/g;
|
|
188
|
+
let match = pattern.exec(content);
|
|
189
|
+
while (match) {
|
|
190
|
+
refs.push(match[1]);
|
|
191
|
+
match = pattern.exec(content);
|
|
192
|
+
}
|
|
193
|
+
return refs;
|
|
194
|
+
}
|
|
195
|
+
function extractConfigKeys(content) {
|
|
196
|
+
const refs = [];
|
|
197
|
+
const pattern = /config\s*\(\s*['"]([^'"]+)['"]/g;
|
|
198
|
+
let match = pattern.exec(content);
|
|
199
|
+
while (match) {
|
|
200
|
+
refs.push(match[1]);
|
|
201
|
+
match = pattern.exec(content);
|
|
202
|
+
}
|
|
203
|
+
return refs;
|
|
204
|
+
}
|
|
205
|
+
function extractViewRenders(content) {
|
|
206
|
+
const refs = [];
|
|
207
|
+
const viewFuncPattern = /view\s*\(\s*['"]([^'"]+)['"]/g;
|
|
208
|
+
let match = viewFuncPattern.exec(content);
|
|
209
|
+
while (match) {
|
|
210
|
+
refs.push(match[1]);
|
|
211
|
+
match = viewFuncPattern.exec(content);
|
|
212
|
+
}
|
|
213
|
+
const viewPropPattern = /view:\s*['"]([^'"]+)['"]/g;
|
|
214
|
+
match = viewPropPattern.exec(content);
|
|
215
|
+
while (match) {
|
|
216
|
+
refs.push(match[1]);
|
|
217
|
+
match = viewPropPattern.exec(content);
|
|
218
|
+
}
|
|
219
|
+
return refs;
|
|
220
|
+
}
|
|
221
|
+
function resolveViewPath(viewName) {
|
|
222
|
+
const parts = viewName.replace(/\./g, "/");
|
|
223
|
+
return `resources/views/${parts}.blade.php`;
|
|
224
|
+
}
|
|
225
|
+
function resolvePhpClass(className, classToPath, sourcePath, suffixHints) {
|
|
226
|
+
const direct = classToPath.get(className);
|
|
227
|
+
if (direct) {
|
|
228
|
+
return direct;
|
|
229
|
+
}
|
|
230
|
+
for (const [fqcn, filePath] of classToPath) {
|
|
231
|
+
const base = fqcn.split("\\").pop() ?? "";
|
|
232
|
+
const normalized = (0, text_1.normalizeText)(base);
|
|
233
|
+
const target = (0, text_1.normalizeText)(className);
|
|
234
|
+
if (normalized === target && filePath !== sourcePath) {
|
|
235
|
+
return filePath;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
return undefined;
|
|
239
|
+
}
|
|
240
|
+
function resolveConfigFile(configKey) {
|
|
241
|
+
const parts = configKey.split(".");
|
|
242
|
+
const firstPart = parts[0];
|
|
243
|
+
const configFiles = ["mail", "app", "database", "cache", "queue", "services", "auth", "session", "logging", "filesystems"];
|
|
244
|
+
if (configFiles.includes(firstPart)) {
|
|
245
|
+
return `config/${firstPart}.php`;
|
|
246
|
+
}
|
|
247
|
+
return "config/app.php";
|
|
248
|
+
}
|
|
249
|
+
function createNestJsExtractor() {
|
|
250
|
+
return {
|
|
251
|
+
name: "nestjs",
|
|
252
|
+
extensions: [".ts", ".js"],
|
|
253
|
+
extractEdges(files) {
|
|
254
|
+
const edges = [];
|
|
255
|
+
const classToPath = new Map();
|
|
256
|
+
const pathToUnitIds = new Map();
|
|
257
|
+
for (const file of files) {
|
|
258
|
+
pathToUnitIds.set(file.relativePath, file.unitIds);
|
|
259
|
+
const className = extractTsClassName(file.content);
|
|
260
|
+
if (className) {
|
|
261
|
+
classToPath.set(className, file.relativePath);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
for (const file of files) {
|
|
265
|
+
const fileUnitIds = file.unitIds;
|
|
266
|
+
function addEdges(targetPath, edgeType, weight) {
|
|
267
|
+
if (!targetPath || targetPath === file.relativePath) {
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
const targetIds = pathToUnitIds.get(targetPath);
|
|
271
|
+
if (targetIds && targetIds.length > 0) {
|
|
272
|
+
for (const fromId of fileUnitIds) {
|
|
273
|
+
for (const toId of targetIds) {
|
|
274
|
+
edges.push({ from: fromId, to: toId, type: edgeType, weight });
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
else if (fileUnitIds.length > 0) {
|
|
279
|
+
const syntheticTargetId = `${targetPath}:1:1:code_region:fw`;
|
|
280
|
+
for (const fromId of fileUnitIds) {
|
|
281
|
+
edges.push({ from: fromId, to: syntheticTargetId, type: edgeType, weight });
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
// NestJS Controller → Service (constructor DI)
|
|
286
|
+
const diTypes = extractNestDiTypes(file.content);
|
|
287
|
+
for (const typeName of diTypes) {
|
|
288
|
+
const targetPath = classToPath.get(typeName) ?? guessTsFilePath(typeName, file.relativePath);
|
|
289
|
+
addEdges(targetPath, "framework_service_dependency", 3);
|
|
290
|
+
}
|
|
291
|
+
// NestJS Module → Controller/Provider references
|
|
292
|
+
const moduleRefs = extractNestModuleRefs(file.content);
|
|
293
|
+
for (const refName of moduleRefs) {
|
|
294
|
+
const targetPath = classToPath.get(refName) ?? guessTsFilePath(refName, file.relativePath);
|
|
295
|
+
addEdges(targetPath, "framework_route_handler", 4);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
return edges;
|
|
299
|
+
}
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
function createExpressExtractor() {
|
|
303
|
+
return {
|
|
304
|
+
name: "express",
|
|
305
|
+
extensions: [".ts", ".js"],
|
|
306
|
+
extractEdges(files) {
|
|
307
|
+
const edges = [];
|
|
308
|
+
const pathToUnitIds = new Map();
|
|
309
|
+
for (const file of files) {
|
|
310
|
+
pathToUnitIds.set(file.relativePath, file.unitIds);
|
|
311
|
+
}
|
|
312
|
+
for (const file of files) {
|
|
313
|
+
const fileUnitIds = file.unitIds;
|
|
314
|
+
function addEdges(targetPath, edgeType, weight) {
|
|
315
|
+
if (!targetPath || targetPath === file.relativePath) {
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
const targetIds = pathToUnitIds.get(targetPath);
|
|
319
|
+
if (targetIds && targetIds.length > 0) {
|
|
320
|
+
for (const fromId of fileUnitIds) {
|
|
321
|
+
for (const toId of targetIds) {
|
|
322
|
+
edges.push({ from: fromId, to: toId, type: edgeType, weight });
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
else if (fileUnitIds.length > 0) {
|
|
327
|
+
const syntheticTargetId = `${targetPath}:1:1:code_region:fw`;
|
|
328
|
+
for (const fromId of fileUnitIds) {
|
|
329
|
+
edges.push({ from: fromId, to: syntheticTargetId, type: edgeType, weight });
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
// Express route handlers: router.get('/path', handlerFn)
|
|
334
|
+
// The handler is often imported from a controller file
|
|
335
|
+
const routeRequires = extractExpressRequires(file.content);
|
|
336
|
+
for (const requirePath of routeRequires) {
|
|
337
|
+
const resolved = resolveRequirePath(requirePath, file.relativePath);
|
|
338
|
+
addEdges(resolved, "framework_route_handler", 4);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
return edges;
|
|
342
|
+
}
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
function extractTsClassName(content) {
|
|
346
|
+
const match = content.match(/(?:export\s+)?class\s+(\w+)/);
|
|
347
|
+
return match?.[1] ?? undefined;
|
|
348
|
+
}
|
|
349
|
+
function extractNestDiTypes(content) {
|
|
350
|
+
const types = [];
|
|
351
|
+
const pattern = /constructor\s*\([\s\S]*?\)\s*[{:]/g;
|
|
352
|
+
let match = pattern.exec(content);
|
|
353
|
+
while (match) {
|
|
354
|
+
const params = match[0];
|
|
355
|
+
const paramPattern = /(?:private|protected|public)\s+(?:readonly\s+)?(\w+)\s*:/g;
|
|
356
|
+
let paramMatch = paramPattern.exec(params);
|
|
357
|
+
while (paramMatch) {
|
|
358
|
+
types.push(paramMatch[1]);
|
|
359
|
+
paramMatch = paramPattern.exec(params);
|
|
360
|
+
}
|
|
361
|
+
match = pattern.exec(content);
|
|
362
|
+
}
|
|
363
|
+
return types;
|
|
364
|
+
}
|
|
365
|
+
function extractNestModuleRefs(content) {
|
|
366
|
+
const refs = [];
|
|
367
|
+
const controllersMatch = content.match(/controllers\s*:\s*\[([^\]]+)\]/);
|
|
368
|
+
if (controllersMatch) {
|
|
369
|
+
const names = controllersMatch[1].match(/(\w+)/g) ?? [];
|
|
370
|
+
refs.push(...names);
|
|
371
|
+
}
|
|
372
|
+
const providersMatch = content.match(/providers\s*:\s*\[([^\]]+)\]/);
|
|
373
|
+
if (providersMatch) {
|
|
374
|
+
const names = providersMatch[1].match(/(\w+)/g) ?? [];
|
|
375
|
+
refs.push(...names);
|
|
376
|
+
}
|
|
377
|
+
return refs;
|
|
378
|
+
}
|
|
379
|
+
function extractExpressRequires(content) {
|
|
380
|
+
const refs = [];
|
|
381
|
+
const pattern = /require\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
|
|
382
|
+
let match = pattern.exec(content);
|
|
383
|
+
while (match) {
|
|
384
|
+
refs.push(match[1]);
|
|
385
|
+
match = pattern.exec(content);
|
|
386
|
+
}
|
|
387
|
+
const importPattern = /\bfrom\s+['"]([^'"]+)['"]/g;
|
|
388
|
+
match = importPattern.exec(content);
|
|
389
|
+
while (match) {
|
|
390
|
+
refs.push(match[1]);
|
|
391
|
+
match = importPattern.exec(content);
|
|
392
|
+
}
|
|
393
|
+
return refs;
|
|
394
|
+
}
|
|
395
|
+
function guessTsFilePath(className, sourcePath) {
|
|
396
|
+
const sourceDir = sourcePath.replace(/\\/g, "/").split("/").slice(0, -1).join("/");
|
|
397
|
+
if (className.endsWith("Service")) {
|
|
398
|
+
return `${sourceDir}/${className}.ts`;
|
|
399
|
+
}
|
|
400
|
+
if (className.endsWith("Controller")) {
|
|
401
|
+
return `${sourceDir}/${className}.ts`;
|
|
402
|
+
}
|
|
403
|
+
return undefined;
|
|
404
|
+
}
|
|
405
|
+
function resolveRequirePath(requirePath, sourcePath) {
|
|
406
|
+
const sourceDir = sourcePath.replace(/\\/g, "/").split("/").slice(0, -1).join("/");
|
|
407
|
+
const resolved = requirePath.replace(/^\.\//, `${sourceDir}/`).replace(/^\.\.\//, `${sourceDir}/../`);
|
|
408
|
+
if (!resolved.includes("/")) {
|
|
409
|
+
return `src/${resolved}.ts`;
|
|
410
|
+
}
|
|
411
|
+
if (!resolved.endsWith(".ts") && !resolved.endsWith(".js")) {
|
|
412
|
+
return `${resolved}.ts`;
|
|
413
|
+
}
|
|
414
|
+
return resolved;
|
|
415
|
+
}
|
|
416
|
+
function createDjangoExtractor() {
|
|
417
|
+
return {
|
|
418
|
+
name: "django",
|
|
419
|
+
extensions: [".py"],
|
|
420
|
+
extractEdges(files) {
|
|
421
|
+
const edges = [];
|
|
422
|
+
const pathToUnitIds = new Map();
|
|
423
|
+
const viewToPath = new Map();
|
|
424
|
+
const modelToPath = new Map();
|
|
425
|
+
for (const file of files) {
|
|
426
|
+
pathToUnitIds.set(file.relativePath, file.unitIds);
|
|
427
|
+
const models = extractDjangoModelNames(file.content);
|
|
428
|
+
for (const model of models) {
|
|
429
|
+
modelToPath.set(model, file.relativePath);
|
|
430
|
+
}
|
|
431
|
+
const views = extractDjangoViewNames(file.content);
|
|
432
|
+
for (const view of views) {
|
|
433
|
+
viewToPath.set(view, file.relativePath);
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
for (const file of files) {
|
|
437
|
+
const fileUnitIds = file.unitIds;
|
|
438
|
+
function addEdges(targetPath, edgeType, weight) {
|
|
439
|
+
if (!targetPath || targetPath === file.relativePath) {
|
|
440
|
+
return;
|
|
441
|
+
}
|
|
442
|
+
const targetIds = pathToUnitIds.get(targetPath);
|
|
443
|
+
if (targetIds && targetIds.length > 0) {
|
|
444
|
+
for (const fromId of fileUnitIds) {
|
|
445
|
+
for (const toId of targetIds) {
|
|
446
|
+
edges.push({ from: fromId, to: toId, type: edgeType, weight });
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
else if (fileUnitIds.length > 0) {
|
|
451
|
+
const syntheticTargetId = `${targetPath}:1:1:code_region:fw`;
|
|
452
|
+
for (const fromId of fileUnitIds) {
|
|
453
|
+
edges.push({ from: fromId, to: syntheticTargetId, type: edgeType, weight });
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
// URL patterns → View functions/classes
|
|
458
|
+
const urlViewRefs = extractDjangoUrlViews(file.content);
|
|
459
|
+
for (const viewName of urlViewRefs) {
|
|
460
|
+
const targetPath = viewToPath.get(viewName) ?? guessDjangoFilePath(viewName, file.relativePath);
|
|
461
|
+
addEdges(targetPath, "framework_route_handler", 4);
|
|
462
|
+
}
|
|
463
|
+
// View → Model usage (objects.filter/get/create)
|
|
464
|
+
const modelRefs = extractDjangoModelRefs(file.content);
|
|
465
|
+
for (const modelName of modelRefs) {
|
|
466
|
+
const targetPath = modelToPath.get(modelName) ?? guessDjangoFilePath(modelName, file.relativePath);
|
|
467
|
+
addEdges(targetPath, "framework_service_dependency", 3);
|
|
468
|
+
}
|
|
469
|
+
// View → Template rendering: render(request, 'template.html')
|
|
470
|
+
const templateRefs = extractDjangoTemplateRefs(file.content);
|
|
471
|
+
for (const template of templateRefs) {
|
|
472
|
+
addEdges(`templates/${template}`, "framework_view_render", 5);
|
|
473
|
+
}
|
|
474
|
+
// Celery task dispatch: task.delay(...)
|
|
475
|
+
const celeryTasks = extractCeleryTaskDispatches(file.content);
|
|
476
|
+
for (const taskName of celeryTasks) {
|
|
477
|
+
const targetPath = guessDjangoFilePath(taskName, file.relativePath);
|
|
478
|
+
addEdges(targetPath, "framework_job_dispatch", 4);
|
|
479
|
+
}
|
|
480
|
+
// Settings usage: settings.EMAIL_HOST
|
|
481
|
+
const settingsRefs = extractDjangoSettingsRefs(file.content);
|
|
482
|
+
if (settingsRefs.length > 0) {
|
|
483
|
+
const settingsPath = guessDjangoFilePath("settings", file.relativePath);
|
|
484
|
+
addEdges(settingsPath, "framework_config_usage", 3);
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
return edges;
|
|
488
|
+
}
|
|
489
|
+
};
|
|
490
|
+
}
|
|
491
|
+
function extractDjangoModelNames(content) {
|
|
492
|
+
const models = [];
|
|
493
|
+
const pattern = /class\s+(\w+)\s*\(\s*(?:models\.Model|Model)\s*\)/g;
|
|
494
|
+
let match = pattern.exec(content);
|
|
495
|
+
while (match) {
|
|
496
|
+
models.push(match[1]);
|
|
497
|
+
match = pattern.exec(content);
|
|
498
|
+
}
|
|
499
|
+
return models;
|
|
500
|
+
}
|
|
501
|
+
function extractDjangoViewNames(content) {
|
|
502
|
+
const views = [];
|
|
503
|
+
const classPattern = /class\s+(\w+)\s*\([^)]*View[^)]*\)/g;
|
|
504
|
+
let match = classPattern.exec(content);
|
|
505
|
+
while (match) {
|
|
506
|
+
views.push(match[1]);
|
|
507
|
+
match = classPattern.exec(content);
|
|
508
|
+
}
|
|
509
|
+
const funcPattern = /def\s+(\w+)\s*\(\s*(?:request|self)/g;
|
|
510
|
+
match = funcPattern.exec(content);
|
|
511
|
+
while (match) {
|
|
512
|
+
views.push(match[1]);
|
|
513
|
+
match = funcPattern.exec(content);
|
|
514
|
+
}
|
|
515
|
+
return views;
|
|
516
|
+
}
|
|
517
|
+
function extractDjangoUrlViews(content) {
|
|
518
|
+
const refs = [];
|
|
519
|
+
const pattern = /(?:path|re_path)\s*\([^,]+,\s*(?:(\w+)\.as_view\(\)|(\w+))\s*,/g;
|
|
520
|
+
let match = pattern.exec(content);
|
|
521
|
+
while (match) {
|
|
522
|
+
refs.push(match[1] ?? match[2]);
|
|
523
|
+
match = pattern.exec(content);
|
|
524
|
+
}
|
|
525
|
+
return refs;
|
|
526
|
+
}
|
|
527
|
+
function extractDjangoModelRefs(content) {
|
|
528
|
+
const refs = [];
|
|
529
|
+
const pattern = /(\w+)\.objects\.(?:filter|get|create|all|update|delete|exclude)\s*\(/g;
|
|
530
|
+
let match = pattern.exec(content);
|
|
531
|
+
while (match) {
|
|
532
|
+
refs.push(match[1]);
|
|
533
|
+
match = pattern.exec(content);
|
|
534
|
+
}
|
|
535
|
+
return [...new Set(refs)];
|
|
536
|
+
}
|
|
537
|
+
function extractDjangoTemplateRefs(content) {
|
|
538
|
+
const refs = [];
|
|
539
|
+
const pattern = /render\s*\(\s*(?:request|self\.request)\s*,\s*['"]([^'"]+)['"]/g;
|
|
540
|
+
let match = pattern.exec(content);
|
|
541
|
+
while (match) {
|
|
542
|
+
refs.push(match[1]);
|
|
543
|
+
match = pattern.exec(content);
|
|
544
|
+
}
|
|
545
|
+
return refs;
|
|
546
|
+
}
|
|
547
|
+
function extractCeleryTaskDispatches(content) {
|
|
548
|
+
const refs = [];
|
|
549
|
+
const pattern = /(\w+)\.(?:delay|apply_async|s)\s*\(/g;
|
|
550
|
+
let match = pattern.exec(content);
|
|
551
|
+
while (match) {
|
|
552
|
+
refs.push(match[1]);
|
|
553
|
+
match = pattern.exec(content);
|
|
554
|
+
}
|
|
555
|
+
return [...new Set(refs)];
|
|
556
|
+
}
|
|
557
|
+
function extractDjangoSettingsRefs(content) {
|
|
558
|
+
const refs = [];
|
|
559
|
+
const pattern = /(?:settings|django\.conf\.settings)\.(\w+)/g;
|
|
560
|
+
let match = pattern.exec(content);
|
|
561
|
+
while (match) {
|
|
562
|
+
refs.push(match[1]);
|
|
563
|
+
match = pattern.exec(content);
|
|
564
|
+
}
|
|
565
|
+
return refs;
|
|
566
|
+
}
|
|
567
|
+
function guessDjangoFilePath(name, sourcePath) {
|
|
568
|
+
const sourceDir = sourcePath.replace(/\\/g, "/").split("/").slice(0, -1).join("/");
|
|
569
|
+
const lower = name.toLowerCase();
|
|
570
|
+
if (lower === "settings")
|
|
571
|
+
return `${sourceDir}/settings.py`;
|
|
572
|
+
if (/\b(?:view|controller)\b/i.test(lower))
|
|
573
|
+
return `${sourceDir}/views.py`;
|
|
574
|
+
if (/\bmodel\b/i.test(lower))
|
|
575
|
+
return `${sourceDir}/models.py`;
|
|
576
|
+
if (/\btask\b/i.test(lower))
|
|
577
|
+
return `${sourceDir}/tasks.py`;
|
|
578
|
+
if (/\bserializer\b/i.test(lower))
|
|
579
|
+
return `${sourceDir}/serializers.py`;
|
|
580
|
+
if (/\bform\b/i.test(lower))
|
|
581
|
+
return `${sourceDir}/forms.py`;
|
|
582
|
+
if (/\burl\b/i.test(lower))
|
|
583
|
+
return `${sourceDir}/urls.py`;
|
|
584
|
+
return `${sourceDir}/${name}.py`;
|
|
585
|
+
}
|
|
586
|
+
//# sourceMappingURL=frameworkEdges.js.map
|
|
@@ -0,0 +1,81 @@
|
|
|
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.getApprovalPolicy = getApprovalPolicy;
|
|
37
|
+
exports.canAutoApprove = canAutoApprove;
|
|
38
|
+
exports.classifyCommand = classifyCommand;
|
|
39
|
+
const vscode = __importStar(require("vscode"));
|
|
40
|
+
function getApprovalPolicy() {
|
|
41
|
+
const config = vscode.workspace.getConfiguration("flowseeker");
|
|
42
|
+
return {
|
|
43
|
+
readWorkspace: config.get("agent.autoApprove.readWorkspace", true),
|
|
44
|
+
editWorkspace: config.get("agent.autoApprove.editWorkspace", false),
|
|
45
|
+
safeCommands: config.get("agent.autoApprove.safeCommands", false),
|
|
46
|
+
allCommands: config.get("agent.autoApprove.allCommands", false),
|
|
47
|
+
mcpTools: config.get("agent.autoApprove.mcpTools", false),
|
|
48
|
+
rules: config.get("agent.rules", [])
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
function canAutoApprove(kind, policy = getApprovalPolicy()) {
|
|
52
|
+
switch (kind) {
|
|
53
|
+
case "read_workspace":
|
|
54
|
+
return policy.readWorkspace;
|
|
55
|
+
case "edit_workspace":
|
|
56
|
+
return policy.editWorkspace;
|
|
57
|
+
case "safe_command":
|
|
58
|
+
return policy.safeCommands || policy.allCommands;
|
|
59
|
+
case "all_command":
|
|
60
|
+
return policy.allCommands;
|
|
61
|
+
case "mcp_tool":
|
|
62
|
+
return policy.mcpTools;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
function classifyCommand(command) {
|
|
66
|
+
const normalized = command.trim().toLowerCase();
|
|
67
|
+
if (!normalized) {
|
|
68
|
+
return { safe: false, destructive: false, reason: "Empty command." };
|
|
69
|
+
}
|
|
70
|
+
if (/\b(rm|del|erase|rmdir|remove-item|move-item|mv|git\s+reset|git\s+clean|format|mkfs|shutdown|reboot)\b/.test(normalized)) {
|
|
71
|
+
return { safe: false, destructive: true, reason: "Command contains a destructive operation." };
|
|
72
|
+
}
|
|
73
|
+
if (/[>;|&]/.test(normalized) && /\b(rm|del|erase|remove-item|git\s+reset|git\s+clean)\b/.test(normalized)) {
|
|
74
|
+
return { safe: false, destructive: true, reason: "Chained command includes a destructive operation." };
|
|
75
|
+
}
|
|
76
|
+
if (/^(git status|git diff|git log|git show|npm test|npm run test|npm run compile|npm run build|npm run lint|pnpm test|pnpm build|pnpm lint|yarn test|yarn build|yarn lint|cargo test|cargo check|go test|pytest|ruff check|tsc\b)/.test(normalized)) {
|
|
77
|
+
return { safe: true, destructive: false, reason: "Command matches a known read/build/test pattern." };
|
|
78
|
+
}
|
|
79
|
+
return { safe: false, destructive: false, reason: "Command is not in the safe allowlist." };
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=approvalPolicy.js.map
|