@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
package/build/index.js CHANGED
@@ -19,8 +19,8 @@ import { registerLoginTool } from "./tools/auth/loginTool.js";
19
19
  import { registerLogoutTool } from "./tools/auth/logoutTool.js";
20
20
  import { registerFixErrorTool } from "./tools/fixErrorTool.js";
21
21
  import { registerAnalyzeRepositoryTool } from "./tools/test-recommendation/analyzeRepositoryTool.js";
22
- import { registerMapTestsTool } from "./tools/test-recommendation/mapTestsTool.js";
23
22
  import { registerRecommendTestsTool } from "./tools/test-recommendation/recommendTestsTool.js";
23
+ import { registerRecommendTestsPrompt } from "./prompts/test-recommendation/registerRecommendTestsPrompt.js";
24
24
  import { registerModularizationTool } from "./tools/code-refactor/modularizationTool.js";
25
25
  import { registerCodeReuseTool } from "./tools/code-refactor/codeReuseTool.js";
26
26
  import { registerScenarioTestTool } from "./tools/generate-tests/generateScenarioRestTool.js";
@@ -34,6 +34,7 @@ import { registerTestbotPrompt, registerTestbotResource, } from "./prompts/testb
34
34
  import { registerInitTestbotTool } from "./tools/initTestbotTool.js";
35
35
  import { registerSubmitReportTool } from "./tools/submitReportTool.js";
36
36
  import { registerInitializeWorkspaceTool } from "./tools/workspace/initializeWorkspaceTool.js";
37
+ import { registerAnalysisResources } from "./resources/analysisResources.js";
37
38
  import { AnalyticsService } from "./services/AnalyticsService.js";
38
39
  import { initCheck } from "./utils/initAgent.js";
