@skyramp/mcp 0.1.0-rc.3 → 0.1.0-rc.5
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/build/commands/recommendTestsAndExecuteCommand.js +3 -17
- package/build/commands/testThisEndpointCommand.js +20 -23
- package/build/index.js +15 -11
- package/build/playwright/traceRecordingPrompt.js +57 -19
- package/build/prompts/initialize-workspace/initializeWorkspacePrompt.js +1 -1
- package/build/prompts/personas.js +19 -0
- package/build/prompts/test-maintenance/drift-analysis-prompt.js +18 -2
- package/build/prompts/test-recommendation/analysisOutputPrompt.js +85 -26
- package/build/prompts/test-recommendation/recommendationSections.js +3 -2
- package/build/prompts/test-recommendation/registerRecommendTestsPrompt.js +5 -1
- package/build/prompts/test-recommendation/scopeAssessment.js +76 -0
- package/build/prompts/test-recommendation/scopeAssessment.test.js +76 -0
- package/build/prompts/test-recommendation/test-recommendation-prompt.js +92 -137
- package/build/prompts/test-recommendation/test-recommendation-prompt.test.js +157 -38
- package/build/prompts/testbot/testbot-prompts.js +29 -65
- package/build/prompts/testbot/testbot-prompts.test.js +9 -7
- package/build/resources/testbotResource.js +40 -0
- package/build/tool-phases.js +0 -2
- package/build/tools/executeSkyrampTestTool.js +42 -0
- package/build/tools/generate-tests/generateBatchScenarioRestTool.js +40 -15
- package/build/tools/generate-tests/generateBatchScenarioRestTool.test.js +1 -1
- package/build/tools/generate-tests/generateContractRestTool.js +42 -23
- package/build/tools/generate-tests/generateIntegrationRestTool.js +1 -1
- package/build/tools/generate-tests/generateLoadRestTool.js +4 -33
- package/build/tools/generate-tests/generateLoadRestTool.test.js +169 -0
- package/build/tools/generate-tests/generateMockRestTool.js +3 -2
- package/build/tools/generate-tests/generateUIRestTool.js +12 -5
- package/build/tools/generate-tests/loadTestSchema.js +32 -0
- package/build/tools/test-management/actionsTool.js +2 -2
- package/build/tools/test-management/analyzeChangesTool.js +36 -14
- package/build/tools/test-management/analyzeChangesTool.test.js +34 -1
- package/build/tools/test-management/analyzeTestHealthTool.js +5 -1
- package/build/tools/test-management/index.js +0 -2
- package/build/types/TestTypes.js +22 -4
- package/build/utils/AnalysisStateManager.test.js +2 -1
- package/build/utils/branchDiff.js +61 -29
- package/build/utils/projectMetadata.js +13 -1
- package/build/utils/repoScanner.js +131 -293
- package/build/utils/routeParsers.js +137 -30
- package/build/utils/routeParsers.test.js +154 -1
- package/build/utils/skyrampMdContent.js +14 -21
- package/node_modules/playwright/lib/mcp/skyramp/exportTool.js +11 -2
- package/package.json +1 -1
- package/build/prompts/architectPersona.js +0 -19
- package/build/tools/test-management/executeTestsTool.js +0 -255
- package/build/tools/test-management/stateCleanupTool.js +0 -163
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
* skyramp_analyze_changes (combined analyze + discover + recommend)
|
|
6
6
|
* → Generate tests for top N recommended types
|
|
7
7
|
* → Execute each via skyramp_execute_test
|
|
8
|
-
* → State cleanup
|
|
9
8
|
*/
|
|
10
9
|
const fullRepoRecommendGenerateExecuteTopNSteps = [
|
|
11
10
|
{
|
|
@@ -62,24 +61,11 @@ const fullRepoRecommendGenerateExecuteTopNSteps = [
|
|
|
62
61
|
},
|
|
63
62
|
conditionalGuidance: "Skip if step 2 generated no tests. Iterate over each generated test file path returned directly from the tools invoked in step 2 and call skyramp_execute_test once per file. Token resolution: (1) user-provided token; (2) token from .skyramp/workspace.yml or repo config; (3) empty string '' — let skyramp_execute_test surface auth errors, then ask the user for a Bearer token to re-run.",
|
|
64
63
|
},
|
|
65
|
-
{
|
|
66
|
-
stepIndex: 4,
|
|
67
|
-
title: "Clean up state files",
|
|
68
|
-
description: "Call skyramp_state_cleanup with action 'cleanup' and maxAgeHours set to 1 to remove temporary state files created by the recommendation toolset. These live in system temp (e.g. /tmp) — not in the user repo.",
|
|
69
|
-
toolCall: {
|
|
70
|
-
toolName: "skyramp_state_cleanup",
|
|
71
|
-
description: "Remove temporary state files from system temp",
|
|
72
|
-
inputs: {
|
|
73
|
-
action: { source: "literal", value: "cleanup" },
|
|
74
|
-
maxAgeHours: { source: "literal", value: 1 },
|
|
75
|
-
},
|
|
76
|
-
},
|
|
77
|
-
},
|
|
78
64
|
];
|
|
79
65
|
export const FULLREPO_RECOMMEND_GENERATE_EXECUTE_TOPN_TESTS_COMMAND = {
|
|
80
66
|
id: "full_repo_scan_recommend_generate_and_execute_top_n_tests",
|
|
81
67
|
name: "Full Repo: Recommend, Generate and Run TopN Tests",
|
|
82
|
-
description: "Run skyramp_analyze_changes to scan the repo and get ranked recommendations, generate tests for the top N recommended types, execute the generated tests
|
|
68
|
+
description: "Run skyramp_analyze_changes to scan the repo and get ranked recommendations, generate tests for the top N recommended types, then execute the generated tests.",
|
|
83
69
|
intent: {
|
|
84
70
|
contextIndicators: [
|
|
85
71
|
"Use when the user wants to scan the entire repository with no specific endpoint or PR diff in mind — to get ranked test recommendations across all endpoints, generate the top N recommended test types, and execute them",
|
|
@@ -89,8 +75,8 @@ export const FULLREPO_RECOMMEND_GENERATE_EXECUTE_TOPN_TESTS_COMMAND = {
|
|
|
89
75
|
"Do NOT use when the user asks about a PR diff or branch-scoped analysis — use skyramp_analyze_changes directly instead",
|
|
90
76
|
"Do NOT use for simple single-tool requests such as 'generate a smoke test' or 'recommend tests for this PR'",
|
|
91
77
|
],
|
|
92
|
-
purpose: "Full repo scan: get recommendations → Generate top N types → Execute generated tests
|
|
93
|
-
workflowSummary: "Full Repo Scan → Recommend → Generate top N → Execute each test
|
|
78
|
+
purpose: "Full repo scan: get recommendations → Generate top N types → Execute generated tests (no specific endpoint, no PR diff). Cleanup is handled automatically.",
|
|
79
|
+
workflowSummary: "Full Repo Scan → Recommend → Generate top N → Execute each test (cleanup is automatic)",
|
|
94
80
|
examples: {
|
|
95
81
|
use: [
|
|
96
82
|
"scan the full repo and recommend and execute top 3 tests",
|
|
@@ -7,7 +7,6 @@
|
|
|
7
7
|
* → Generate missing tests (by type)
|
|
8
8
|
* → Execute generated tests
|
|
9
9
|
* → [if existing tests found] Analyze test health → Optional batch execute → Actions
|
|
10
|
-
* → State cleanup
|
|
11
10
|
*/
|
|
12
11
|
const comprehensivelyTestGivenEndpointSteps = [
|
|
13
12
|
{
|
|
@@ -82,41 +81,39 @@ const comprehensivelyTestGivenEndpointSteps = [
|
|
|
82
81
|
},
|
|
83
82
|
outputs: ["stateFile"],
|
|
84
83
|
},
|
|
85
|
-
conditionalGuidance: "Only run when step 1 found existing tests specifically for the target endpoint. If no tests were found for the target endpoint, skip steps 5–7
|
|
84
|
+
conditionalGuidance: "Only run when step 1 found existing tests specifically for the target endpoint. If no tests were found for the target endpoint, skip steps 5–7.",
|
|
86
85
|
},
|
|
87
86
|
{
|
|
88
87
|
stepIndex: 6,
|
|
89
|
-
title: "Optional: execute existing tests
|
|
90
|
-
description: "Run only if step 5 ran. Optionally
|
|
88
|
+
title: "Optional: execute existing tests (only if step 5 ran)",
|
|
89
|
+
description: "Run only if step 5 ran. Optionally execute existing tests using skyramp_execute_test for each test file discovered in the stateFile. Extract test file paths, languages, and types from the stateFile (from step 1), then call skyramp_execute_test once per test with stateFile parameter to write results back. Use token from user or empty string. If you skip this step, proceed directly to step 7.",
|
|
91
90
|
toolCall: {
|
|
92
|
-
toolName: "
|
|
93
|
-
description: "Optionally run existing tests
|
|
91
|
+
toolName: "skyramp_execute_test",
|
|
92
|
+
description: "Optionally run existing tests individually; iterate over tests from stateFile and write results back",
|
|
94
93
|
inputs: {
|
|
95
|
-
|
|
96
|
-
|
|
94
|
+
workspacePath: { source: "user", paramKey: "repositoryPath" },
|
|
95
|
+
testFile: { source: "literal", value: "path from stateFile existingTests array" },
|
|
96
|
+
language: { source: "literal", value: "language from stateFile existingTests array" },
|
|
97
|
+
testType: { source: "literal", value: "testType from stateFile existingTests array" },
|
|
98
|
+
token: { source: "user", paramKey: "token" },
|
|
99
|
+
stateFile: { source: "step", stepIndex: 1, outputKey: "stateFile" },
|
|
97
100
|
},
|
|
98
|
-
outputs: [
|
|
101
|
+
outputs: [],
|
|
99
102
|
},
|
|
100
|
-
conditionalGuidance: "Only run when step 5 was executed. This step is optional — skip if
|
|
103
|
+
conditionalGuidance: "Only run when step 5 was executed. This step is optional — skip if execution is not needed. Read the stateFile from step 1 to get the list of existing tests (existingTests array), then iterate and call skyramp_execute_test once per test with its testFile, language, testType, AND stateFile (from step 1) so execution results are written back for health scoring in step 7.",
|
|
101
104
|
},
|
|
102
105
|
{
|
|
103
106
|
stepIndex: 7,
|
|
104
107
|
title: "Run maintenance actions (only if step 5 ran)",
|
|
105
|
-
description: "Run only if step 5 ran. Call skyramp_actions with the stateFile from step
|
|
106
|
-
conditionalGuidance: "Only run when step 5 was executed. Use step 6's stateFile if step 6 ran; use step 5's stateFile if step 6 was skipped. Call skyramp_actions with the resolved stateFile.",
|
|
107
|
-
},
|
|
108
|
-
{
|
|
109
|
-
stepIndex: 8,
|
|
110
|
-
title: "Clean up state files",
|
|
111
|
-
description: "Call skyramp_state_cleanup with action 'cleanup' and maxAgeHours set to 1 to remove temporary state files created by the analysis and maintenance toolsets. These live in system temp (e.g. /tmp) — not in the user repo.",
|
|
108
|
+
description: "Run only if step 5 ran. Call skyramp_actions with the stateFile from step 1 (which now contains execution results if step 6 ran, since skyramp_execute_test writes results back in-place). This applies recommended fixes (UPDATE/REGENERATE/VERIFY) to existing tests and generates tests for new endpoints. Call it immediately after the assessment without waiting for user confirmation.",
|
|
112
109
|
toolCall: {
|
|
113
|
-
toolName: "
|
|
114
|
-
description: "
|
|
110
|
+
toolName: "skyramp_actions",
|
|
111
|
+
description: "Apply recommended test maintenance actions",
|
|
115
112
|
inputs: {
|
|
116
|
-
|
|
117
|
-
maxAgeHours: { source: "literal", value: 1 },
|
|
113
|
+
stateFile: { source: "step", stepIndex: 1, outputKey: "stateFile" },
|
|
118
114
|
},
|
|
119
115
|
},
|
|
116
|
+
conditionalGuidance: "Only run when step 5 was executed. Always use stateFile from step 1 — if step 6 ran, it has updated this file in-place with execution results. The stateFile now contains all the context needed for execution-aware recommendations.",
|
|
120
117
|
},
|
|
121
118
|
];
|
|
122
119
|
export const TEST_GIVEN_ENDPOINT_COMPREHENSIVELY_COMMAND = {
|
|
@@ -131,8 +128,8 @@ export const TEST_GIVEN_ENDPOINT_COMPREHENSIVELY_COMMAND = {
|
|
|
131
128
|
"Do NOT use for broad repo-level requests where no specific endpoint is named — use skyramp_analyze_changes directly instead",
|
|
132
129
|
"Do NOT use for simple single-tool requests such as 'generate a smoke test for this endpoint' — those go directly to the generation tool",
|
|
133
130
|
],
|
|
134
|
-
purpose: "Deep test a given endpoint: discover existing → evaluate missing → generate missing → execute → (if existing found) health analysis → maintenance actions
|
|
135
|
-
workflowSummary: "Analyze Changes → Evaluate missing → Generate missing → Execute generated → [if existing] Test Health → Batch execute → Actions
|
|
131
|
+
purpose: "Deep test a given endpoint: discover existing → evaluate missing → generate missing → execute → (if existing found) health analysis → maintenance actions. Cleanup is handled automatically.",
|
|
132
|
+
workflowSummary: "Analyze Changes → Evaluate missing → Generate missing → Execute generated → [if existing] Test Health → Batch execute → Actions (cleanup is automatic)",
|
|
136
133
|
examples: {
|
|
137
134
|
use: [
|
|
138
135
|
"comprehensively test the products endpoint",
|
package/build/index.js
CHANGED
|
@@ -22,8 +22,9 @@ import { registerModularizationTool } from "./tools/code-refactor/modularization
|
|
|
22
22
|
import { registerCodeReuseTool } from "./tools/code-refactor/codeReuseTool.js";
|
|
23
23
|
import { registerBatchScenarioTestTool } from "./tools/generate-tests/generateBatchScenarioRestTool.js";
|
|
24
24
|
import { registerMockTool } from "./tools/generate-tests/generateMockRestTool.js";
|
|
25
|
-
import { registerAnalyzeChangesTool, registerAnalyzeTestHealthTool,
|
|
26
|
-
import { registerTestbotPrompt
|
|
25
|
+
import { registerAnalyzeChangesTool, registerAnalyzeTestHealthTool, registerActionsTool, } from "./tools/test-management/index.js";
|
|
26
|
+
import { registerTestbotPrompt } from "./prompts/testbot/testbot-prompts.js";
|
|
27
|
+
import { registerTestbotResource } from "./resources/testbotResource.js";
|
|
27
28
|
import { registerSubmitReportTool } from "./tools/submitReportTool.js";
|
|
28
29
|
import { registerInitializeWorkspaceTool } from "./tools/workspace/initializeWorkspaceTool.js";
|
|
29
30
|
import { registerInitScanWorkspaceTool } from "./tools/workspace/initScanWorkspaceTool.js";
|
|
@@ -33,6 +34,10 @@ import { registerProgressResource } from "./resources/progressResource.js";
|
|
|
33
34
|
import { AnalyticsService } from "./services/AnalyticsService.js";
|
|
34
35
|
import { registerInitTriggerOnMCPInitialized } from "./utils/initAgent.js";
|
|
35
36
|
import { registerPlaywrightTools, registerTraceRecordingPrompt, getPlaywrightTraceService, } from "./playwright/index.js";
|
|
37
|
+
const oneClickEnabled = process.env.SKYRAMP_FEATURE_ONE_CLICK === "1";
|
|
38
|
+
const oneClickInstructions = oneClickEnabled
|
|
39
|
+
? `\n- When the user asks to comprehensively, thoroughly, or deeply test a specific endpoint: MUST call \`skyramp_one_click_tool\` with workflow \`test_given_endpoint_comprehensively\` first. Do NOT self-orchestrate the steps manually.\n- When the user asks to scan the full repo, recommend, generate, and execute top N tests: MUST call \`skyramp_one_click_tool\` with workflow \`full_repo_scan_recommend_generate_and_execute_top_n_tests\`.`
|
|
40
|
+
: "";
|
|
36
41
|
const server = new McpServer({
|
|
37
42
|
name: "Skyramp MCP Server",
|
|
38
43
|
version: "1.0.0",
|
|
@@ -58,12 +63,10 @@ If the workspace root is a git repo AND \`.skyramp/workspace.yml\` does not exis
|
|
|
58
63
|
Skip only if: not a git repo, \`.skyramp/workspace.yml\` already exists, or user explicitly declines.
|
|
59
64
|
|
|
60
65
|
## Rules
|
|
61
|
-
- NEVER show CLI commands. ALWAYS use the MCP tools provided.
|
|
66
|
+
- NEVER show CLI commands. NEVER attempt to install or configure the Skyramp CLI. ALWAYS use the MCP tools provided.
|
|
62
67
|
- For UI and E2E tests, there are TWO recording modes:
|
|
63
68
|
1. **AI-driven recording** (default): Use the browser_* tools (browser_navigate, browser_click, etc.) to record interactions, then call skyramp_export_zip to export the trace, then call skyramp_ui_test_generation with the zip path.
|
|
64
|
-
2. **Manual recording**: ONLY when the user explicitly says "manual recording", "record myself", "I will interact", or "Docker trace" — use skyramp_start_trace_collection / skyramp_stop_trace_collection to let the user interact with the browser themselves
|
|
65
|
-
- When the user asks to comprehensively, thoroughly, or deeply test a specific endpoint: MUST call \`skyramp_one_click_tool\` with workflow \`test_given_endpoint_comprehensively\` first. Do NOT self-orchestrate the steps manually.
|
|
66
|
-
- When the user asks to scan the full repo, recommend, generate, and execute top N tests: MUST call \`skyramp_one_click_tool\` with workflow \`full_repo_scan_recommend_generate_and_execute_top_n_tests\`.
|
|
69
|
+
2. **Manual recording**: ONLY when the user explicitly says "manual recording", "record myself", "I will interact", or "Docker trace" — use skyramp_start_trace_collection / skyramp_stop_trace_collection to let the user interact with the browser themselves.${oneClickInstructions}
|
|
67
70
|
|
|
68
71
|
## Test Management Flow
|
|
69
72
|
Use \`skyramp_analyze_changes\` as the single entry point for both test recommendations and test health analysis.
|
|
@@ -75,8 +78,8 @@ Use \`skyramp_analyze_changes\` as the single entry point for both test recommen
|
|
|
75
78
|
### Health Analysis (4-step)
|
|
76
79
|
1. Call \`skyramp_analyze_changes\` with \`repositoryPath\` and \`scope\` → returns a \`stateFile\`.
|
|
77
80
|
2. Call \`skyramp_analyze_test_health\` with \`stateFile\` → runs drift analysis + health scoring + LLM semantic assessment.
|
|
78
|
-
3. (Optional)
|
|
79
|
-
4. Call \`skyramp_actions\` with \`stateFile\` → executes UPDATE/REGENERATE/ADD recommendations.
|
|
81
|
+
3. (Optional) Execute tests using \`skyramp_execute_test\` with \`stateFile\` param → validates test status live and writes results back to stateFile for health scoring.
|
|
82
|
+
4. Call \`skyramp_actions\` with \`stateFile\` → executes UPDATE/REGENERATE/ADD recommendations (with execution-aware prioritization if step 3 ran).
|
|
80
83
|
|
|
81
84
|
After \`skyramp_analyze_changes\`, inspect enriched data via MCP Resources (use the \`sessionId\` returned in the output):
|
|
82
85
|
- \`skyramp://analysis/{sessionId}/summary\` — high-level overview
|
|
@@ -146,14 +149,15 @@ registerProgressResource(server);
|
|
|
146
149
|
// Register unified test-management tools (replaces separate test-maintenance tools)
|
|
147
150
|
registerAnalyzeChangesTool(server);
|
|
148
151
|
registerAnalyzeTestHealthTool(server);
|
|
149
|
-
registerExecuteTestsTool(server);
|
|
150
152
|
registerActionsTool(server);
|
|
151
|
-
registerStateCleanupTool(server);
|
|
152
153
|
// Register workspace management tools
|
|
153
154
|
registerInitScanWorkspaceTool(server);
|
|
154
155
|
registerInitializeWorkspaceTool(server);
|
|
155
156
|
// Register one-click orchestrated workflows
|
|
156
|
-
|
|
157
|
+
if (oneClickEnabled) {
|
|
158
|
+
registerOneClickTool(server);
|
|
159
|
+
logger.info("One-click tools enabled via SKYRAMP_FEATURE_ONE_CLICK");
|
|
160
|
+
}
|
|
157
161
|
// Register other Skyramp tools
|
|
158
162
|
const infrastructureTools = [
|
|
159
163
|
registerLoginTool,
|
|
@@ -2,21 +2,24 @@
|
|
|
2
2
|
* MCP prompt that guides the LLM through the Playwright-based trace recording
|
|
3
3
|
* and Skyramp test generation flow.
|
|
4
4
|
*/
|
|
5
|
+
import { z } from "zod";
|
|
5
6
|
import { logger } from "../utils/logger.js";
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
7
|
+
import { SKYRAMP_QA_PERSONA } from "../prompts/personas.js";
|
|
8
|
+
export function getTraceRecordingPromptText(opts) {
|
|
9
|
+
const outputDir = opts?.outputDir;
|
|
10
|
+
const modularize = opts?.modularize ?? true;
|
|
11
|
+
const exportInstruction = outputDir
|
|
12
|
+
? `Call \`skyramp_export_zip\` with \`outputPath\` set to \`${outputDir}/<test_name>_trace.zip\` (absolute path).`
|
|
13
|
+
: `Call \`skyramp_export_zip\` with \`outputPath\` set to the absolute zip path (same directory and base name as the test file, replacing \`.spec.ts\` with \`.zip\`).`;
|
|
14
|
+
const generateInstruction = modularize
|
|
15
|
+
? `Call \`skyramp_ui_test_generation\` with \`playwrightInput\` set to the absolute zip path from the Export step.`
|
|
16
|
+
: `Call \`skyramp_ui_test_generation\` with \`playwrightInput\` set to the absolute zip path from step 5 and \`modularizeCode: false\`.`;
|
|
17
|
+
const modularizeNote = modularize
|
|
18
|
+
? `- **After generating the test**, run \`skyramp_modularization\` for code quality.`
|
|
19
|
+
: `- Do NOT run \`skyramp_modularization\` — skip modularization in CI.`;
|
|
20
|
+
return `## Skyramp UI Test Recording
|
|
18
21
|
|
|
19
|
-
|
|
22
|
+
${SKYRAMP_QA_PERSONA} For UI recording, every action must be grounded in what \`browser_snapshot\` returns. If an element is not visible in the snapshot, do not interact with it.
|
|
20
23
|
|
|
21
24
|
### Required workflow
|
|
22
25
|
|
|
@@ -28,25 +31,60 @@ Then execute in strict order:
|
|
|
28
31
|
2. **Snapshot**: Call \`browser_snapshot\` to get the current ARIA tree and element refs.
|
|
29
32
|
3. **Interact**: Call the appropriate tool (\`browser_click\`, \`browser_type\`, \`browser_hover\`, etc.) using refs from the snapshot.
|
|
30
33
|
4. **Repeat steps 2–3** for each user action until all steps are complete.
|
|
31
|
-
5. **Export**:
|
|
32
|
-
6. **Generate**:
|
|
34
|
+
5. **Export**: ${exportInstruction} Do NOT ask the user first — call it automatically.
|
|
35
|
+
6. **Generate**: ${generateInstruction}
|
|
33
36
|
|
|
34
37
|
### Cross-tool rules
|
|
35
38
|
|
|
36
39
|
- **After every action that changes the page**, call \`browser_snapshot\` before the next interaction — refs become stale after navigation, clicks that trigger page updates, and form submissions.
|
|
37
40
|
- **Iframe content** appears inline in the snapshot — interact with those elements using their refs normally.
|
|
38
41
|
- **Trace deduplication**: if you retry from the start URL, only the last complete attempt is exported.
|
|
39
|
-
- **
|
|
42
|
+
- **No Docker required**: the \`browser_*\` tools run a local browser session managed by the MCP server. Docker is ONLY used by \`skyramp_start_trace_collection\` (manual recording mode). Never suggest or check for Docker when using AI-driven recording.
|
|
43
|
+
${modularizeNote}
|
|
40
44
|
|
|
41
45
|
### Assertions
|
|
42
|
-
Call \`browser_assert\` when
|
|
46
|
+
Call \`browser_assert\` when assertions are needed. Always provide the \`expected\` value.
|
|
43
47
|
- \`type: "text"\` — verify an element contains expected text
|
|
44
48
|
- \`type: "value"\` — verify an input field has an expected value
|
|
45
49
|
|
|
50
|
+
When generating test code that uses \`expect\`, always import it from \`@skyramp/skyramp\`, never from \`@playwright/test\`:
|
|
51
|
+
\`\`\`ts
|
|
52
|
+
import { expect } from '@skyramp/skyramp';
|
|
53
|
+
\`\`\`
|
|
54
|
+
|
|
55
|
+
### Tips
|
|
56
|
+
- **Custom dropdowns (Radix, MUI, etc.)**: click the combobox trigger → \`browser_snapshot\` → click the option. Do NOT use \`browser_select_option\` — it only works on native \`<select>\` elements.
|
|
57
|
+
|
|
46
58
|
### Constraints
|
|
47
|
-
- Do NOT write JSONL or HAR files manually — \`skyramp_export_zip\`
|
|
59
|
+
- Do NOT write JSONL or HAR files manually — \`skyramp_export_zip\` reads the recorded trace, builds the JSONL action log and HAR, and packages them into the zip.
|
|
48
60
|
- Do NOT reuse zip files from previous sessions — always record fresh.
|
|
49
|
-
|
|
61
|
+
`;
|
|
62
|
+
}
|
|
63
|
+
export function registerTraceRecordingPrompt(server) {
|
|
64
|
+
logger.info("Registering trace recording prompt");
|
|
65
|
+
server.registerPrompt("skyramp_trace_recording_prompt", {
|
|
66
|
+
description: "Guide for recording browser interactions as a Skyramp trace and generating UI tests",
|
|
67
|
+
argsSchema: {
|
|
68
|
+
outputDir: z
|
|
69
|
+
.string()
|
|
70
|
+
.optional()
|
|
71
|
+
.describe("Directory where zip files should be written. Defaults to same directory as the test file."),
|
|
72
|
+
modularize: z
|
|
73
|
+
.boolean()
|
|
74
|
+
.default(true)
|
|
75
|
+
.optional()
|
|
76
|
+
.describe("Whether to run skyramp_modularization after generation. Default: true. Set to false in CI."),
|
|
77
|
+
},
|
|
78
|
+
}, (args) => ({
|
|
79
|
+
messages: [
|
|
80
|
+
{
|
|
81
|
+
role: "user",
|
|
82
|
+
content: {
|
|
83
|
+
type: "text",
|
|
84
|
+
text: getTraceRecordingPromptText({
|
|
85
|
+
outputDir: args.outputDir,
|
|
86
|
+
modularize: args.modularize,
|
|
87
|
+
}),
|
|
50
88
|
},
|
|
51
89
|
},
|
|
52
90
|
],
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { getPersonaPrefix } from "../
|
|
1
|
+
import { getPersonaPrefix } from "../personas.js";
|
|
2
2
|
export const INIT_WORKSPACE_INSTRUCTIONS = `${getPersonaPrefix()}Your task is to scan this repository, discover ALL services, and call the \`skyramp_init_workspace\` tool with the discovered services array and the scanToken.
|
|
3
3
|
|
|
4
4
|
After scanning the workspace, before calling the \`skyramp_init_workspace\` tool, you MUST:
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skyramp personas injected into tool descriptions and prompts.
|
|
3
|
+
*
|
|
4
|
+
* In TestBot environments (ENABLE_SKYRAMP_TESTBOT=true), the persona is injected
|
|
5
|
+
* once as a system prompt via `claude --system-prompt` rather than repeating it in
|
|
6
|
+
* every tool description. In that case getPersonaPrefix() returns empty string
|
|
7
|
+
* to avoid wasting context tokens.
|
|
8
|
+
*
|
|
9
|
+
* In IDE/MCP-direct environments, it is included in each tool description so the
|
|
10
|
+
* model has the role context available without a separate system prompt.
|
|
11
|
+
*/
|
|
12
|
+
export const SKYRAMP_QA_PERSONA = `You are acting as a Skyramp QA Automation Engineer. Your responsibility is to translate user test intent into precise, deterministic test artifacts — whether generating API tests from specs, recording browser interactions for UI flows, or maintaining existing test suites. Derive all parameters strictly from the codebase, workspace config, API schemas, and page snapshots. Never guess or hallucinate values.`;
|
|
13
|
+
/**
|
|
14
|
+
* Returns the persona prefix for use in tool descriptions.
|
|
15
|
+
* Returns an empty string when running inside TestBot (persona is injected via system prompt instead).
|
|
16
|
+
*/
|
|
17
|
+
export function getPersonaPrefix() {
|
|
18
|
+
return process.env.SKYRAMP_FEATURE_TESTBOT ? '' : `${SKYRAMP_QA_PERSONA}\n\n`;
|
|
19
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { buildDriftScoringGuide, buildActionDecisionMatrix, buildBreakingChangePatterns, buildTestAssessmentGuidelines, buildAddRecommendationGuidelines, buildDriftOutputChecklist, buildUpdateExecutionRules, } from "./driftAnalysisSections.js";
|
|
2
2
|
export function buildDriftAnalysisPrompt(params) {
|
|
3
|
-
const { existingTests, parsedDiff, scannedEndpoints, repositoryPath, stateFile } = params;
|
|
3
|
+
const { existingTests, parsedDiff, scannedEndpoints, repositoryPath, stateFile, routerMountContext, candidateRouteFiles } = params;
|
|
4
4
|
const inlineMode = !stateFile;
|
|
5
5
|
// Detect new endpoints count from parsedDiff
|
|
6
6
|
let newEndpointCount = 0;
|
|
@@ -30,6 +30,7 @@ No existing Skyramp tests found in repository.
|
|
|
30
30
|
`;
|
|
31
31
|
const scannedSection = scannedEndpoints.length > 0
|
|
32
32
|
? `## Scanned Endpoints (${scannedEndpoints.length})
|
|
33
|
+
Note: paths below come from static analysis and may be incomplete for nested resources or unsupported frameworks. Use the Routing entry-point files section below to verify and reconstruct full paths.
|
|
33
34
|
${scannedEndpoints.map((ep) => {
|
|
34
35
|
let methods;
|
|
35
36
|
if (Array.isArray(ep.methods)) {
|
|
@@ -40,6 +41,19 @@ ${scannedEndpoints.map((ep) => {
|
|
|
40
41
|
}
|
|
41
42
|
return `- ${methods} ${ep.path}`;
|
|
42
43
|
}).join("\n")}
|
|
44
|
+
`
|
|
45
|
+
: "";
|
|
46
|
+
const mountSection = routerMountContext?.length
|
|
47
|
+
? `## Routing entry-point files
|
|
48
|
+
Read these to trace the full router/module hierarchy when verifying endpoint paths:
|
|
49
|
+
${routerMountContext.map(f => `- \`${f}\``).join("\n")}
|
|
50
|
+
`
|
|
51
|
+
: "";
|
|
52
|
+
const hasJavaFiles = candidateRouteFiles?.some(f => /\.(java|kt)$/.test(f)) ?? false;
|
|
53
|
+
const candidateFilesSection = candidateRouteFiles && candidateRouteFiles.length > 0
|
|
54
|
+
? `## Route Files (read these to find endpoints from any framework)
|
|
55
|
+
${candidateRouteFiles.map(f => `- ${f}`).join("\n")}
|
|
56
|
+
${hasJavaFiles ? "Note — Java Spring: full URL = class-level `@RequestMapping` prefix + method-level path. If the prefix is a constant reference (e.g. `@RequestMapping(Url.PAGE_URL)`), find the constant — same file, inner class, or a separate `Url.java` — and resolve it (including `+` concatenation)." : ""}
|
|
43
57
|
`
|
|
44
58
|
: "";
|
|
45
59
|
// In inline mode (testbot), skip the context header — existing tests and diff
|
|
@@ -54,7 +68,9 @@ ${scannedEndpoints.map((ep) => {
|
|
|
54
68
|
|
|
55
69
|
${diffSection}
|
|
56
70
|
${testListSection}
|
|
57
|
-
${scannedSection}
|
|
71
|
+
${scannedSection}
|
|
72
|
+
${mountSection}
|
|
73
|
+
${candidateFilesSection}`;
|
|
58
74
|
if (inlineMode) {
|
|
59
75
|
// Testbot inline mode: all maintenance logic lives here so the testbot
|
|
60
76
|
// prompt only orchestrates steps without duplicating rules.
|
|
@@ -1,4 +1,44 @@
|
|
|
1
1
|
import { AnalysisScope } from "../../types/RepositoryAnalysis.js";
|
|
2
|
+
// File extensions that indicate a frontend-only file — no API route definitions.
|
|
3
|
+
// Uses extensions rather than path-segment patterns so backend files named
|
|
4
|
+
// e.g. Spring /services/Foo.java or Gin handlers/main.go are not misclassified.
|
|
5
|
+
// Keep this narrow: .json/.md can contain backend-affecting artifacts (OpenAPI specs,
|
|
6
|
+
// config, API docs) and would incorrectly classify a PR as UI-only.
|
|
7
|
+
const FRONTEND_EXT = /\.(tsx?|jsx?|vue|svelte|css|scss|less|html|svg)$/i;
|
|
8
|
+
/**
|
|
9
|
+
* Returns a Step 1.5 instruction block that forces the LLM to read the
|
|
10
|
+
* entry-point files from the Router Mounting section and build an authoritative
|
|
11
|
+
* file→full-prefix table before touching any endpoint URLs.
|
|
12
|
+
* Returned as an empty string when no router context is available.
|
|
13
|
+
*/
|
|
14
|
+
function buildPathResolutionTableStep(p) {
|
|
15
|
+
if (!p.routerMountContext.length || p.wsSchemaPath)
|
|
16
|
+
return "";
|
|
17
|
+
return `### Step 1.5: Build path resolution table
|
|
18
|
+
The **Routing entry-point files** section above lists the files to read.
|
|
19
|
+
|
|
20
|
+
**Read each of those files** and trace every router mount call to understand nesting — the pattern varies by framework but the structure is universal: a parent attaches a child router with an optional extra prefix segment. If a prefix is a variable (e.g. \`prefix=api_prefix\`), resolve the variable's value by reading the assignment or the config/settings file it comes from. Examples of what to look for (non-exhaustive):
|
|
21
|
+
- Python (FastAPI/Flask): \`parent.include_router(child, prefix="...")\`, \`app.register_blueprint(...)\`
|
|
22
|
+
- JS/TS (Express/Fastify/Hapi): \`app.use('/path', childRouter)\`, \`router.use('/path', sub)\`
|
|
23
|
+
- NestJS: \`@Module({ imports: [FeatureModule] })\` — trace the module import chain; each \`@Controller('prefix')\` contributes a segment
|
|
24
|
+
- Go (Gin/Echo/Chi): \`r.Group('/path')\`, \`r.Mount('/path', sub)\`
|
|
25
|
+
- Ruby (Rails): \`namespace\`, \`scope\`, \`resources ... do\`
|
|
26
|
+
- Django: \`path('prefix/', include(urls))\`
|
|
27
|
+
|
|
28
|
+
Chain all segments from the app root down through every intermediate mount to each leaf router file. Build a table:
|
|
29
|
+
|
|
30
|
+
| Source file | Full URL prefix |
|
|
31
|
+
|-------------|----------------|
|
|
32
|
+
| (leaf router file) | (fully chained prefix, e.g. /api/v1/products/{id}/reviews) |
|
|
33
|
+
|
|
34
|
+
**This table is authoritative.** Before placing any URL in a tool call, look up the source file. If the pre-built catalog shows a different path, use the table value.
|
|
35
|
+
|
|
36
|
+
`;
|
|
37
|
+
}
|
|
38
|
+
// Inline note added to any step where the LLM reads Java source files. Java Spring
|
|
39
|
+
// has no router-mounting file — each controller defines its own class-level prefix,
|
|
40
|
+
// and that prefix may reference a constant defined elsewhere.
|
|
41
|
+
const JAVA_SPRING_NOTE = `For Java Spring: full URL = class-level \`@RequestMapping\` prefix + method-level path. If the prefix is a constant reference (e.g. \`@RequestMapping(Url.PAGE_URL)\`), find the constant — same file, inner class, or a separate \`Url.java\` — and resolve it (including \`+\` concatenation).`;
|
|
2
42
|
function buildEnrichmentInstructions(p) {
|
|
3
43
|
const isDiffScope = p.analysisScope === AnalysisScope.CurrentBranchDiff;
|
|
4
44
|
const useHealthFlow = p.nextTool === "skyramp_analyze_test_health";
|
|
@@ -16,38 +56,59 @@ The ranked test recommendation catalog is pre-built and shown below (after the s
|
|
|
16
56
|
3. Do NOT call any Skyramp generation tools. The catalog shows ready-to-use tool calls that can be executed on demand.
|
|
17
57
|
|
|
18
58
|
**If** Steps 1–2 revealed additional scenarios the catalog does not cover (e.g. a computed formula or FK relationship that was missed), you may optionally call \`skyramp_recommend_tests\` with \`stateFile: "${p.stateFile ?? p.sessionId}"\` and \`enrichedScenarios\` to regenerate a more complete catalog — but only after presenting the current one.`;
|
|
59
|
+
const hasJavaFiles = p.candidateRouteFiles?.some(f => /\.(java|kt)$/.test(f)) ?? false;
|
|
60
|
+
const routeFilesSection = p.candidateRouteFiles && p.candidateRouteFiles.length > 0
|
|
61
|
+
? `\nRoute/controller files found by static scan (read these to discover endpoints — the regex-based catalog below may be incomplete for your framework):\n${p.candidateRouteFiles.map(f => `- ${f}`).join("\n")}\n`
|
|
62
|
+
: "";
|
|
63
|
+
const resolvePathsNote = p.routerMountContext.length
|
|
64
|
+
? `**Resolve nested paths** using your Step 1.5 table — a router in the table with prefix \`/api/v1/products/{product_id}/reviews\` means every endpoint in that file lives under that full path.`
|
|
65
|
+
: `**Resolve full paths** using the prefixes you identified in Step 1 (e.g. Java Spring class-level \`@RequestMapping\` prefix + method-level path).`;
|
|
19
66
|
return `## Your Task — Fill in and Present the Catalog (full repo)
|
|
20
67
|
|
|
21
68
|
### Step 1: Read key files
|
|
22
|
-
Read route/controller files and model/schema files (Pydantic models, Zod schemas, DTOs)
|
|
23
|
-
|
|
69
|
+
${routeFilesSection}Read the route/controller files above **and** model/schema files (Pydantic models, Zod schemas, DTOs) to find: required request body fields, computed response fields and formulas, auth middleware type, storage backend, and how sub-routers are mounted (cross-check against Router Mounting section above).
|
|
70
|
+
${hasJavaFiles ? JAVA_SPRING_NOTE : ""}
|
|
71
|
+
If the endpoint catalog below is missing endpoints visible in these files (e.g. from a framework the static scanner doesn't recognise), extract them now and include them in Step 3's \`enrichedScenarios\`.
|
|
24
72
|
|
|
25
|
-
### Step 2: Map cross-resource relationships and resolve endpoint paths
|
|
73
|
+
${buildPathResolutionTableStep(p)}### Step 2: Map cross-resource relationships and resolve endpoint paths
|
|
26
74
|
(Distinct from Step 1 — Step 1 reads individual schemas; Step 2 maps how endpoints relate to each other.)
|
|
27
75
|
For each endpoint: which POST creates resources consumed by other endpoints?
|
|
28
|
-
|
|
76
|
+
${resolvePathsNote}
|
|
29
77
|
For GET list endpoints: identify query params (\`limit\`, \`offset\`, \`order\`, \`orderBy\`) from framework annotations (FastAPI \`Query()\`, Express \`req.query\`, etc.).
|
|
30
78
|
|
|
31
79
|
${nextStep}`;
|
|
32
80
|
}
|
|
33
81
|
const changedFiles = p.parsedDiff?.changedFiles.join(", ") ?? "";
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
82
|
+
// Whether the regex pre-detected any API endpoints — used as a hint only.
|
|
83
|
+
// Step 2 always asks the LLM to extract endpoints from the diff so unknown
|
|
84
|
+
// frameworks (e.g. Spring class-level @RequestMapping, Django, Rails) are
|
|
85
|
+
// covered even when the static regex returns nothing.
|
|
86
|
+
const regexFoundEndpoints = p.parsedDiff && (p.parsedDiff.newEndpoints.length > 0 || p.parsedDiff.modifiedEndpoints.length > 0);
|
|
87
|
+
const diffFiles = p.parsedDiff?.changedFiles ?? [];
|
|
88
|
+
const isUIOnly = diffFiles.length > 0 &&
|
|
89
|
+
!regexFoundEndpoints &&
|
|
90
|
+
diffFiles.every(f => FRONTEND_EXT.test(f));
|
|
91
|
+
const diffHasJavaFiles = diffFiles.some(f => /\.(java|kt)$/.test(f));
|
|
92
|
+
const diffSection = p.diffContent
|
|
93
|
+
? `\n<diff>\n${p.diffContent}\n</diff>`
|
|
94
|
+
: "";
|
|
95
|
+
const step2 = isUIOnly
|
|
96
|
+
? `### Step 2: Identify consumed API endpoints
|
|
97
|
+
UI-only PR — read changed components to find API calls (fetch, axios, hooks).`
|
|
98
|
+
: p.diffContent
|
|
99
|
+
? `### Step 2: Extract new and modified API endpoints from the diff
|
|
100
|
+
Read the \`<diff>\` above and identify every new or modified API endpoint — route registrations, handler methods, controller annotations. Then use the **Router Mounting / Nesting** section above to reconstruct the full URL path for each endpoint by chaining all parent router prefixes down to the handler (e.g. a handler in a file with \`prefix="/reviews"\` that is mounted at \`/{product_id}\` under a router mounted at \`/api/v1/products\` → full path \`/api/v1/products/{product_id}/reviews\`).
|
|
101
|
+
${diffHasJavaFiles ? JAVA_SPRING_NOTE : ""}
|
|
102
|
+
For each endpoint found: note the HTTP method, full path, and source file.
|
|
103
|
+
${regexFoundEndpoints ? "The static analysis above pre-detected some endpoints — verify and augment with anything it missed." : "The static analysis did not detect endpoints for this framework — rely on the diff to extract them."}
|
|
42
104
|
**CRITICAL — Query params vs body:** For GET endpoints (especially search/filter/list),
|
|
43
105
|
identify which parameters are URL query params vs request body. Look at framework-specific
|
|
44
106
|
annotations (FastAPI \`Query()\`, Express \`req.query\`, Spring \`@RequestParam\`, etc.).
|
|
45
107
|
Pass these as \`queryParams\` (not \`requestBody\`) when generating scenarios.`
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
No API route changes detected — read changed files to identify affected endpoints.`;
|
|
108
|
+
: `### Step 2: Extract new and modified API endpoints from source files
|
|
109
|
+
No diff was available — read the changed source files listed above directly to identify new or modified API endpoints. Use the **Router Mounting / Nesting** section to reconstruct full paths.
|
|
110
|
+
${diffHasJavaFiles ? JAVA_SPRING_NOTE : ""}
|
|
111
|
+
For each endpoint found: note the HTTP method, full path, and source file.`;
|
|
51
112
|
const criticalPatternStep = `### Step 2.5: Identify critical patterns for test categorization
|
|
52
113
|
Look for these patterns in model/schema/handler files to inform test recommendations:
|
|
53
114
|
- **Unique constraints**: \`@unique\`, \`unique: true\`, unique indexes, \`.refine()\` uniqueness checks, \`UNIQUE\` in SQL migrations
|
|
@@ -88,10 +149,10 @@ Call \`skyramp_recommend_tests\` with:
|
|
|
88
149
|
- \`enrichedScenarios\`: (optional) JSON array of your Step 3 scenarios — see the tool's inputSchema for the exact shape. Your enriched scenarios override server-side ones with the same \`scenarioName\` and are prioritized in ranking. Omit if you drafted nothing in Step 3.`;
|
|
89
150
|
return `## Your Task — Enrich & Recommend (PR-scoped)
|
|
90
151
|
|
|
91
|
-
### Step 1: Read the changed files
|
|
92
|
-
${changedFiles}
|
|
152
|
+
### Step 1: Read the changed files and diff
|
|
153
|
+
${changedFiles}${diffSection}
|
|
93
154
|
|
|
94
|
-
${step2}
|
|
155
|
+
${buildPathResolutionTableStep(p)}${step2}
|
|
95
156
|
|
|
96
157
|
${criticalPatternStep}
|
|
97
158
|
|
|
@@ -103,13 +164,11 @@ export function buildAnalysisOutputText(p) {
|
|
|
103
164
|
// Branch diff, endpoint catalog, auth config, and OpenAPI spec are omitted here
|
|
104
165
|
// because they are already present in the recommendation prompt that is
|
|
105
166
|
// concatenated in the same tool response.
|
|
106
|
-
const routerSection = !p.wsSchemaPath && p.routerMountContext
|
|
167
|
+
const routerSection = !p.wsSchemaPath && p.routerMountContext.length
|
|
107
168
|
? `
|
|
108
|
-
##
|
|
109
|
-
|
|
110
|
-
${p.routerMountContext}
|
|
111
|
-
\`\`\`
|
|
112
|
-
Use this to resolve full URL paths for nested endpoints.`
|
|
169
|
+
## Routing entry-point files
|
|
170
|
+
Read these in Step 1.5 to trace the full router/module hierarchy:
|
|
171
|
+
${p.routerMountContext.map(f => `- \`${f}\``).join("\n")}`
|
|
113
172
|
: "";
|
|
114
173
|
const enrichment = buildEnrichmentInstructions(p);
|
|
115
174
|
return `# Repository Analysis
|
|
@@ -156,9 +156,10 @@ When no Playwright trace exists, use the Playwright browser tools (\`browser_nav
|
|
|
156
156
|
recommend a different test that adds new coverage.`;
|
|
157
157
|
}
|
|
158
158
|
export function buildVerificationChecklist(topN, maxGen) {
|
|
159
|
+
const minTotal = Math.min(maxGen + 1, topN);
|
|
159
160
|
return `<verification>
|
|
160
161
|
Before finalizing your output, verify:
|
|
161
|
-
1. **Count**: Total recommendation count equals
|
|
162
|
+
1. **Count**: Total recommendation count equals the total you stated in your Budget Plan (between ${minTotal} and ${topN}). Your GENERATE + ADDITIONAL counts must match the split you committed to. Not fewer than your stated Budget Plan total.
|
|
162
163
|
2. **Distinct paths**: Each GENERATE item targets a distinct code path — no two share the same HTTP method + endpoint + expected status.
|
|
163
164
|
3. **Auth parameters are consistent** across all tool calls (same authHeader and authScheme).
|
|
164
165
|
4. Every endpointURL includes both the base URL and the path (not just the base, e.g. \`http://host/api/v1/orders/{id}\`).
|
|
@@ -334,7 +335,7 @@ ${PATH_PARAM_UUID_GUIDANCE}
|
|
|
334
335
|
3. Interact using \`browser_click\`, \`browser_type\`, \`browser_fill_form\`, etc.
|
|
335
336
|
4. \`browser_snapshot\` after each interaction that changes the page
|
|
336
337
|
5. \`skyramp_export_zip\` with an **absolute** output path: \`<repositoryPath>/.skyramp/<test_name>_trace.zip\`
|
|
337
|
-
6. \`skyramp_ui_test_generation\` with \`playwrightInput\` = the **absolute** path of the exported zip
|
|
338
|
+
6. \`skyramp_ui_test_generation\` with \`playwrightInput\` = the **absolute** path of the exported zip, and \`outputDir\` = the **frontend** service's \`testDirectory\` from workspace.yml (e.g. \`frontend/tests\`). Do NOT use the backend service's testDirectory — UI tests must go in the frontend service's test directory.
|
|
338
339
|
|
|
339
340
|
Tips: For custom dropdowns (Radix, MUI): click combobox → snapshot → click option (NOT \`browser_select_option\`).
|
|
340
341
|
|
|
@@ -158,7 +158,11 @@ export function registerRecommendTestsPrompt(server) {
|
|
|
158
158
|
if (!fullAnalysis) {
|
|
159
159
|
throw new Error(`Analysis data for session not found in memory or on disk. Re-run skyramp_analyze_changes.`);
|
|
160
160
|
}
|
|
161
|
-
|
|
161
|
+
// Normalize legacy state files: before AnalysisScope enum normalization, state stored
|
|
162
|
+
// the user-facing param value "branch_diff". Map it explicitly so diff-mode detection
|
|
163
|
+
// works correctly on state created before this deployment (2-hour TTL window).
|
|
164
|
+
const rawScope = state.analysisScope;
|
|
165
|
+
const analysisScope = rawScope === "branch_diff" || rawScope === AnalysisScope.CurrentBranchDiff
|
|
162
166
|
? AnalysisScope.CurrentBranchDiff
|
|
163
167
|
: AnalysisScope.FullRepo;
|
|
164
168
|
const effectiveTopN = args.topN;
|