@skyramp/mcp 0.0.59 → 0.0.60-rc.2

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 (35) hide show
  1. package/build/index.js +32 -5
  2. package/build/prompts/test-recommendation/analysisOutputPrompt.js +98 -0
  3. package/build/prompts/test-recommendation/recommendationSections.js +226 -0
  4. package/build/prompts/test-recommendation/registerRecommendTestsPrompt.js +71 -0
  5. package/build/prompts/test-recommendation/test-recommendation-prompt.js +166 -104
  6. package/build/prompts/testGenerationPrompt.js +2 -3
  7. package/build/prompts/testbot/testbot-prompts.js +96 -93
  8. package/build/resources/analysisResources.js +254 -0
  9. package/build/services/ScenarioGenerationService.js +70 -26
  10. package/build/tools/generate-tests/generateIntegrationRestTool.js +54 -1
  11. package/build/tools/generate-tests/generateScenarioRestTool.js +8 -5
  12. package/build/tools/submitReportTool.js +28 -0
  13. package/build/tools/test-maintenance/stateCleanupTool.js +8 -0
  14. package/build/tools/test-recommendation/analyzeRepositoryTool.js +349 -217
  15. package/build/tools/test-recommendation/recommendTestsTool.js +163 -159
  16. package/build/tools/workspace/initializeWorkspaceTool.js +1 -1
  17. package/build/types/RepositoryAnalysis.js +99 -12
  18. package/build/utils/AnalysisStateManager.js +40 -23
  19. package/build/utils/branchDiff.js +47 -0
  20. package/build/utils/pr-comment-parser.js +124 -0
  21. package/build/utils/projectMetadata.js +188 -0
  22. package/build/utils/projectMetadata.test.js +81 -0
  23. package/build/utils/repoScanner.js +378 -0
  24. package/build/utils/routeParsers.js +213 -0
  25. package/build/utils/routeParsers.test.js +87 -0
  26. package/build/utils/scenarioDrafting.js +119 -0
  27. package/build/utils/scenarioDrafting.test.js +66 -0
  28. package/build/utils/trace-parser.js +166 -0
  29. package/build/utils/workspaceAuth.js +16 -0
  30. package/package.json +1 -1
  31. package/build/prompts/test-recommendation/repository-analysis-prompt.js +0 -326
  32. package/build/prompts/test-recommendation/test-mapping-prompt.js +0 -266
  33. package/build/tools/test-recommendation/mapTestsTool.js +0 -243
  34. package/build/types/TestMapping.js +0 -173
  35. package/build/utils/scoring-engine.js +0 -380