39
40
  const server = new McpServer({
@@ -47,13 +48,32 @@ const server = new McpServer({
47
48
  prompts: {
48
49
  listChanged: true,
49
50
  },
51
+ resources: {
52
+ listChanged: true,
53
+ },
50
54
  },
51
- instructions: `Skyramp MCP Server — generates and executes API tests (smoke, fuzz, contract, load, integration, E2E, UI).
55
+ instructions: `Skyramp MCP Server — generates and executes API tests (fuzz, contract, integration, E2E, UI).
52
56
 
53
57
  ## Rules
54
58
  - NEVER show CLI commands. ALWAYS use the MCP tools provided.
55
59
  - For UI and E2E tests, use the trace collection start/stop tools.
56
60
 
61
+ ## Test Recommendation Flow (2-step)
62
+ 1. Call \`skyramp_analyze_repository\` → returns a \`sessionId\`.
63
+ The analysis scans source code (code-first) to build enriched endpoints
64
+ (Path → Method → Interaction with request/response bodies, headers, cookies)
65
+ and draft user-flow scenarios for integration/E2E tests.
66
+ 2. Call \`skyramp_recommend_tests\` with \`sessionId\` → the LLM reasons over the
67
+ enriched data to recommend tests, referencing specific interactions and scenarios.
68
+
69
+ After analysis, you can also inspect data via MCP Resources:
70
+ - \`skyramp://analysis/{sessionId}/summary\` — high-level overview
71
+ - \`skyramp://analysis/{sessionId}/endpoints\` — compact endpoint listing
72
+ - \`skyramp://analysis/{sessionId}/endpoints/{path}\` — full path detail
73
+ - \`skyramp://analysis/{sessionId}/endpoints/{path}/{method}\` — single method detail
74
+ - \`skyramp://analysis/{sessionId}/scenarios\` — drafted scenarios
75
+ - \`skyramp://analysis/{sessionId}/diff\` — branch diff context
76
+
57
77
  ## Workspace Initialization (before ANY other Skyramp tool)
58
78
  Follow this flow EVERY time before calling any Skyramp tool:
59
79
 
@@ -75,8 +95,13 @@ Before calling ANY test generation tool, you MUST follow this flow:
75
95
  2. **Extract** the \`language\`, \`framework\`, \`outputDir\`, and \`api.baseUrl\` from the services section.
76
96
  3. **Use those values** as defaults for the test generation tool call. Do NOT ask the user for these values if they are already configured in the workspace file.
77
97
  4. **CRITICAL — endpointURL**: The \`endpointURL\` parameter MUST be the full URL to the specific endpoint being tested, NOT just the base URL. Construct it by combining \`api.baseUrl\` with the endpoint path. Example: if \`api.baseUrl\` is \`http://localhost:8000\` and the endpoint is \`/api/v1/products\`, pass \`endpointURL: "http://localhost:8000/api/v1/products"\`. NEVER pass just the base URL (e.g. \`http://localhost:8000\`) as \`endpointURL\`.
78
- 5. **If the workspace file does not exist**, or the needed values (language, framework, outputDir) are missing from the workspace config, ASK the user which language and framework they want before calling the tool.
79
- 6. The user can always override workspace defaults by explicitly specifying values in their request.
98
+ 5. **CRITICAL scenario generation**: When calling \`skyramp_scenario_test_generation\`, ALWAYS pass:
99
+ - \`baseURL\`: The full base URL from \`api.baseUrl\` (e.g., \`http://localhost:3000\`). This determines the scheme, host, and port in the generated trace. Without it, the trace defaults to https:443 which is almost always wrong for local development.
100
+ - \`authHeader\`: The auth header name from \`api.authHeader\` in the workspace config. Use \`Cookie\` for cookie/session-based auth (NextAuth, etc.), \`Authorization\` for Bearer tokens, \`X-API-Key\` for API keys. Without it, the trace defaults to \`Authorization: Bearer\` which breaks cookie-based apps.
101
+ - \`apiSchema\` is OPTIONAL — omit it for code-first apps without OpenAPI specs.
102
+ 6. **CRITICAL — integration test from scenario**: When calling \`skyramp_integration_test_generation\` with a \`scenarioFile\`, ALSO pass \`authHeader\` (same value as used in scenario generation). This tells the CLI which header to parameterize with the auth token. Without it, the generated test defaults to \`Authorization: Bearer\` regardless of what's in the trace.
103
+ 7. **If the workspace file does not exist**, or the needed values (language, framework, outputDir) are missing from the workspace config, ASK the user which language and framework they want before calling the tool.
104
+ 8. The user can always override workspace defaults by explicitly specifying values in their request.
80
105
  `,
81
106
  });
82
107
  // Check for first-time invocation after version update (runs in background, doesn't block)
@@ -116,6 +141,7 @@ const prompts = [
116
141
  registerTestGenerationPrompt,
117
142
  registerStartTraceCollectionPrompt,
118
143
  registerTestHealthPrompt,
144
+ registerRecommendTestsPrompt,
119
145
  ];
120
146
  if (process.env.SKYRAMP_FEATURE_TESTBOT === "1") {
121
147
  prompts.push(registerTestbotPrompt);
@@ -145,8 +171,9 @@ const codeQualityTools = [
145
171
  codeQualityTools.forEach((registerTool) => registerTool(server));
146
172
  // Register test recommendation tools
147
173
  registerAnalyzeRepositoryTool(server);
148
- registerMapTestsTool(server);
149
174
  registerRecommendTestsTool(server);
175
+ // Register analysis resources (MCP Resources for enriched data access)
176
+ registerAnalysisResources(server);
150
177
  // Register test maintenance tools
151
178
  registerDiscoverTestsTool(server);
152
179
  registerAnalyzeTestDriftTool(server);
@@ -0,0 +1,98 @@
1
+ function buildEnrichmentInstructions(p) {
2
+ const isDiffScope = p.analysisScope === "current_branch_diff";
3
+ if (!isDiffScope) {
4
+ return `## Your Task — Enrich & Recommend (full repo)
5
+
6
+ ### Step 1: Read key files
7
+ Read \`package.json\` / \`requirements.txt\`, \`docker-compose.yml\`, and route/controller files
8
+ to understand the tech stack, endpoint shapes, and auth mechanisms.
9
+
10
+ ### Step 2: Identify resource relationships
11
+ Map how endpoints relate to each other — which POST creates resources consumed by other endpoints?
12
+ **Resolve nested/sub-router paths** from the Router Mounting section above.
13
+
14
+ ### Step 3: Call recommend tests
15
+ Call \`skyramp_recommend_tests\` with \`sessionId: "${p.sessionId}"\``;
16
+ }
17
+ const changedFiles = p.parsedDiff?.changedFiles.join(", ") ?? "";
18
+ const hasApiEndpoints = p.parsedDiff && (p.parsedDiff.newEndpoints.length > 0 || p.parsedDiff.modifiedEndpoints.length > 0);
19
+ const isUIOnly = !hasApiEndpoints && (p.parsedDiff?.changedFiles.every(f => !f.match(/\/(api|routes?|controllers?|routers?|handlers?|endpoints?)\//)) ?? false);
20
+ const step2 = hasApiEndpoints
21
+ ? `### Step 2: Discover related endpoints
22
+ Read handler code for changed API endpoints. Find related endpoints via imports, shared
23
+ models, adjacent route files. Resolve nested/sub-router paths from Router Mounting context.`
24
+ : isUIOnly
25
+ ? `### Step 2: Identify consumed API endpoints
26
+ UI-only PR — read changed components to find API calls (fetch, axios, hooks).`
27
+ : `### Step 2: Identify affected endpoints
28
+ No API route changes detected — read changed files to identify affected endpoints.`;
29
+ return `## Your Task — Enrich & Recommend (PR-scoped)
30
+
31
+ ### Step 1: Read the changed files
32
+ ${changedFiles}
33
+
34
+ ${step2}
35
+
36
+ ### Step 3: Draft integration scenarios
37
+ Draft multi-step scenarios simulating realistic user workflows:
38
+ - **Cross-resource data flow**: Foreign key relationships, parent→child creation, verification
39
+ - **Search/filter verification**: Create data, search, verify results
40
+ - **Negative/error paths**: Invalid references → appropriate errors
41
+ - **UI user journeys**: Concrete browser steps for frontend changes
42
+
43
+ **Quality:** Realistic request bodies, response data verification, actual field names for chaining.
44
+
45
+ ### Step 4: Call recommend tests
46
+ Call \`skyramp_recommend_tests\` with \`sessionId: "${p.sessionId}"\``;
47
+ }
48
+ export function buildAnalysisOutputText(p) {
49
+ const isDiffScope = p.analysisScope === "current_branch_diff";
50
+ const diffSection = p.parsedDiff
51
+ ? `
52
+ ## Branch Diff Context
53
+ **Branch**: \`${p.parsedDiff.currentBranch}\` → base: \`${p.parsedDiff.baseBranch}\`
54
+ **Changed Files** (${p.parsedDiff.changedFiles.length}): ${p.parsedDiff.changedFiles.join(", ")}
55
+ **New Endpoints** (${p.parsedDiff.newEndpoints.length}): ${p.parsedDiff.newEndpoints.map((e) => `${e.method} ${e.path} (${e.sourceFile})`).join(", ") || "none"}
56
+ **Modified Endpoints** (${p.parsedDiff.modifiedEndpoints.length}): ${p.parsedDiff.modifiedEndpoints.map((e) => `${e.method} ${e.path} (${e.sourceFile})`).join(", ") || "none"}
57
+ **Affected Services**: ${p.parsedDiff.affectedServices.join(", ") || "none"}
58
+ `
59
+ : "";
60
+ const endpointCatalog = p.scannedEndpoints.length > 0
61
+ ? `
62
+ ## Pre-Scanned Endpoint Catalog (${p.scannedEndpoints.length} routes)
63
+ ${p.scannedEndpoints.map((ep) => ` ${ep.methods.join("|")} ${ep.path} (${ep.sourceFile})`).join("\n")}
64
+ `
65
+ : "";
66
+ const wsLine = p.wsBaseUrl
67
+ ? `**Base URL**: \`${p.wsBaseUrl}\` | **Auth header**: \`${p.wsAuthHeader || "Authorization"}\``
68
+ : "";
69
+ const specSection = p.wsSchemaPath
70
+ ? `
71
+ ## OpenAPI Spec Available
72
+ Spec at \`${p.wsSchemaPath}\`. **Read it** for authoritative paths and schemas.
73
+ Pass \`apiSchema: "${p.wsSchemaPath}"\` to ALL test generation tool calls.`
74
+ : p.routerMountContext
75
+ ? `
76
+ ## Router Mounting / Nesting
77
+ \`\`\`
78
+ ${p.routerMountContext}
79
+ \`\`\`
80
+ Use this to resolve full URL paths for nested endpoints.`
81
+ : "";
82
+ const enrichment = buildEnrichmentInstructions(p);
83
+ return `# Repository Analysis
84
+
85
+ **Session ID**: \`${p.sessionId}\`
86
+ **Repository**: \`${p.repositoryPath}\`
87
+ **Analysis Scope**: \`${p.analysisScope}\`
88
+ ${isDiffScope ? `**Diff endpoints**: ${(p.parsedDiff?.newEndpoints.length ?? 0) + (p.parsedDiff?.modifiedEndpoints.length ?? 0)}` : `**Pre-scanned endpoints**: ${p.scannedEndpoints.length}`}
89
+ ${wsLine}
90
+ ${p.wsSchemaPath ? `**OpenAPI Spec**: \`${p.wsSchemaPath}\` (spec-based flow)` : "**Flow**: Code-scanning (may miss nesting)"}
91
+
92
+ ${diffSection}
93
+ ${endpointCatalog}
94
+ ${specSection}
95
+ ${enrichment}
96
+
97
+ **CRITICAL**: No .json/.md file creation. Prioritize cross-resource workflows.`;
98
+ }
@@ -0,0 +1,226 @@
1
+ export function buildPrioritizationDimensions() {
2
+ return `## Prioritization Dimensions (evaluate each candidate test)
3
+
4
+ For each candidate test, assess these dimensions using your judgment:
5
+
6
+ | Dimension | What to assess |
7
+ |-----------|---------------|
8
+ | **Sophistication** | Does it test a multi-step workflow or non-obvious scenario? Or is it a simple request→response check? |
9
+ | **Bug-Finding Potential** | Does it target known failure modes (race conditions, data consistency, state transitions, cascade effects)? |
10
+ | **User Journey Relevance** | Does it reflect how real users interact with the system (from traces, business flows, or critical paths)? |
11
+ | **Coverage Gap** | Does it address an area with zero existing test coverage? Or does it duplicate what\'s already tested? |
12
+ | **Code Insight** | Is it derived from actual implementation analysis (e.g., spotted a middleware pattern, found an N+1 risk) rather than just API shape? |
13
+
14
+ Candidates scoring well across MULTIPLE dimensions should be recommended first.
15
+ Candidates satisfying only ONE dimension (e.g., covers a gap but is trivially simple) should be deprioritized.
16
+
17
+ **Quality Gate:** For each candidate, ask: "Would a senior engineer be impressed by this test?"
18
+ If the answer is no — deprioritize it. Impressive tests catch real bugs, exercise real workflows,
19
+ and demonstrate understanding of the system\'s behavior. Trivial tests do not.`;
20
+ }
21
+ export function buildTestExamples() {
22
+ return `## Test Examples (calibrate your judgment)
23
+
24
+ **Impressive tests (recommend these):**
25
+ 1. "Register user → login → create order → verify order appears in user\'s order list"
26
+ Cross-resource workflow with auth chaining and data verification across users + orders.
27
+ 2. "Create product with inventory=10 → place order for qty=10 → verify inventory=0 →
28
+ place another order → verify 409 out-of-stock error"
29
+ Cross-resource state machine + business rule validation (products + orders + inventory).
30
+ 3. "POST /users with duplicate email → verify 409 Conflict → verify original user unchanged"
31
+ Error handling with side-effect verification — not just status code check.
32
+
33
+ **Non-impressive tests (deprioritize or skip):**
34
+ 1. "GET /products → 200" — trivial health check, no assertions beyond status code.
35
+ 2. "POST /products → GET /products/{id} → PUT /products/{id} → DELETE /products/{id}"
36
+ Single-resource CRUD — baseline, not impressive by itself.
37
+ 3. "POST /products with missing name → 422" — obvious validation, already covered by contract/fuzz.`;
38
+ }
39
+ export function buildTestPatternGuidelines() {
40
+ return `## Test Pattern Guidelines (reference, not rigid rules)
41
+
42
+ ### Tier 1 — Base Patterns
43
+ - CRUD lifecycle per resource group (Create → Read → Update → Delete)
44
+ - Auth flow (Register → Login → Access protected → Token refresh → Logout)
45
+ - Pagination & filtering (boundary values, empty results, large page sizes)
46
+ - Error responses (400, 401, 403, 404, 409, 422 — each with specific trigger)
47
+
48
+ ### Tier 2 — Code-Informed Patterns (higher value — look for these in the codebase)
49
+ - **Middleware chains**: If auth/rate-limit/logging middleware exists, test the chain
50
+ (e.g., rate limit hit → auth still checked → correct error returned)
51
+ - **N+1 query risk**: If list endpoints join related data (e.g., orders with products),
52
+ test with large datasets under load
53
+ - **State machines**: If resources have status transitions (draft→published→archived),
54
+ test invalid transitions (e.g., archived→draft should fail)
55
+ - **Cascade deletes**: If deleting a parent removes children, verify cascade AND verify
56
+ orphan prevention (delete product → orders referencing it get error or cascade)
57
+ - **Race conditions**: If concurrent writes are possible (inventory deduction, counter
58
+ increment), test concurrent requests under load
59
+ - **Computed fields**: If response contains derived values (total, average, count),
60
+ verify computation with known inputs
61
+ - **Webhook/event side effects**: If endpoints trigger async operations, test that side
62
+ effects occur (e.g., POST /orders triggers email notification)`;
63
+ }
64
+ export function buildTestQualityCriteria() {
65
+ return `## What Makes a Good Test
66
+
67
+ **Integration tests** should demonstrate cross-resource data flow — step A creates data
68
+ that step B depends on (e.g., create product \u2192 create order referencing that product's ID \u2192
69
+ verify order contains correct product). Single-resource CRUD alone is not an integration test.
70
+ Use realistic request bodies from source code schemas and verify response data, not just
71
+ status codes.
72
+
73
+ **E2E tests** should follow realistic user journeys end-to-end: browse products \u2192 search \u2192
74
+ add to cart \u2192 checkout. Verify that frontend actions trigger the correct API calls and
75
+ that the UI reflects backend state.
76
+
77
+ **UI tests** should exercise component behavior and interaction flows: fill form \u2192 validate
78
+ inputs \u2192 submit \u2192 see confirmation. Include visual state changes (loading, error, empty)
79
+ and accessibility checks.`;
80
+ }
81
+ export function buildGenerationRules(isUIOnlyPR) {
82
+ return `## Generation Guidelines
83
+
84
+ **Scenario fidelity:** Every workflow scenario should reflect the actual resource
85
+ relationships in the code. If the pre-drafted scenarios don't match the real data model,
86
+ replace them with accurate ones.
87
+
88
+ **Priority ordering by PR type:**
89
+ ${isUIOnlyPR ? `This is a **UI-only PR**. The most valuable tests are UI and E2E tests.
90
+
91
+ If Playwright traces exist for the changed pages, prioritize UI/E2E tests in the top 4.
92
+ If no traces exist, UI/E2E tests are still the highest-value recommendations — rank them
93
+ in the top 7 with scenario steps and trace recording instructions. The testbot will not
94
+ generate tests without traces, so all 7 become additionalRecommendations.
95
+
96
+ 1. **UI tests** — per changed component/page
97
+ 2. **E2E tests** — per user flow spanning frontend to backend
98
+ 3. **Integration tests** — only when the changed UI calls backend APIs
99
+ ` : `1. **Multi-resource integration tests** — one per cross-resource workflow (2-3 max).
100
+ 2. **Fuzz tests** — per POST/PUT endpoint with complex request bodies. Tests boundary values,
101
+ type coercion, missing/extra fields, and edge cases the schema allows.
102
+ 3. **Contract tests** — per endpoint with new/changed response schemas. Validates the response
103
+ structure matches expectations (field types, required fields, nested objects).
104
+ 4. **E2E tests** — per distinct user flow if the API serves a frontend or client
105
+ 5. **CRUD lifecycle integration tests** — only for resources with new/changed endpoints
106
+ where multi-resource tests don't already cover them.
107
+ `}When no Playwright trace exists, still recommend the test with instructions for recording
108
+ a trace using \`skyramp_start_trace_collection\` with \`playwright: true\`.
109
+
110
+ **Mixed PRs with frontend changes:** Include at least 1 E2E or UI test in the top 7,
111
+ ranked by value regardless of trace availability. If traces exist, place it in the top 4.
112
+ If no traces, it can still rank highly — the testbot will handle trace-dependent generation.
113
+
114
+ **Before finalizing:** Check that the top 4 aren't filled with CRUD tests for unchanged
115
+ resources when PR-relevant tests exist lower in the ranking. Swap if needed.
116
+
117
+ **No duplicate coverage.** If an existing test already covers an endpoint + test type,
118
+ recommend a multi-resource workflow that includes that endpoint alongside others instead.`;
119
+ }
120
+ export function buildToolWorkflows(authHeaderValue) {
121
+ return `## How to Generate Tests — Tool Workflows
122
+
123
+ **Auth Header:** \`authHeader: "${authHeaderValue}"\` — pass to EVERY tool call below.
124
+
125
+ **For multi-endpoint workflows (integration tests) — Scenario → Integration pipeline:**
126
+ 1. Call \`skyramp_scenario_test_generation\` once per step: \`scenarioName\`, \`destination\`,
127
+ \`baseURL\`, \`method\`, \`path\`, \`requestBody\`, \`authHeader: "${authHeaderValue}"\`.
128
+ \`statusCode\` is optional — defaults: POST→201, DELETE→204, GET/PUT/PATCH→200. Only override for non-standard codes.
129
+ **OpenAPI spec is NOT required.** \`apiSchema\` is OPTIONAL — omit it if no spec exists.
130
+ \`requestBody\` should use realistic field values from source code schemas (Zod, Pydantic, DTOs).
131
+ Inspect the source code to determine the correct request body shape — avoid sending \`{}\`.
132
+ Use unique names with timestamp suffix to avoid conflicts on re-runs.
133
+ For GET/PUT/DELETE with path IDs, use a placeholder — chaining resolves the real ID.
134
+ 2. Produces a \`scenario_<name>.json\` in the same \`outputDir\` as the test files (not \`.skyramp/\`).
135
+ 3. Call \`skyramp_integration_test_generation\` with \`scenarioFile\` AND \`authHeader: "${authHeaderValue}"\`.
136
+ Do NOT pass \`chainingKey\` — defaults to \`response.id\`. After generation, the testbot
137
+ will verify and fix path param chaining in the generated test.
138
+
139
+ **For single-endpoint tests (contract/fuzz):**
140
+ \`skyramp_{type}_test_generation\` with \`endpointURL\` (full URL incl. base + path), \`method\`,
141
+ \`authHeader: "${authHeaderValue}"\`, and \`requestData\` from source code schemas.
142
+ If an OpenAPI spec exists, ALSO pass \`apiSchema\` — it enables schema-aware validation
143
+ (contract tests verify response structure, fuzz tests generate smarter boundary values).
144
+ Without a spec, \`endpointURL\` alone is sufficient.
145
+
146
+ **For UI tests (no Playwright recording):**
147
+ 1. \`skyramp_start_trace_collection\` (playwright: true)
148
+ 2. Perform browser steps
149
+ 3. \`skyramp_stop_trace_collection\`
150
+ 4. \`skyramp_ui_test_generation\` with playwright zip
151
+
152
+ **For E2E tests:**
153
+ Same trace flow, pass both trace file and playwright zip to \`skyramp_e2e_test_generation\`.`;
154
+ }
155
+ export function buildCoverageChecklist(openApiSpec, isUIOnlyPR, hasFrontendChanges, authHeaderValue, topN) {
156
+ const specNote = openApiSpec
157
+ ? `\n**OpenAPI Spec available**: \`${openApiSpec.path}\`
158
+ Use it actively:
159
+ - **Contract tests**: pass \`apiSchema: "${openApiSpec.path}"\` — the CLI validates response schemas against the spec.
160
+ - **Fuzz tests**: pass \`apiSchema: "${openApiSpec.path}"\` — the CLI generates boundary values from schema constraints.
161
+ - **Integration tests**: pass \`apiSchema\` to \`skyramp_scenario_test_generation\` — it extracts destination and request/response shapes.
162
+ - **Single-endpoint tests**: pass both \`endpointURL\` AND \`apiSchema\` for schema-aware generation.
163
+ \n`
164
+ : "";
165
+ const distribution = isUIOnlyPR
166
+ ? `- Prioritize UI tests (≥3), then E2E tests (≥2), then integration only if UI calls APIs. 0% smoke.`
167
+ : hasFrontendChanges
168
+ ? `- Mix: integration (2-3), E2E (1-2), UI (1-2), fuzz or contract (1). 0% smoke.`
169
+ : `- Mix: integration (2-3, multi-resource first), fuzz (1-2), contract (1-2), E2E (1 if user-facing flows exist). 0% smoke.`;
170
+ return `## Coverage Checklist
171
+ ${specNote}
172
+ ${isUIOnlyPR ? `**UI-only PR** — This PR has no backend changes. Focus on UI and E2E tests.
173
+
174
+ With Playwright traces: prioritize UI tests (one per changed component) and E2E tests
175
+ (one per page-level user flow). Integration tests are relevant only if the UI calls APIs.
176
+
177
+ Without traces: recommend UI/E2E tests with scenario steps and trace recording instructions
178
+ (\`skyramp_start_trace_collection\` with \`playwright: true\`). The testbot will skip generation
179
+ entirely for frontend-only PRs without traces — all recommendations become additional
180
+ recommendations in the report. Skip fuzz, contract, and smoke tests.
181
+ ` : `For each endpoint, recommend the most valuable test types — aim for variety:
182
+ 1. **Integration** — multi-resource workflows (not just single-resource CRUD)
183
+ 2. **Fuzz** — POST/PUT endpoints with request bodies (validates edge cases, type safety)
184
+ 3. **Contract** — endpoints with new/changed response schemas (validates structure)
185
+ 4. **E2E** — user flows spanning frontend to backend${hasFrontendChanges ? " (include at least 1 for this PR)" : ""}
186
+ 5. **UI** — changed frontend components${hasFrontendChanges ? " (include at least 1)" : ""}
187
+ 6. No smoke tests.
188
+ Do NOT recommend 7 integration tests — diversify across test types.
189
+ `}
190
+
191
+ ## For Each Recommendation Include:
192
+ 1. Test type 2. Priority (high/medium/low) 3. Target endpoint/scenario
193
+ 4. What it validates (business logic, not just "tests the endpoint")
194
+ 5. Skyramp tool call details — exact tool + key params for zero-editing execution
195
+ 6. For integration/E2E: reference draftedScenario by scenarioName
196
+
197
+ ## When Artifacts Are Missing
198
+ Recommend the test anyway — never mark it "blocked":
199
+ - **No OpenAPI spec** \u2192 use \`endpointURL\` and \`requestBody\` from source code
200
+ - **No Playwright recording** \u2192 provide trace recording instructions
201
+ - **No backend trace** \u2192 use the scenario generation pipeline
202
+
203
+ ## Select the Top ${topN}
204
+ Consider all possible tests (endpoints \u00d7 interaction types + scenarios), then select the
205
+ top ${topN} most valuable. Include \`totalConsidered\` count in your output. The top 4 will
206
+ be generated; recommendations #5-${topN} will appear in the report but won't be generated,
207
+ so ensure the top 4 are the highest-impact tests.
208
+
209
+ **Before outputting, verify:**
210
+ ${isUIOnlyPR ? `- If traces exist, at least 2 of the top 4 should be UI/E2E tests.
211
+ - Without traces, all 7 become additionalRecommendations (no generation). Rank UI/E2E highest.
212
+ - Avoid CRUD tests for unchanged resources the UI doesn't call.` : `- If the PR includes frontend changes, include at least 1 E2E/UI test in the top 4.
213
+ - CRUD tests for unchanged resources should not displace PR-relevant tests in the top 4.`}
214
+ - Each integration scenario's step sequence should be logically valid — preconditions
215
+ met by prior steps.
216
+
217
+ Preferred ordering: ${isUIOnlyPR ? "UI \u2192 E2E \u2192 integration (if UI calls APIs)." : "integration \u2192 fuzz \u2192 contract \u2192 E2E \u2192 UI."}
218
+ ${distribution}
219
+
220
+ Each recommendation should include enough detail for direct tool invocation.
221
+ Reference draftedScenarios by name and interactions by description.
222
+ Use "high"/"medium"/"low" for priority — no numeric scores.
223
+ Total candidates should be \u2265 ${topN}.
224
+
225
+ Generate recommendations now.`;
226
+ }
@@ -0,0 +1,71 @@
1
+ import { z } from "zod";
2
+ import { StateManager, getSessionFilePath, hasSessionData, getSessionData, } from "../../utils/AnalysisStateManager.js";
3
+ import { logger } from "../../utils/logger.js";
4
+ import { buildRecommendationPrompt } from "./test-recommendation-prompt.js";
5
+ import { getWorkspaceAuthHeader } from "../../utils/workspaceAuth.js";
6
+ export function registerRecommendTestsPrompt(server) {
7
+ server.registerPrompt("skyramp_recommend_tests", {
8
+ description: "Generate test recommendations from enriched repository analysis. " +
9
+ "Provide a sessionId from skyramp_analyze_repository.",
10
+ argsSchema: {
11
+ sessionId: z
12
+ .string()
13
+ .describe("Session ID from skyramp_analyze_repository"),
14
+ scope: z
15
+ .enum(["full_repo", "current_branch_diff"])
16
+ .default("full_repo")
17
+ .optional()
18
+ .describe("Analysis scope (defaults to the scope used during analysis)"),
19
+ focus: z
20
+ .enum(["all", "interactions", "scenarios"])
21
+ .default("all")
22
+ .optional()
23
+ .describe("Focus area: all tests, interaction-based (contract/fuzz), or scenario-based (integration/e2e)"),
24
+ },
25
+ }, async (args) => {
26
+ const sessionId = args.sessionId;
27
+ if (!sessionId) {
28
+ throw new Error("sessionId is required");
29
+ }
30
+ // Try process memory first, then fall back to state file
31
+ let data = null;
32
+ if (hasSessionData(sessionId)) {
33
+ data = getSessionData(sessionId);
34
+ }
35
+ else {
36
+ const registeredPath = getSessionFilePath(sessionId);
37
+ const mgr = registeredPath
38
+ ? StateManager.fromStatePath(registeredPath)
39
+ : StateManager.fromSessionId(sessionId);
40
+ if (!mgr.exists()) {
41
+ throw new Error(`Analysis session "${sessionId}" not found. Run skyramp_analyze_repository first.`);
42
+ }
43
+ data = await mgr.readData();
44
+ }
45
+ if (!data?.analysis) {
46
+ throw new Error(`Session "${sessionId}" has no analysis data.`);
47
+ }
48
+ const scope = args.scope || data.analysisScope || "full_repo";
49
+ const focus = args.focus || "all";
50
+ const effectiveTopN = scope === "current_branch_diff" ? 7 : 10;
51
+ const workspaceAuthHeader = data.repositoryPath
52
+ ? await getWorkspaceAuthHeader(data.repositoryPath)
53
+ : undefined;
54
+ const prompt = buildRecommendationPrompt(data.analysis, scope, focus, effectiveTopN, data.prContext, workspaceAuthHeader);
55
+ logger.info("Serving recommendation prompt via MCP Prompt", {
56
+ sessionId,
57
+ scope,
58
+ });
59
+ return {
60
+ messages: [
61
+ {
62
+ role: "user",
63
+ content: {
64
+ type: "text",
65
+ text: `Session: ${sessionId}\nRepository: ${data.repositoryPath}\nScope: ${scope}\n\nAvailable MCP Resources:\n- skyramp://analysis/${sessionId}/summary\n- skyramp://analysis/${sessionId}/endpoints\n- skyramp://analysis/${sessionId}/scenarios\n- skyramp://analysis/${sessionId}/diff\n\n${prompt}`,
66
+ },
67
+ },
68
+ ],
69
+ };
70
+ });
71
+ }