@skyramp/mcp 0.0.38 → 0.0.39

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.
@@ -0,0 +1,173 @@
1
+ import { z } from "zod";
2
+ import { TestType } from "./TestTypes.js";
3
+ // Re-export TestType for convenience
4
+ export { TestType };
5
+ // Base impact scores for each test type
6
+ export const BASE_SCORES = {
7
+ [TestType.E2E]: 100,
8
+ [TestType.UI]: 95,
9
+ [TestType.INTEGRATION]: 90,
10
+ [TestType.LOAD]: 60,
11
+ [TestType.FUZZ]: 50,
12
+ [TestType.CONTRACT]: 80,
13
+ [TestType.SMOKE]: 70,
14
+ };
15
+ export const CONTEXT_RULES = {
16
+ [TestType.E2E]: [
17
+ {
18
+ condition: "full-stack",
19
+ multiplier: 1.2,
20
+ description: "Full-stack apps benefit most from E2E tests",
21
+ },
22
+ {
23
+ condition: "backend-only",
24
+ multiplier: 0.7,
25
+ description: "Limited E2E applicability for backend-only",
26
+ },
27
+ {
28
+ condition: "frontend-spa",
29
+ multiplier: 0.9,
30
+ description: "Slightly reduced without backend",
31
+ },
32
+ {
33
+ condition: "library",
34
+ multiplier: 0,
35
+ description: "Not applicable to libraries",
36
+ },
37
+ ],
38
+ [TestType.LOAD]: [
39
+ {
40
+ condition: "hasKubernetes",
41
+ multiplier: 1.2,
42
+ description: "K8s suggests high-traffic expectations",
43
+ },
44
+ {
45
+ condition: "hasDockerCompose",
46
+ multiplier: 1.15,
47
+ description: "Scaled infrastructure indicates traffic needs",
48
+ },
49
+ {
50
+ condition: "cli-or-library",
51
+ multiplier: 0.4,
52
+ description: "Low traffic expected",
53
+ },
54
+ {
55
+ condition: "internal-tool",
56
+ multiplier: 0.4,
57
+ description: "Minimal load",
58
+ },
59
+ ],
60
+ [TestType.FUZZ]: [
61
+ {
62
+ condition: "handles-payments",
63
+ multiplier: 1.2,
64
+ description: "Security critical for payments",
65
+ },
66
+ {
67
+ condition: "handles-pii",
68
+ multiplier: 1.2,
69
+ description: "Security critical for PII",
70
+ },
71
+ {
72
+ condition: "oauth2",
73
+ multiplier: 1.15,
74
+ description: "Public API security needs",
75
+ },
76
+ {
77
+ condition: "internal-service",
78
+ multiplier: 0.9,
79
+ description: "Lower security priority",
80
+ },
81
+ ],
82
+ [TestType.CONTRACT]: [
83
+ {
84
+ condition: "microservices",
85
+ multiplier: 1.2,
86
+ description: "Critical for service contracts",
87
+ },
88
+ {
89
+ condition: "multiple-services",
90
+ multiplier: 1.15,
91
+ description: "Service interactions important",
92
+ },
93
+ {
94
+ condition: "monolith",
95
+ multiplier: 0.9,
96
+ description: "Less critical",
97
+ },
98
+ ],
99
+ [TestType.INTEGRATION]: [
100
+ {
101
+ condition: "microservices",
102
+ multiplier: 1.1,
103
+ description: "Service communication important",
104
+ },
105
+ {
106
+ condition: "has-unit-missing-integration",
107
+ multiplier: 1.2,
108
+ description: "Fill coverage gap",
109
+ },
110
+ {
111
+ condition: "has-integration-tests",
112
+ multiplier: 0.9,
113
+ description: "Less urgent",
114
+ },
115
+ ],
116
+ [TestType.UI]: [
117
+ {
118
+ condition: "frontend-spa-or-fullstack",
119
+ multiplier: 1.2,
120
+ description: "Full value for UI apps",
121
+ },
122
+ {
123
+ condition: "backend-only",
124
+ multiplier: 0,
125
+ description: "Not applicable",
126
+ },
127
+ ],
128
+ [TestType.SMOKE]: [
129
+ {
130
+ condition: "no-existing-tests",
131
+ multiplier: 1.2,
132
+ description: "Quick wins needed",
133
+ },
134
+ {
135
+ condition: "has-cicd",
136
+ multiplier: 1.1,
137
+ description: "Deployment validation",
138
+ },
139
+ {
140
+ condition: "comprehensive-tests",
141
+ multiplier: 0.8,
142
+ description: "Less valuable",
143
+ },
144
+ ],
145
+ };
146
+ // Zod schemas for validation
147
+ export const testPriorityScoreSchema = z.object({
148
+ testType: z.nativeEnum(TestType),
149
+ _baseScore: z.number().min(0).max(120),
150
+ contextMultiplier: z.number().min(0).max(2),
151
+ _finalScore: z.number().min(0).max(150),
152
+ feasibility: z.enum(["high", "medium", "low", "not-applicable"]),
153
+ requiredArtifacts: z.object({
154
+ available: z.array(z.string()),
155
+ missing: z.array(z.string()),
156
+ }),
157
+ reasoning: z.string(),
158
+ });
159
+ export const testMappingResultSchema = z.object({
160
+ priorityScores: z.array(testPriorityScoreSchema),
161
+ contextFactors: z.object({
162
+ applied: z.array(z.object({
163
+ factor: z.string(),
164
+ impact: z.string(),
165
+ multiplier: z.number(),
166
+ })),
167
+ }),
168
+ summary: z.object({
169
+ highPriority: z.array(z.nativeEnum(TestType)),
170
+ mediumPriority: z.array(z.nativeEnum(TestType)),
171
+ lowPriority: z.array(z.nativeEnum(TestType)),
172
+ }),
173
+ });
@@ -0,0 +1,52 @@
1
+ import { z } from "zod";
2
+ import { TestType } from "./TestTypes.js";
3
+ // Test type to documentation URL mapping
4
+ export const TEST_TYPE_DOCS = {
5
+ [TestType.SMOKE]: "https://www.skyramp.dev/docs/smoke-tests",
6
+ [TestType.CONTRACT]: "https://www.skyramp.dev/docs/contract-tests",
7
+ [TestType.FUZZ]: "https://www.skyramp.dev/docs/fuzz-tests",
8
+ [TestType.INTEGRATION]: "https://www.skyramp.dev/docs/integration-tests",
9
+ [TestType.LOAD]: "https://www.skyramp.dev/docs/load-tests",
10
+ [TestType.E2E]: "https://www.skyramp.dev/docs/e2e-tests",
11
+ [TestType.UI]: "https://www.skyramp.dev/docs/ui-tests",
12
+ };
13
+ // Zod schemas for validation
14
+ export const specificTestSchema = z.object({
15
+ testName: z.string(),
16
+ description: z.string(),
17
+ targetEndpoint: z.string().optional(),
18
+ targetFlow: z.string().optional(),
19
+ // generationPrompt: z.string(),
20
+ requiredInputs: z.object({
21
+ available: z.array(z.object({
22
+ name: z.string(),
23
+ path: z.string(),
24
+ })),
25
+ missing: z.array(z.object({
26
+ name: z.string(),
27
+ guidance: z.string(),
28
+ })),
29
+ }),
30
+ estimatedValue: z.string(),
31
+ });
32
+ export const testTypeRecommendationSchema = z.object({
33
+ priority: z.enum(["high", "medium", "low"]),
34
+ testType: z.nativeEnum(TestType),
35
+ rationale: z.string(),
36
+ specificTests: z.array(specificTestSchema),
37
+ gettingStarted: z.object({
38
+ prerequisites: z.array(z.string()),
39
+ quickStartCommand: z.string().optional(),
40
+ documentationUrl: z.string(),
41
+ }),
42
+ });
43
+ export const testRecommendationSchema = z.object({
44
+ summary: z.object({
45
+ totalRecommended: z.number(),
46
+ highPriorityCount: z.number(),
47
+ estimatedEffort: z.string(),
48
+ quickWins: z.array(z.string()),
49
+ }),
50
+ recommendations: z.array(testTypeRecommendationSchema),
51
+ nextSteps: z.array(z.string()),
52
+ });
@@ -68,7 +68,7 @@ export const baseSchema = z.object({
68
68
  export const baseTraceSchema = z.object({
69
69
  trace: z
70
70
  .string()
71
- .describe("MUST be absolute path to trace file for test generation like /path/to/trace.json and MUST be a json file"),
71
+ .describe("Absolute path to the trace file in JSON format (e.g., /path/to/trace.json). Must use .json extension only. Do not use .jsonl files."),
72
72
  include: z
73
73
  .array(z.string())
74
74
  .default([])
@@ -0,0 +1,262 @@
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) {
12
+ const _baseScore = BASE_SCORES[testType];
13
+ const contextMultiplier = this.calculateContextMultiplier(testType, analysis);
14
+ const _finalScore = _baseScore * contextMultiplier;
15
+ const feasibility = this.assessFeasibility(testType, analysis);
16
+ const requiredArtifacts = this.identifyRequiredArtifacts(testType, analysis);
17
+ const reasoning = this.generateReasoning(testType, _baseScore, contextMultiplier, _finalScore, analysis);
18
+ return {
19
+ testType,
20
+ _baseScore,
21
+ contextMultiplier,
22
+ _finalScore: Math.round(_finalScore * 10) / 10,
23
+ feasibility,
24
+ requiredArtifacts,
25
+ reasoning,
26
+ };
27
+ }
28
+ /**
29
+ * Calculate context multiplier based on repository characteristics
30
+ */
31
+ static calculateContextMultiplier(testType, analysis) {
32
+ const rules = CONTEXT_RULES[testType];
33
+ let multiplier = 1.0;
34
+ for (const rule of rules) {
35
+ if (this.evaluateCondition(rule.condition, analysis)) {
36
+ multiplier = rule.multiplier;
37
+ break; // Use first matching rule
38
+ }
39
+ }
40
+ return multiplier;
41
+ }
42
+ /**
43
+ * Evaluate a context condition against repository analysis
44
+ */
45
+ static evaluateCondition(condition, analysis) {
46
+ const { projectClassification, infrastructure, existingTests, artifacts } = analysis;
47
+ switch (condition) {
48
+ // Project type conditions
49
+ case "full-stack":
50
+ return projectClassification.projectType === "full-stack";
51
+ case "backend-only":
52
+ return projectClassification.projectType === "rest-api";
53
+ case "frontend-spa":
54
+ return projectClassification.projectType === "frontend";
55
+ case "library":
56
+ return projectClassification.projectType === "library";
57
+ case "cli-or-library":
58
+ return ["library", "cli"].includes(projectClassification.projectType);
59
+ case "microservices":
60
+ return (projectClassification.projectType === "microservices" ||
61
+ projectClassification.deploymentPattern === "microservices");
62
+ case "monolith":
63
+ return (projectClassification.deploymentPattern === "containerized-monolith");
64
+ // Infrastructure conditions
65
+ case "hasKubernetes":
66
+ return infrastructure.hasKubernetes;
67
+ case "hasDockerCompose":
68
+ return infrastructure.hasDockerCompose;
69
+ case "has-cicd":
70
+ return infrastructure.hasCiCd;
71
+ // Testing conditions
72
+ case "no-existing-tests":
73
+ return Object.values(existingTests.coverage).every((v) => v === 0);
74
+ case "has-unit-missing-integration":
75
+ return (existingTests.coverage.unit > 0 &&
76
+ existingTests.coverage.integration === 0);
77
+ case "has-integration-tests":
78
+ return existingTests.coverage.integration > 0;
79
+ case "comprehensive-tests":
80
+ return (Object.values(existingTests.coverage).reduce((a, b) => a + b, 0) > 20);
81
+ // Artifact conditions
82
+ case "frontend-spa-or-fullstack":
83
+ return ["frontend", "full-stack"].includes(projectClassification.projectType);
84
+ case "multiple-services":
85
+ return projectClassification.deploymentPattern === "microservices";
86
+ // Security conditions (inferred)
87
+ case "handles-payments":
88
+ return (analysis.businessContext.mainPurpose
89
+ .toLowerCase()
90
+ .includes("payment") ||
91
+ analysis.businessContext.mainPurpose
92
+ .toLowerCase()
93
+ .includes("commerce"));
94
+ case "handles-pii":
95
+ return (analysis.businessContext.mainPurpose.toLowerCase().includes("user") ||
96
+ analysis.businessContext.mainPurpose.toLowerCase().includes("profile"));
97
+ case "oauth2":
98
+ return analysis.authentication.method === "oauth2";
99
+ case "internal-service":
100
+ case "internal-tool":
101
+ return analysis.businessContext.mainPurpose
102
+ .toLowerCase()
103
+ .includes("internal");
104
+ default:
105
+ return false;
106
+ }
107
+ }
108
+ /**
109
+ * Assess feasibility of generating a test type
110
+ */
111
+ static assessFeasibility(testType, analysis) {
112
+ const { artifacts, projectClassification } = analysis;
113
+ // Check for N/A cases first
114
+ if (testType === TestType.UI &&
115
+ projectClassification.projectType === "rest-api") {
116
+ return "not-applicable";
117
+ }
118
+ if (testType === TestType.E2E &&
119
+ projectClassification.projectType === "library") {
120
+ return "not-applicable";
121
+ }
122
+ // Check artifact requirements
123
+ const requiredArtifacts = this.identifyRequiredArtifacts(testType, analysis);
124
+ const missingCount = requiredArtifacts.missing.length;
125
+ if (missingCount === 0) {
126
+ return "high";
127
+ }
128
+ else if (missingCount === 1) {
129
+ return "medium";
130
+ }
131
+ else {
132
+ return "low";
133
+ }
134
+ }
135
+ /**
136
+ * Identify required artifacts and their availability
137
+ */
138
+ static identifyRequiredArtifacts(testType, analysis) {
139
+ const available = [];
140
+ const missing = [];
141
+ const { artifacts } = analysis;
142
+ // Check OpenAPI spec
143
+ if (artifacts.openApiSpecs.length > 0) {
144
+ available.push("openApiSpec");
145
+ }
146
+ else {
147
+ if ([
148
+ TestType.SMOKE,
149
+ TestType.CONTRACT,
150
+ TestType.FUZZ,
151
+ TestType.INTEGRATION,
152
+ TestType.LOAD,
153
+ ].includes(testType)) {
154
+ missing.push("openApiSpec");
155
+ }
156
+ }
157
+ // Check Playwright recordings
158
+ if (artifacts.playwrightRecordings.length > 0) {
159
+ available.push("playwrightRecording");
160
+ }
161
+ else {
162
+ if ([TestType.UI, TestType.E2E].includes(testType)) {
163
+ missing.push("playwrightRecording");
164
+ }
165
+ }
166
+ // Check trace files
167
+ if (artifacts.traceFiles.length > 0) {
168
+ available.push("traceFile");
169
+ }
170
+ else {
171
+ if ([TestType.E2E, TestType.INTEGRATION].includes(testType)) {
172
+ // Trace files are helpful but not required if OpenAPI is available
173
+ if (artifacts.openApiSpecs.length === 0) {
174
+ missing.push("traceFile");
175
+ }
176
+ }
177
+ }
178
+ return { available, missing };
179
+ }
180
+ /**
181
+ * Generate reasoning explanation for the score
182
+ */
183
+ static generateReasoning(testType, _baseScore, multiplier, _finalScore, analysis) {
184
+ const { projectClassification, existingTests, artifacts, infrastructure } = analysis;
185
+ let reasoning = "";
186
+ // Base reasoning by test type
187
+ switch (testType) {
188
+ case TestType.INTEGRATION:
189
+ if (existingTests.coverage.integration === 0) {
190
+ reasoning = `No integration tests exist for ${analysis.apiEndpoints.totalCount} endpoints. `;
191
+ }
192
+ else {
193
+ reasoning = `${existingTests.coverage.integration} integration tests exist. `;
194
+ }
195
+ if (projectClassification.deploymentPattern === "microservices") {
196
+ reasoning +=
197
+ "Microservices architecture makes integration testing critical. ";
198
+ }
199
+ break;
200
+ case TestType.E2E:
201
+ if (projectClassification.projectType === "full-stack") {
202
+ reasoning =
203
+ "Full-stack application - E2E tests validate complete user journeys. ";
204
+ }
205
+ else {
206
+ reasoning =
207
+ "E2E tests have limited applicability for this project type. ";
208
+ }
209
+ break;
210
+ case TestType.UI:
211
+ if (["frontend", "full-stack"].includes(projectClassification.projectType)) {
212
+ reasoning = "UI testing essential for frontend validation. ";
213
+ }
214
+ else {
215
+ reasoning = "No UI components detected. ";
216
+ }
217
+ break;
218
+ case TestType.SMOKE:
219
+ if (Object.values(existingTests.coverage).every((v) => v === 0)) {
220
+ reasoning = "No existing tests - smoke tests provide quick wins. ";
221
+ }
222
+ if (infrastructure.hasCiCd) {
223
+ reasoning += "CI/CD pipeline benefits from smoke test validation. ";
224
+ }
225
+ break;
226
+ case TestType.LOAD:
227
+ if (infrastructure.hasKubernetes) {
228
+ reasoning =
229
+ "Kubernetes deployment suggests high-traffic expectations. ";
230
+ }
231
+ else {
232
+ reasoning = "Load testing validates performance and scalability. ";
233
+ }
234
+ break;
235
+ case TestType.FUZZ:
236
+ if (analysis.authentication.method !== "none") {
237
+ reasoning =
238
+ "API handles authentication - security testing important. ";
239
+ }
240
+ reasoning += "Fuzz testing discovers input validation issues. ";
241
+ break;
242
+ case TestType.CONTRACT:
243
+ if (artifacts.openApiSpecs.length > 0) {
244
+ reasoning =
245
+ "OpenAPI spec available - contract validation straightforward. ";
246
+ }
247
+ else {
248
+ reasoning = "No OpenAPI spec - contract tests harder to generate. ";
249
+ }
250
+ break;
251
+ }
252
+ // Add artifact status
253
+ const requiredArtifacts = this.identifyRequiredArtifacts(testType, analysis);
254
+ if (requiredArtifacts.missing.length > 0) {
255
+ reasoning += `Missing: ${requiredArtifacts.missing.join(", ")}. `;
256
+ }
257
+ else if (requiredArtifacts.available.length > 0) {
258
+ reasoning += `All required artifacts available. `;
259
+ }
260
+ return reasoning.trim();
261
+ }
262
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skyramp/mcp",
3
- "version": "0.0.38",
3
+ "version": "0.0.39",
4
4
  "main": "build/index.js",
5
5
  "type": "module",
6
6
  "bin": {