@@ -1,380 +0,0 @@
1
- import { BASE_SCORES, CONTEXT_RULES, } from "../types/TestMapping.js";
2
- import { TestType } from "../types/TestTypes.js";
3
- /**
4
- * Scoring Engine
5
- * Calculates priority scores for test types based on repository analysis
6
- */
7
- export class ScoringEngine {
8
- /**
9
- * Calculate priority score for a specific test type
10
- */
11
- static calculateTestScore(testType, analysis, analysisScope) {
12
- const _baseScore = BASE_SCORES[testType];
13
- const contextMultiplier = this.calculateContextMultiplier(testType, analysis);
14
- const diffMultiplier = analysisScope === "current_branch_diff" && analysis.branchDiffContext
15
- ? this.calculateDiffMultiplier(testType, analysis.branchDiffContext)
16
- : 1.0;
17
- const combinedMultiplier = contextMultiplier * diffMultiplier;
18
- const _finalScore = _baseScore * combinedMultiplier;
19
- const feasibility = this.assessFeasibility(testType, analysis);
20
- const requiredArtifacts = this.identifyRequiredArtifacts(testType, analysis);
21
- const reasoning = this.generateReasoning(testType, _baseScore, combinedMultiplier, _finalScore, analysis, analysisScope);
22
- return {
23
- testType,
24
- _baseScore,
25
- // Note: this field stores the combined multiplier (contextMultiplier * diffMultiplier)
26
- contextMultiplier: Math.round(combinedMultiplier * 100) / 100,
27
- _finalScore: Math.round(_finalScore * 10) / 10,
28
- feasibility,
29
- requiredArtifacts,
30
- reasoning,
31
- };
32
- }
33
- /**
34
- * Calculate a score multiplier based on what changed in the branch diff.
35
- * Boosts test types that are most relevant to the specific code changes.
36
- */
37
- static calculateDiffMultiplier(testType, diff) {
38
- const hasNewEndpoints = diff.newEndpoints.length > 0;
39
- const hasModifiedEndpoints = diff.modifiedEndpoints.length > 0;
40
- const hasEndpointChanges = hasNewEndpoints || hasModifiedEndpoints;
41
- if (!hasEndpointChanges) {
42
- if (diff.changedFiles.length === 0) {
43
- // Truly empty diff — lower priority across the board.
44
- return 0.5;
45
- }
46
- // Check whether any changed file looks like an API-affecting resource
47
- // (model, schema, validator, serializer, DTO) even though no route
48
- // decorator lines were touched. These changes affect API behaviour just
49
- // as much as a route change (e.g. tightening a Pydantic field validator).
50
- const hasApiRelatedFiles = diff.changedFiles.some((f) => /(model|schema|validator|serializer|dto)/i.test(f));
51
- if (hasApiRelatedFiles) {
52
- // Boost contract and fuzz — they're the most relevant for
53
- // validation/schema changes. Give smoke a small nudge too.
54
- switch (testType) {
55
- case TestType.CONTRACT:
56
- return 1.3;
57
- case TestType.FUZZ:
58
- return 1.4; // validation rule changes are exactly what fuzz catches
59
- case TestType.SMOKE:
60
- return 1.1;
61
- default:
62
- return 1.0;
63
- }
64
- }
65
- // Files changed but not API-related — neutral.
66
- return 1.0;
67
- }
68
- switch (testType) {
69
- case TestType.CONTRACT:
70
- if (hasModifiedEndpoints)
71
- return 1.4;
72
- if (hasNewEndpoints)
73
- return 1.3;
74
- return 1.0;
75
- case TestType.FUZZ:
76
- if (hasModifiedEndpoints)
77
- return 1.4;
78
- if (hasNewEndpoints)
79
- return 1.2;
80
- return 1.0;
81
- case TestType.SMOKE:
82
- if (hasNewEndpoints)
83
- return 1.3;
84
- if (hasModifiedEndpoints)
85
- return 1.2;
86
- return 1.0;
87
- case TestType.INTEGRATION:
88
- if (hasNewEndpoints)
89
- return 1.2;
90
- if (hasModifiedEndpoints)
91
- return 1.1;
92
- return 1.0;
93
- case TestType.LOAD:
94
- if (hasNewEndpoints)
95
- return 1.1;
96
- if (hasModifiedEndpoints)
97
- return 1.0;
98
- return 1.0;
99
- case TestType.E2E:
100
- if (hasNewEndpoints)
101
- return 1.1;
102
- if (hasModifiedEndpoints)
103
- return 1.0;
104
- return 1.0;
105
- case TestType.UI:
106
- return 0.5;
107
- default:
108
- return 1.0;
109
- }
110
- }
111
- /**
112
- * Calculate context multiplier based on repository characteristics
113
- */
114
- static calculateContextMultiplier(testType, analysis) {
115
- const rules = CONTEXT_RULES[testType];
116
- let multiplier = 1.0;
117
- for (const rule of rules) {
118
- if (this.evaluateCondition(rule.condition, analysis)) {
119
- multiplier = rule.multiplier;
120
- break; // Use first matching rule
121
- }
122
- }
123
- return multiplier;
124
- }
125
- /**
126
- * Evaluate a context condition against repository analysis
127
- */
128
- static evaluateCondition(condition, analysis) {
129
- const { projectClassification, infrastructure, existingTests, artifacts } = analysis;
130
- switch (condition) {
131
- // Project type conditions
132
- case "full-stack":
133
- return projectClassification.projectType === "full-stack";
134
- case "backend-only":
135
- return projectClassification.projectType === "rest-api";
136
- case "frontend-spa":
137
- return projectClassification.projectType === "frontend";
138
- case "library":
139
- return projectClassification.projectType === "library";
140
- case "cli-or-library":
141
- return ["library", "cli"].includes(projectClassification.projectType);
142
- case "microservices":
143
- return (projectClassification.projectType === "microservices" ||
144
- projectClassification.deploymentPattern === "microservices");
145
- case "monolith":
146
- return (projectClassification.deploymentPattern === "containerized-monolith");
147
- // Infrastructure conditions
148
- case "hasKubernetes":
149
- return infrastructure.hasKubernetes;
150
- case "hasDockerCompose":
151
- return infrastructure.hasDockerCompose;
152
- case "has-cicd":
153
- return infrastructure.hasCiCd;
154
- // Testing conditions
155
- case "no-existing-tests":
156
- return Object.values(existingTests.coverage).every((v) => v === 0);
157
- case "has-unit-missing-integration":
158
- return (existingTests.coverage.unit > 0 &&
159
- existingTests.coverage.integration === 0);
160
- case "has-integration-tests":
161
- return existingTests.coverage.integration > 0;
162
- case "comprehensive-tests":
163
- return (Object.values(existingTests.coverage).reduce((a, b) => a + b, 0) > 20);
164
- // Artifact conditions
165
- case "frontend-spa-or-fullstack":
166
- return ["frontend", "full-stack"].includes(projectClassification.projectType);
167
- case "multiple-services":
168
- return projectClassification.deploymentPattern === "microservices";
169
- // Security conditions (inferred)
170
- case "handles-payments":
171
- return (analysis.businessContext.mainPurpose
172
- .toLowerCase()
173
- .includes("payment") ||
174
- analysis.businessContext.mainPurpose
175
- .toLowerCase()
176
- .includes("commerce"));
177
- case "handles-pii":
178
- return (analysis.businessContext.mainPurpose.toLowerCase().includes("user") ||
179
- analysis.businessContext.mainPurpose.toLowerCase().includes("profile"));
180
- case "oauth2":
181
- return analysis.authentication.method === "oauth2";
182
- case "internal-service":
183
- case "internal-tool":
184
- return analysis.businessContext.mainPurpose
185
- .toLowerCase()
186
- .includes("internal");
187
- default:
188
- return false;
189
- }
190
- }
191
- /**
192
- * Assess feasibility of generating a test type
193
- */
194
- static assessFeasibility(testType, analysis) {
195
- const { artifacts, projectClassification } = analysis;
196
- // Check for N/A cases first
197
- if (testType === TestType.UI &&
198
- projectClassification.projectType === "rest-api") {
199
- return "not-applicable";
200
- }
201
- if (testType === TestType.E2E &&
202
- projectClassification.projectType === "library") {
203
- return "not-applicable";
204
- }
205
- // Check artifact requirements
206
- const requiredArtifacts = this.identifyRequiredArtifacts(testType, analysis);
207
- const missingCount = requiredArtifacts.missing.length;
208
- if (missingCount === 0) {
209
- return "high";
210
- }
211
- else if (missingCount === 1) {
212
- return "medium";
213
- }
214
- else {
215
- return "low";
216
- }
217
- }
218
- /**
219
- * Identify required artifacts and their availability
220
- */
221
- static identifyRequiredArtifacts(testType, analysis) {
222
- const available = [];
223
- const missing = [];
224
- const { artifacts } = analysis;
225
- // Check OpenAPI spec
226
- if (artifacts.openApiSpecs.length > 0) {
227
- available.push("openApiSpec");
228
- }
229
- else {
230
- if ([
231
- TestType.SMOKE,
232
- TestType.CONTRACT,
233
- TestType.FUZZ,
234
- TestType.INTEGRATION,
235
- TestType.LOAD,
236
- ].includes(testType)) {
237
- missing.push("openApiSpec");
238
- }
239
- }
240
- // Check Playwright recordings
241
- if (artifacts.playwrightRecordings.length > 0) {
242
- available.push("playwrightRecording");
243
- }
244
- else {
245
- if ([TestType.UI, TestType.E2E].includes(testType)) {
246
- missing.push("playwrightRecording");
247
- }
248
- }
249
- // Check trace files
250
- if (artifacts.traceFiles.length > 0) {
251
- available.push("traceFile");
252
- }
253
- else {
254
- if ([TestType.E2E, TestType.INTEGRATION].includes(testType)) {
255
- // Trace files are helpful but not required if OpenAPI is available
256
- if (artifacts.openApiSpecs.length === 0) {
257
- missing.push("traceFile");
258
- }
259
- }
260
- }
261
- return { available, missing };
262
- }
263
- /**
264
- * Generate reasoning explanation for the score
265
- */
266
- static generateReasoning(testType, _baseScore, multiplier, _finalScore, analysis, analysisScope) {
267
- const { projectClassification, existingTests, artifacts, infrastructure } = analysis;
268
- let reasoning = "";
269
- if (analysisScope === "current_branch_diff" &&
270
- analysis.branchDiffContext) {
271
- const diff = analysis.branchDiffContext;
272
- const hasNew = diff.newEndpoints.length > 0;
273
- const hasMod = diff.modifiedEndpoints.length > 0;
274
- const endpoints = [
275
- ...diff.newEndpoints.map((e) => `${e.method} ${e.path} (new)`),
276
- ...diff.modifiedEndpoints.map((e) => `${e.method} ${e.path} (modified)`),
277
- ];
278
- if (!hasNew && !hasMod) {
279
- reasoning += "No endpoint changes in branch diff — lower priority. ";
280
- }
281
- else {
282
- const affectedLabel = endpoints.join(", ");
283
- switch (testType) {
284
- case TestType.CONTRACT:
285
- reasoning += `Modified endpoints change API contract — contract tests critical. Affected: ${affectedLabel}. `;
286
- break;
287
- case TestType.FUZZ:
288
- reasoning += `Validation rules changed — fuzz testing needed to verify boundary handling. Affected: ${affectedLabel}. `;
289
- break;
290
- case TestType.SMOKE:
291
- reasoning += `Endpoint behavior changed — smoke tests verify basic functionality. Affected: ${affectedLabel}. `;
292
- break;
293
- case TestType.INTEGRATION:
294
- reasoning += `Endpoint changes may affect multi-step flows. Affected: ${affectedLabel}. `;
295
- break;
296
- default:
297
- if (hasNew || hasMod) {
298
- reasoning += `Branch changes affect: ${affectedLabel}. `;
299
- }
300
- break;
301
- }
302
- }
303
- }
304
- // Base reasoning by test type — use += to preserve any diff reasoning above
305
- switch (testType) {
306
- case TestType.INTEGRATION:
307
- if (existingTests.coverage.integration === 0) {
308
- reasoning += `No integration tests exist for ${analysis.apiEndpoints.totalCount} endpoints. `;
309
- }
310
- else {
311
- reasoning += `${existingTests.coverage.integration} integration tests exist. `;
312
- }
313
- if (projectClassification.deploymentPattern === "microservices") {
314
- reasoning +=
315
- "Microservices architecture makes integration testing critical. ";
316
- }
317
- break;
318
- case TestType.E2E:
319
- if (projectClassification.projectType === "full-stack") {
320
- reasoning +=
321
- "Full-stack application - E2E tests validate complete user journeys. ";
322
- }
323
- else {
324
- reasoning +=
325
- "E2E tests have limited applicability for this project type. ";
326
- }
327
- break;
328
- case TestType.UI:
329
- if (["frontend", "full-stack"].includes(projectClassification.projectType)) {
330
- reasoning += "UI testing essential for frontend validation. ";
331
- }
332
- else {
333
- reasoning += "No UI components detected. ";
334
- }
335
- break;
336
- case TestType.SMOKE:
337
- if (Object.values(existingTests.coverage).every((v) => v === 0)) {
338
- reasoning += "No existing tests - smoke tests provide quick wins. ";
339
- }
340
- if (infrastructure.hasCiCd) {
341
- reasoning += "CI/CD pipeline benefits from smoke test validation. ";
342
- }
343
- break;
344
- case TestType.LOAD:
345
- if (infrastructure.hasKubernetes) {
346
- reasoning +=
347
- "Kubernetes deployment suggests high-traffic expectations. ";
348
- }
349
- else {
350
- reasoning += "Load testing validates performance and scalability. ";
351
- }
352
- break;
353
- case TestType.FUZZ:
354
- if (analysis.authentication.method !== "none") {
355
- reasoning +=
356
- "API handles authentication - security testing important. ";
357
- }
358
- reasoning += "Fuzz testing discovers input validation issues. ";
359
- break;
360
- case TestType.CONTRACT:
361
- if (artifacts.openApiSpecs.length > 0) {
362
- reasoning +=
363
- "OpenAPI spec available - contract validation straightforward. ";
364
- }
365
- else {
366
- reasoning += "No OpenAPI spec - contract tests harder to generate. ";
367
- }
368
- break;
369
- }
370
- // Add artifact status
371
- const requiredArtifacts = this.identifyRequiredArtifacts(testType, analysis);
372
- if (requiredArtifacts.missing.length > 0) {
373
- reasoning += `Missing: ${requiredArtifacts.missing.join(", ")}. `;
374
- }
375
- else if (requiredArtifacts.available.length > 0) {
376
- reasoning += `All required artifacts available. `;
377
- }
378
- return reasoning.trim();
379
- }
380
- }