@skyramp/mcp 0.2.8 → 0.2.150-rc.ldw-

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 (90) hide show
  1. package/build/commands/commandLibrary.d.ts +1 -0
  2. package/build/commands/commandLibrary.js +17 -14
  3. package/build/commands/localDevTestChangesCommand.d.ts +15 -0
  4. package/build/commands/localDevTestChangesCommand.js +188 -0
  5. package/build/index.js +74 -13
  6. package/build/prompts/enhance-assertions/sharedAssertionRules.d.ts +1 -0
  7. package/build/prompts/enhance-assertions/sharedAssertionRules.js +17 -0
  8. package/build/prompts/initialize-workspace/initializeWorkspacePrompt.js +7 -6
  9. package/build/prompts/local-dev/local-dev-plan.d.ts +25 -0
  10. package/build/prompts/local-dev/local-dev-plan.js +413 -0
  11. package/build/prompts/local-dev/local-dev-prompts.d.ts +4 -0
  12. package/build/prompts/local-dev/local-dev-prompts.js +155 -0
  13. package/build/prompts/prompt-utils.d.ts +8 -0
  14. package/build/prompts/prompt-utils.js +33 -0
  15. package/build/prompts/sut-setup/modes/adaptWorkflowPrompt.js +3 -1
  16. package/build/prompts/sut-setup/modes/dockerComposePrompt.js +3 -1
  17. package/build/prompts/sut-setup/shared.d.ts +20 -0
  18. package/build/prompts/sut-setup/shared.js +69 -7
  19. package/build/prompts/test-recommendation/analysisOutputPrompt.js +21 -29
  20. package/build/prompts/test-recommendation/analysisOutputPrompt.test.js +0 -28
  21. package/build/prompts/test-recommendation/test-recommendation-prompt.js +41 -4
  22. package/build/prompts/test-recommendation/test-recommendation-prompt.test.js +16 -1
  23. package/build/prompts/testbot/testbot-prompts.d.ts +0 -5
  24. package/build/prompts/testbot/testbot-prompts.js +4 -34
  25. package/build/resources/testbotResource.js +2 -1
  26. package/build/services/TestExecutionService.d.ts +2 -1
  27. package/build/services/TestExecutionService.js +9 -9
  28. package/build/services/TestExecutionService.test.js +42 -1
  29. package/build/services/TestGenerationService.d.ts +2 -2
  30. package/build/tool-phases.js +4 -0
  31. package/build/tools/code-refactor/enhanceAssertionsTool.js +42 -18
  32. package/build/tools/enrichTestWithMocksTool.d.ts +15 -0
  33. package/build/tools/enrichTestWithMocksTool.js +413 -0
  34. package/build/tools/enrichTestWithMocksTool.test.d.ts +1 -0
  35. package/build/tools/enrichTestWithMocksTool.test.js +60 -0
  36. package/build/tools/executeSkyrampTestTool.d.ts +4 -0
  37. package/build/tools/executeSkyrampTestTool.js +39 -18
  38. package/build/tools/executeSkyrampTestTool.test.d.ts +1 -0
  39. package/build/tools/executeSkyrampTestTool.test.js +16 -0
  40. package/build/tools/generate-tests/batchMockGenerationTool.d.ts +79 -0
  41. package/build/tools/generate-tests/batchMockGenerationTool.js +395 -0
  42. package/build/tools/generate-tests/generateContractRestTool.js +2 -2
  43. package/build/tools/generate-tests/generateMockRestTool.d.ts +56 -6
  44. package/build/tools/generate-tests/generateMockRestTool.js +193 -21
  45. package/build/tools/generateEnrichedIntegrationTestTool.d.ts +24 -0
  46. package/build/tools/generateEnrichedIntegrationTestTool.js +209 -0
  47. package/build/tools/localDevWorkflowFixes.test.d.ts +1 -0
  48. package/build/tools/localDevWorkflowFixes.test.js +144 -0
  49. package/build/tools/one-click/oneClickTool.d.ts +10 -0
  50. package/build/tools/one-click/oneClickTool.js +177 -21
  51. package/build/tools/one-click/oneClickTool.test.d.ts +1 -0
  52. package/build/tools/one-click/oneClickTool.test.js +172 -0
  53. package/build/tools/preflightMockCheckTool.d.ts +2 -0
  54. package/build/tools/preflightMockCheckTool.js +91 -0
  55. package/build/tools/test-management/analyzeChangesTool.d.ts +2 -1
  56. package/build/tools/test-management/analyzeChangesTool.js +59 -38
  57. package/build/tools/workspace/initializeWorkspaceTool.js +2 -9
  58. package/build/tools/workspace/initializeWorkspaceTool.test.js +9 -4
  59. package/build/types/OneClickCommands.d.ts +1 -1
  60. package/build/types/RepositoryAnalysis.d.ts +117 -0
  61. package/build/types/RepositoryAnalysis.js +16 -2
  62. package/build/types/TestTypes.d.ts +11 -2
  63. package/build/types/TestTypes.js +11 -1
  64. package/build/utils/analyze-openapi.js +18 -1
  65. package/build/utils/analyze-openapi.test.d.ts +1 -0
  66. package/build/utils/analyze-openapi.test.js +19 -0
  67. package/build/utils/branchDiff.d.ts +17 -1
  68. package/build/utils/branchDiff.js +96 -14
  69. package/build/utils/branchDiff.test.d.ts +1 -0
  70. package/build/utils/branchDiff.test.js +107 -0
  71. package/build/utils/docker.test.js +1 -1
  72. package/build/utils/featureFlags.d.ts +15 -1
  73. package/build/utils/featureFlags.js +19 -2
  74. package/build/utils/featureFlags.test.js +57 -2
  75. package/build/utils/grpcMockValidation.d.ts +1 -0
  76. package/build/utils/grpcMockValidation.js +49 -0
  77. package/build/utils/grpcMockValidation.test.d.ts +1 -0
  78. package/build/utils/grpcMockValidation.test.js +41 -0
  79. package/build/utils/httpMethodValidation.d.ts +4 -0
  80. package/build/utils/httpMethodValidation.js +13 -0
  81. package/build/utils/mockCompatibility.d.ts +49 -0
  82. package/build/utils/mockCompatibility.js +80 -0
  83. package/build/utils/mockCompatibility.test.d.ts +1 -0
  84. package/build/utils/mockCompatibility.test.js +88 -0
  85. package/build/utils/versions.d.ts +3 -3
  86. package/build/utils/versions.js +1 -1
  87. package/build/workspace/workspace.d.ts +35 -21
  88. package/build/workspace/workspace.js +8 -4
  89. package/build/workspace/workspace.test.js +65 -6
  90. package/package.json +2 -2
@@ -10,3 +10,4 @@ export declare function getCommandIds(): OneClickCommandId[];
10
10
  export declare function lookupCommand(id: string): OneClickCommandDef;
11
11
  export { TEST_GIVEN_ENDPOINT_COMPREHENSIVELY_COMMAND } from "./testThisEndpointCommand.js";
12
12
  export { FULLREPO_RECOMMEND_GENERATE_EXECUTE_TOPN_TESTS_COMMAND } from "./recommendTestsAndExecuteCommand.js";
13
+ export { LOCAL_DEV_TEST_CHANGES_COMMAND } from "./localDevTestChangesCommand.js";
@@ -1,30 +1,32 @@
1
+ import { isLocalDevEnabled } from "../utils/featureFlags.js";
1
2
  import { TEST_GIVEN_ENDPOINT_COMPREHENSIVELY_COMMAND } from "./testThisEndpointCommand.js";
2
3
  import { FULLREPO_RECOMMEND_GENERATE_EXECUTE_TOPN_TESTS_COMMAND } from "./recommendTestsAndExecuteCommand.js";
3
- /**
4
- * To add a new predefined workflow:
5
- * 1. Add a new id to OneClickCommandId in types/OneClickCommands.ts
6
- * 2. Create a new file in commands/ (e.g. myWorkflowCommand.ts) with steps and export the command
7
- * 3. Import the command here and add it to COMMAND_LIBRARY
8
- * 4. The generic buildWorkflowFromCommand() will render detailed instructions from your steps
9
- * (no schema changes needed in oneClickTool.ts — the workflow param description is generated dynamically)
10
- */
11
- /** All predefined one-click commands */
12
- const COMMAND_LIBRARY = {
4
+ import { LOCAL_DEV_TEST_CHANGES_COMMAND } from "./localDevTestChangesCommand.js";
5
+ /** Base predefined one-click commands */
6
+ const BASE_COMMAND_LIBRARY = {
13
7
  test_given_endpoint_comprehensively: TEST_GIVEN_ENDPOINT_COMPREHENSIVELY_COMMAND,
14
8
  full_repo_scan_recommend_generate_and_execute_top_n_tests: FULLREPO_RECOMMEND_GENERATE_EXECUTE_TOPN_TESTS_COMMAND,
15
9
  };
10
+ /** All enabled predefined one-click commands */
11
+ function getCommandLibrary() {
12
+ const commands = { ...BASE_COMMAND_LIBRARY };
13
+ if (isLocalDevEnabled()) {
14
+ commands.local_dev_test_changes = LOCAL_DEV_TEST_CHANGES_COMMAND;
15
+ }
16
+ return commands;
17
+ }
16
18
  export function getCommand(id) {
17
- const cmd = COMMAND_LIBRARY[id];
19
+ const cmd = getCommandLibrary()[id];
18
20
  if (!cmd) {
19
21
  throw new Error(`Unknown one-click command: ${id}`);
20
22
  }
21
23
  return cmd;
22
24
  }
23
25
  export function getAllCommands() {
24
- return Object.values(COMMAND_LIBRARY);
26
+ return Object.values(getCommandLibrary()).filter((cmd) => Boolean(cmd));
25
27
  }
26
28
  export function getCommandIds() {
27
- return Object.keys(COMMAND_LIBRARY);
29
+ return getAllCommands().map((cmd) => cmd.id);
28
30
  }
29
31
  /**
30
32
  * Look up a command by its string ID. Accepts any string so the tool handler
@@ -32,7 +34,7 @@ export function getCommandIds() {
32
34
  * the ID is not found.
33
35
  */
34
36
  export function lookupCommand(id) {
35
- const cmd = COMMAND_LIBRARY[id];
37
+ const cmd = getCommandLibrary()[id];
36
38
  if (!cmd) {
37
39
  const available = getCommandIds().join(", ");
38
40
  throw new Error(`Unknown workflow: "${id}". Valid workflow IDs: ${available}.`);
@@ -42,3 +44,4 @@ export function lookupCommand(id) {
42
44
  // Re-export commands for consumers that import from commandLibrary
43
45
  export { TEST_GIVEN_ENDPOINT_COMPREHENSIVELY_COMMAND } from "./testThisEndpointCommand.js";
44
46
  export { FULLREPO_RECOMMEND_GENERATE_EXECUTE_TOPN_TESTS_COMMAND } from "./recommendTestsAndExecuteCommand.js";
47
+ export { LOCAL_DEV_TEST_CHANGES_COMMAND } from "./localDevTestChangesCommand.js";
@@ -0,0 +1,15 @@
1
+ import type { OneClickCommandDef } from "../types/OneClickCommands.js";
2
+ /**
3
+ * Predefined One-Click Command: Local Dev — Test Changes
4
+ *
5
+ * This is the BOOTSTRAPPER / ENTRY POINT for local-dev testing.
6
+ * It handles first-time setup (skills + workspace), then delegates
7
+ * the actual test workflow to the `skyramp_local_dev` MCP prompt.
8
+ *
9
+ * Flow:
10
+ * Step 0: Install IDE skill into repo for the calling agent
11
+ * Step 1: Ensure .skyramp/workspace.yml exists
12
+ * Step 2: Invoke skyramp_local_dev prompt (which runs the full workflow)
13
+ */
14
+ export declare const LOCAL_DEV_SKILL_CONTENT = "---\nname: local-dev\ndescription: Run the Skyramp MCP local-dev workflow to test local code changes against a base branch. Use when the user asks to test changes, mock downstream services, validate a feature branch, run local-dev, or verify a PR before merge.\nuser_invocable: true\n---\n\n# local-dev\n\nUse the Skyramp MCP local-dev workflow as the source of truth. Do not recreate the workflow steps from this skill.\n\n## Usage\n\n`/local-dev` \u2014 diff against main (default)\n`/local-dev main --mock stripe,billing --real postgres,redis`\n\n## Invocation\n\nCall the `skyramp_local_dev` MCP prompt with the user's intent preserved:\n- `baseCommit`: branch/SHA to diff against (default: main)\n- `repositoryPath`: absolute path to repo\n- `servicesToMock`: comma-separated services the user explicitly wants mocked\n- `realServices`: comma-separated services that should receive real traffic\n- `mockProtocols`: comma-separated protocols the user explicitly wants mocked (for example, `rest`)\n\nBy default, mock only true third-party downstream services. Keep the service under test, infrastructure, and local first-party services real unless the user explicitly asks to mock them.\n";
15
+ export declare const LOCAL_DEV_TEST_CHANGES_COMMAND: OneClickCommandDef;
@@ -0,0 +1,188 @@
1
+ import { localDevPlan } from "../prompts/local-dev/local-dev-plan.js";
2
+ /**
3
+ * Predefined One-Click Command: Local Dev — Test Changes
4
+ *
5
+ * This is the BOOTSTRAPPER / ENTRY POINT for local-dev testing.
6
+ * It handles first-time setup (skills + workspace), then delegates
7
+ * the actual test workflow to the `skyramp_local_dev` MCP prompt.
8
+ *
9
+ * Flow:
10
+ * Step 0: Install IDE skill into repo for the calling agent
11
+ * Step 1: Ensure .skyramp/workspace.yml exists
12
+ * Step 2: Invoke skyramp_local_dev prompt (which runs the full workflow)
13
+ */
14
+ // ── Skill content (single source of truth) ──────────────────────────────────
15
+ // These get written to user repos by Step 0. Edit here, not in separate files.
16
+ export const LOCAL_DEV_SKILL_CONTENT = `---
17
+ name: local-dev
18
+ description: Run the Skyramp MCP local-dev workflow to test local code changes against a base branch. Use when the user asks to test changes, mock downstream services, validate a feature branch, run local-dev, or verify a PR before merge.
19
+ user_invocable: true
20
+ ---
21
+
22
+ # local-dev
23
+
24
+ Use the Skyramp MCP local-dev workflow as the source of truth. Do not recreate the workflow steps from this skill.
25
+
26
+ ## Usage
27
+
28
+ \`/local-dev\` — diff against main (default)
29
+ \`/local-dev main --mock stripe,billing --real postgres,redis\`
30
+
31
+ ## Invocation
32
+
33
+ Call the \`skyramp_local_dev\` MCP prompt with the user's intent preserved:
34
+ - \`baseCommit\`: branch/SHA to diff against (default: main)
35
+ - \`repositoryPath\`: absolute path to repo
36
+ - \`servicesToMock\`: comma-separated services the user explicitly wants mocked
37
+ - \`realServices\`: comma-separated services that should receive real traffic
38
+ - \`mockProtocols\`: comma-separated protocols the user explicitly wants mocked (for example, \`rest\`)
39
+
40
+ By default, mock only true third-party downstream services. Keep the service under test, infrastructure, and local first-party services real unless the user explicitly asks to mock them.
41
+ `;
42
+ const localDevTestChangesSteps = [
43
+ {
44
+ stepIndex: 0,
45
+ title: "Install local-dev skill into the repository",
46
+ description: `The \`skyramp_one_click_tool\` installs the local-dev skill file for the calling agent automatically when this workflow is invoked. Verify the matching file exists before continuing.
47
+
48
+ File to check/create only if the tool did not already install it:
49
+ - Cursor: \`.cursor/skills/local-dev/SKILL.md\`
50
+ - Claude Code: \`.claude/skills/local-dev/SKILL.md\`
51
+ Agents without repo-local skill support skip skill creation. Also ensure the MCP config has the required env vars set (SKYRAMP_FEATURE_LOCAL_DEV=1 automatically enables contract consumer mode too).`,
52
+ conditionalGuidance: `Create the skill file for the calling agent ONLY if it doesn't already exist — never overwrite.
53
+
54
+ **Skill path for the calling agent only:**
55
+ - Cursor: \`.cursor/skills/local-dev/SKILL.md\`
56
+ - Claude Code: \`.claude/skills/local-dev/SKILL.md\`
57
+ Agents without repo-local skill support should skip skill creation. Write this exact content to the matching Cursor or Claude Code file:
58
+ ${LOCAL_DEV_SKILL_CONTENT}
59
+
60
+ **MCP config env vars** — also ensure the following env vars are set in the Skyramp MCP server config:
61
+ - For Cursor: \`~/.cursor/mcp.json\` → add to the skyramp server's \`env\` block:
62
+ \`"SKYRAMP_FEATURE_LOCAL_DEV": "1"\`
63
+ - For Claude Code: \`~/.claude/settings.json\` → add to the skyramp server's \`env\` block:
64
+ \`"SKYRAMP_FEATURE_LOCAL_DEV": "1"\`
65
+ - For VS Code: add to the Skyramp MCP server's VS Code MCP config env block:
66
+ \`"SKYRAMP_FEATURE_LOCAL_DEV": "1"\`
67
+
68
+ Setting SKYRAMP_FEATURE_LOCAL_DEV=1 automatically enables consumer contract tests (no separate flag needed). After updating the config, the user must restart the IDE or reload MCP servers for the change to take effect.
69
+
70
+ After verifying or creating files, inform the user what was installed.`,
71
+ },
72
+ {
73
+ stepIndex: 1,
74
+ title: "Ensure Skyramp workspace is initialized",
75
+ description: "Check whether .skyramp/workspace.yml exists at repositoryPath. If it exists, read it to confirm it has services and dependencies configured. If it does not exist and .git exists, call skyramp_init_scan with workspacePath to discover services, then call skyramp_init_workspace with the scan results to create the workspace file.",
76
+ toolCall: {
77
+ toolName: "skyramp_init_scan",
78
+ description: "Scan repository for services if workspace not initialized",
79
+ inputs: {
80
+ workspacePath: { source: "user", paramKey: "repositoryPath" },
81
+ },
82
+ },
83
+ conditionalGuidance: "Only call skyramp_init_scan + skyramp_init_workspace if .skyramp/workspace.yml does NOT exist. If it already exists, skip this step entirely. The workspace.yml is required by the skyramp_local_dev prompt — it reads service URLs, dependencies, runtime config, and network settings from it.",
84
+ },
85
+ {
86
+ stepIndex: 2,
87
+ title: "Invoke the skyramp_local_dev prompt",
88
+ description: "Call the `skyramp_local_dev` MCP prompt to run the full local-dev testing workflow.\n\nPass these parameters:\n- `baseCommit`: the one-click `baseBranch` value (default `main`)\n- `repositoryPath`: absolute path to the repo\n- `servicesToMock`: comma-separated list only when the user explicitly named services to mock\n- `realServices`: comma-separated list only when the user explicitly named services that should receive real traffic\n- `mockProtocols`: comma-separated list only when the user explicitly constrained mocked protocols (for example, `rest`)\n\nThe one-click tool exposes `baseBranch` because that matches other workflow inputs; the `skyramp_local_dev` prompt argument is named `baseCommit`. Always map `baseBranch` → `baseCommit` when invoking the prompt.\n\nThe workflow generates the highest-value tests regardless of type. For integration scenarios with selected downstream mocks: skyramp_mock_generation → skyramp_integration_test_generation → skyramp_enrich_test_with_mocks. For contract scenarios: skyramp_contract_test_generation only; do not enrich contract tests. Integration enrich is MANDATORY when the selected scenario has downstream mocks — it embeds MOCK_SERVICES into the test file.\n\nFollow the returned prompt instructions EXACTLY — do not skip steps or freelance.",
89
+ conditionalGuidance: renderFallbackGuidance(),
90
+ },
91
+ ];
92
+ /**
93
+ * Render the fallback guidance from the canonical localDevPlan.
94
+ * This ensures the one-click command fallback stays in sync with the plan
95
+ * without maintaining a separate handwritten copy.
96
+ */
97
+ function renderFallbackGuidance() {
98
+ const preamble = `The skyramp_local_dev prompt returns a detailed multi-step workflow. Follow it completely.
99
+
100
+ If the prompt is NOT available as a callable prompt in this IDE, fall back to calling the individual tools in sequence. Replace <REPOSITORY_PATH> with the actual repository path and <BASE_BRANCH> with the user's one-click \`baseBranch\` value (default "main"). When the plan says \`baseCommit\`, use that same baseBranch value.
101
+
102
+ Fallback service routing inputs from the one-click invocation:
103
+ <SERVICE_ROUTING>
104
+ <MOCK_SERVICES><servicesToMock from one-click params, if provided; use all when the user requested mock all></MOCK_SERVICES>
105
+ <REAL_SERVICES><realServices from one-click params, if provided></REAL_SERVICES>
106
+ <MOCK_PROTOCOLS><mockProtocols from one-click params, if provided></MOCK_PROTOCOLS>
107
+ </SERVICE_ROUTING>
108
+
109
+ Two rules apply in fallback mode at all times:
110
+ 1. **Mock scope**: if the user said "mock all downstream services", "mock all", "mock everything", or "mock all except X", treat all discovered downstream dependencies as selected for mocking (same as passing \`servicesToMock: "all"\`). Do not silently default to the third-party-only policy when the user expressed broad mock intent.
111
+ 2. **No pytest fallback**: if \`skyramp_execute_test\` becomes unavailable at any point (tool not found, MCP disconnected, context compaction), stop immediately and ask the user to reconnect the Skyramp MCP server. Do not fall back to \`pytest\`, \`python3\`, or any direct runtime — doing so bypasses mock deployment and produces results that cannot be trusted as Skyramp-validated.
112
+
113
+ ---
114
+
115
+ `;
116
+ const ctx = {
117
+ repositoryPath: "<REPOSITORY_PATH>",
118
+ baseCommit: "<BASE_BRANCH from one-click baseBranch, default main>",
119
+ maxRecommendations: 8,
120
+ maxGenerate: 5,
121
+ schemaSourceGuidance: "Use any OpenAPI spec found in the repository as the authoritative schema source. If no spec is available, infer request/response shapes from the implementation code.",
122
+ serviceRoutingBlock: `<SERVICE_ROUTING>
123
+ <MOCK_SERVICES><servicesToMock from one-click params, if provided; use all when the user requested mock all></MOCK_SERVICES>
124
+ <REAL_SERVICES><realServices from one-click params, if provided></REAL_SERVICES>
125
+ <MOCK_PROTOCOLS><mockProtocols from one-click params, if provided></MOCK_PROTOCOLS>
126
+ </SERVICE_ROUTING>`,
127
+ serviceContext: `<SERVICE_CONTEXT>
128
+ Read <REPOSITORY_PATH>/.skyramp/workspace.yml before classifying dependencies. Use its services, dependencies, runtime, ports, and docker network values as the local service inventory.
129
+ </SERVICE_CONTEXT>`,
130
+ };
131
+ const planBody = localDevPlan.render(ctx);
132
+ const principles = `
133
+ ---
134
+
135
+ ## Key Principles
136
+ - ALL output paths absolute inside repo. NEVER /tmp.
137
+ - For **integration tests**: call \`skyramp_enrich_test_with_mocks\` after generating each test that uses selected downstream mocks. Each integration test with mocked dependencies MUST have MOCK_SERVICES embedded for third-party mocks or user-requested mocks.
138
+ - For **contract tests**: mocks are wired inline by the generation tool — do NOT call \`skyramp_enrich_test_with_mocks\` for contract tests. Consumer contract tests already include \`client.apply_mock(mock)\` in the generated test function.
139
+ - Prioritize tests that catch real bugs and edge cases; test type is secondary to coverage value.
140
+ - Present results directly to the user (no skyramp_submit_report).
141
+ - **Prefer batch operations**: Use \`skyramp_batch_mock_generation\` over individual mock calls.
142
+ - **Prefer combined tools**: Use \`skyramp_generate_enriched_integration_test\` over separate generate+enrich.
143
+ - **Pre-flight first**: Call \`skyramp_preflight_mock_check\` before mock generation to catch protocol conflicts.
144
+ - **Compact assertions**: Use \`skyramp_enhance_assertions\` with \`autoApply: true\` for shorter instructions.`;
145
+ return preamble + planBody + principles;
146
+ }
147
+ export const LOCAL_DEV_TEST_CHANGES_COMMAND = {
148
+ id: "local_dev_test_changes",
149
+ name: "Local Dev — Test Changes",
150
+ description: "Bootstrap and run the local-dev testing workflow: install the active agent's IDE skill into the repo, initialize workspace, then invoke skyramp_local_dev to diff local changes, generate mocks and exhaustive tests, execute, and report. First run sets up the repo for future `/local-dev` usage.",
151
+ intent: {
152
+ contextIndicators: [
153
+ "Use when the user wants to test their LOCAL code changes (committed or uncommitted) against a base branch",
154
+ "Use when the user asks to set up, initialize, or bootstrap Skyramp local-dev testing in their repo",
155
+ "Use when the user mentions mocking downstream services for local testing",
156
+ "Use when the user asks to validate a feature branch, generate tests for their diff, or run a local-dev workflow",
157
+ "First invocation installs the active agent's skill so future runs can use /local-dev directly",
158
+ "Do NOT use for testing a deployed/remote service — this is for LOCAL changes only",
159
+ "Do NOT use for a single test type request like 'generate a smoke test' — use the specific generation tool",
160
+ ],
161
+ purpose: "Bootstrap local-dev testing: install the active agent's IDE skill into repo, initialize workspace, then delegate to skyramp_local_dev prompt for the full test workflow (diff → mock → generate → execute → report).",
162
+ workflowSummary: "Install active agent skill → Init workspace → Invoke skyramp_local_dev prompt (which runs: analyze diff → recommend scenarios → generate mocks + tests → verify → deploy SUT + mocks → execute → report)",
163
+ examples: {
164
+ use: [
165
+ "test my local changes against main",
166
+ "setup skyramp local dev",
167
+ "initialize local dev testing",
168
+ "install skyramp skills",
169
+ "generate tests for my feature branch",
170
+ "run local-dev workflow",
171
+ "mock downstream services and test my diff",
172
+ "validate my changes before pushing",
173
+ "bootstrap skyramp testing in this repo",
174
+ ],
175
+ doNotUse: [
176
+ "test the products endpoint comprehensively → use test_given_endpoint_comprehensively instead",
177
+ "generate a smoke test for /api/users → use skyramp_smoke_test_generation directly",
178
+ "scan the full repo and run tests → use full_repo_scan_recommend_generate_and_execute_top_n_tests",
179
+ "run my existing tests → use skyramp_execute_test directly",
180
+ ],
181
+ },
182
+ },
183
+ steps: localDevTestChangesSteps,
184
+ inputParams: {
185
+ required: ["repositoryPath"],
186
+ optional: ["baseBranch", "servicesToMock", "realServices", "mockProtocols", "endpointURL", "token"],
187
+ },
188
+ };
package/build/index.js CHANGED
@@ -24,24 +24,41 @@ import { registerCodeReuseTool } from "./tools/code-refactor/codeReuseTool.js";
24
24
  import { registerEnhanceAssertionsTool } from "./tools/code-refactor/enhanceAssertionsTool.js";
25
25
  import { registerBatchScenarioTestTool } from "./tools/generate-tests/generateBatchScenarioRestTool.js";
26
26
  import { registerMockTool } from "./tools/generate-tests/generateMockRestTool.js";
27
+ import { registerBatchMockTool } from "./tools/generate-tests/batchMockGenerationTool.js";
27
28
  import { registerAnalyzeChangesTool, registerAnalyzeTestHealthTool, registerActionsTool, } from "./tools/test-management/index.js";
28
29
  import { registerTestbotPrompt } from "./prompts/testbot/testbot-prompts.js";
29
30
  import { registerSutSetupResource } from "./resources/sutSetupResource.js";
30
31
  import { registerTestbotResource } from "./resources/testbotResource.js";
32
+ import { registerLocalDevPrompt } from "./prompts/local-dev/local-dev-prompts.js";
31
33
  import { registerSubmitReportTool } from "./tools/submitReportTool.js";
32
34
  import { registerInitializeWorkspaceTool } from "./tools/workspace/initializeWorkspaceTool.js";
33
35
  import { registerInitScanWorkspaceTool } from "./tools/workspace/initScanWorkspaceTool.js";
34
36
  import { registerOneClickTool } from "./tools/one-click/oneClickTool.js";
37
+ import { registerEnrichTestWithMocksTool } from "./tools/enrichTestWithMocksTool.js";
38
+ import { registerPreflightMockCheckTool } from "./tools/preflightMockCheckTool.js";
39
+ import { registerGenerateEnrichedIntegrationTestTool } from "./tools/generateEnrichedIntegrationTestTool.js";
35
40
  import { registerAnalysisResources } from "./resources/analysisResources.js";
36
41
  import { registerProgressResource } from "./resources/progressResource.js";
37
42
  import { AnalyticsService } from "./services/AnalyticsService.js";
38
43
  import { registerInitTriggerOnMCPInitialized } from "./utils/initAgent.js";
39
- import { isTestbotEnabled } from "./utils/featureFlags.js";
44
+ import { isTestbotEnabled, isLocalDevEnabled } from "./utils/featureFlags.js";
40
45
  import { registerPlaywrightTools, registerTraceRecordingPrompt, getPlaywrightTraceService, } from "./playwright/index.js";
46
+ // One-click routing instructions — injected into MCP server instructions so agents
47
+ // know to delegate to the one-click tool rather than self-orchestrating.
48
+ // Suppressed in testbot mode where the testbot prompt drives orchestration.
41
49
  const oneClickEnabled = process.env.SKYRAMP_FEATURE_ONE_CLICK === "1";
42
- const oneClickInstructions = oneClickEnabled
43
- ? `\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\`.`
44
- : "";
50
+ const localDevEnabled = isLocalDevEnabled();
51
+ const localDevInstructions = `\n- When the user asks to test local changes, validate a feature branch, mock services and test, or run local-dev: MUST call \`skyramp_one_click_tool\` with workflow \`local_dev_test_changes\`, \`callingAgent\` set to the current MCP client (\`cursor\` or \`claude\` for skill installation; VS Code does not support repo-local Agent Skills), \`repositoryPath\`, and \`baseBranch\` (default "main"). Do NOT self-orchestrate the one-click tool handles skill installation, workspace init, and prompt invocation.`;
52
+ const fullOneClickInstructions = `\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\`.${localDevEnabled ? localDevInstructions : ""}`;
53
+ // Testbot suppresses one-click workflows (it has its own orchestration via testbot prompt),
54
+ // but local-dev routing is always active when the flag is set — they serve different use cases.
55
+ const oneClickInstructions = isTestbotEnabled()
56
+ ? (localDevEnabled ? localDevInstructions : "")
57
+ : oneClickEnabled
58
+ ? fullOneClickInstructions
59
+ : localDevEnabled
60
+ ? localDevInstructions
61
+ : "";
45
62
  const server = new McpServer({
46
63
  name: "Skyramp MCP Server",
47
64
  version: "1.0.0",
@@ -59,13 +76,6 @@ const server = new McpServer({
59
76
  },
60
77
  instructions: `Skyramp MCP Server — generates and executes API tests (fuzz, contract, integration, E2E, UI).
61
78
 
62
- ## Workspace Initialization (REQUIRED before the first Skyramp tool call)
63
- If the workspace root is a git repo AND \`.skyramp/workspace.yml\` does not exist:
64
- 1. Call \`skyramp_init_scan\` with \`workspacePath\` → follow the returned instructions to discover all services.
65
- 2. Call \`skyramp_init_workspace\` with \`workspacePath\`, \`services\`, and the \`scanToken\` from step 1.
66
- 3. Proceed with the originally requested tool.
67
- Skip only if: not a git repo, \`.skyramp/workspace.yml\` already exists, or user explicitly declines.
68
-
69
79
  ## Rules
70
80
  - NEVER show CLI commands. NEVER attempt to install or configure the Skyramp CLI. ALWAYS use the MCP tools provided.
71
81
  - For UI and E2E tests, there are TWO recording modes:
@@ -128,6 +138,10 @@ if (isTestbotEnabled()) {
128
138
  registerSutSetupResource(server);
129
139
  logger.info("Testbot SUT setup resource enabled via SKYRAMP_FEATURE_TESTBOT");
130
140
  }
141
+ if (isLocalDevEnabled()) {
142
+ prompts.push(registerLocalDevPrompt);
143
+ logger.info("Local-dev prompt enabled via SKYRAMP_FEATURE_LOCAL_DEV");
144
+ }
131
145
  prompts.forEach((registerPrompt) => registerPrompt(server));
132
146
  logger.info("All prompts registered successfully");
133
147
  // Register test generation tools
@@ -141,6 +155,7 @@ const testGenerationTools = [
141
155
  registerUITestTool,
142
156
  registerBatchScenarioTestTool,
143
157
  registerMockTool,
158
+ registerBatchMockTool,
144
159
  ];
145
160
  testGenerationTools.forEach((registerTool) => registerTool(server));
146
161
  // Register modularization and code quality tools
@@ -162,9 +177,14 @@ registerActionsTool(server);
162
177
  registerInitScanWorkspaceTool(server);
163
178
  registerInitializeWorkspaceTool(server);
164
179
  // Register one-click orchestrated workflows
165
- if (oneClickEnabled) {
180
+ if (oneClickEnabled || localDevEnabled) {
166
181
  registerOneClickTool(server);
167
- logger.info("One-click tools enabled via SKYRAMP_FEATURE_ONE_CLICK");
182
+ logger.info("One-click tools enabled", {
183
+ enabledBy: {
184
+ SKYRAMP_FEATURE_ONE_CLICK: oneClickEnabled,
185
+ SKYRAMP_FEATURE_LOCAL_DEV: localDevEnabled,
186
+ },
187
+ });
168
188
  }
169
189
  // Register other Skyramp tools
170
190
  const infrastructureTools = [
@@ -174,6 +194,10 @@ const infrastructureTools = [
174
194
  registerTraceTool,
175
195
  registerTraceStopTool,
176
196
  ];
197
+ if (isLocalDevEnabled()) {
198
+ infrastructureTools.push(registerEnrichTestWithMocksTool, registerPreflightMockCheckTool, registerGenerateEnrichedIntegrationTestTool);
199
+ logger.info("Local-dev tools enabled via SKYRAMP_FEATURE_LOCAL_DEV");
200
+ }
177
201
  if (isTestbotEnabled()) {
178
202
  infrastructureTools.push(registerSubmitReportTool);
179
203
  logger.info("Testbot tools enabled via SKYRAMP_FEATURE_TESTBOT");
@@ -202,6 +226,38 @@ process.on("uncaughtException", async (error) => {
202
226
  }
203
227
  process.exit(1);
204
228
  });
229
+ // Handle unhandled promise rejections — without this handler Node.js 15+
230
+ // terminates the process on any unhandled rejection, which drops the MCP
231
+ // connection if e.g. a progress notification write fails on a broken transport.
232
+ process.on("unhandledRejection", async (reason) => {
233
+ const error = reason instanceof Error ? reason : new Error(String(reason));
234
+ // EPIPE means the client already disconnected — exit cleanly
235
+ if ("code" in error && error.code === "EPIPE") {
236
+ process.exit(0);
237
+ }
238
+ // gRPC/Docker connectivity errors — log and survive.
239
+ // These occur when Docker containers/networks are destroyed while the MCP
240
+ // server has pending gRPC calls to the Skyramp worker. The server should
241
+ // stay alive so non-Docker tools remain available.
242
+ const msg = error.message || "";
243
+ const isDockerDisconnect = /UNAVAILABLE|DEADLINE_EXCEEDED|ECONNREFUSED|ECONNRESET|connection reset|socket hang up|stream removed/i.test(msg);
244
+ if (isDockerDisconnect) {
245
+ logger.info("gRPC/Docker connection lost (container or network likely destroyed)", {
246
+ error: msg,
247
+ });
248
+ return;
249
+ }
250
+ logger.error("Unhandled promise rejection", {
251
+ error: error.message,
252
+ stack: error.stack,
253
+ });
254
+ try {
255
+ await AnalyticsService.pushServerCrashEvent("unhandledRejection", error.message, error.stack);
256
+ }
257
+ catch {
258
+ // Best effort — don't let telemetry failure cascade
259
+ }
260
+ });
205
261
  // Start MCP server
206
262
  async function main() {
207
263
  const transport = new StdioServerTransport();
@@ -237,6 +293,11 @@ async function main() {
237
293
  logger.error("STDIN error, parent likely disconnected", { error: err });
238
294
  handleParentDisconnect("stdin error");
239
295
  });
296
+ // Handle stdout errors (EPIPE = client stopped reading, broken pipe)
297
+ process.stdout.on("error", (err) => {
298
+ logger.error("STDOUT error, client likely disconnected", { error: err });
299
+ handleParentDisconnect("stdout error");
300
+ });
240
301
  // Handle process termination signals
241
302
  process.on("SIGTERM", () => handleParentDisconnect("SIGTERM"));
242
303
  process.on("SIGINT", () => handleParentDisconnect("SIGINT"));
@@ -12,4 +12,5 @@ export interface AssertionEnrichmentRule {
12
12
  examples: AssertionEnrichmentExample[];
13
13
  }
14
14
  export declare function renderRule(index: number, rule: AssertionEnrichmentRule): string;
15
+ export declare function renderSharedAssertionRules(): string;
15
16
  export declare function getAssertionsPrompt(specificRules: AssertionEnrichmentRule[], scope: string, testFile: string, enhanceType: EnhanceType): string;
@@ -122,6 +122,20 @@ expect(getResponseValue(productsPostResponse, "created_at")).toMatch(/^\\d{4}-\\
122
122
  },
123
123
  ],
124
124
  },
125
+ {
126
+ title: "Use exact recorded JSON paths",
127
+ description: "Do not add object-wrapper prefixes or nested paths that are not present in the actual response body. Use the exact JSON path from the recorded response body.",
128
+ subPoints: [
129
+ `If the body is flat (\`{"id": "acc_...", "tax_exempt": true}\`), use \`"tax_exempt"\`, not \`"account.tax_exempt"\`.`,
130
+ "Only use dot notation when the recorded response genuinely nests the field under that key.",
131
+ ],
132
+ examples: [
133
+ {
134
+ language: "javascript",
135
+ code: `expect(getResponseValue(accountResponse, "tax_exempt")).toBe(true);`,
136
+ },
137
+ ],
138
+ },
125
139
  {
126
140
  title: "Computed response fields, derived using dynamic formulas",
127
141
  description: "When the response contains a field whose value is derived from a calculation, assert the result using a formula built from prior response values and request inputs, not a hardcoded literal.",
@@ -158,6 +172,9 @@ ${ex.code}
158
172
  function renderRules(rules) {
159
173
  return rules.map((rule, i) => renderRule(i + 1, rule)).join("\n\n");
160
174
  }
175
+ export function renderSharedAssertionRules() {
176
+ return renderRules(SHARED_RULES);
177
+ }
161
178
  export function getAssertionsPrompt(specificRules, scope, testFile, enhanceType) {
162
179
  const allRules = [...SHARED_RULES, ...specificRules];
163
180
  const ruleChecklistKeys = allRules
@@ -13,7 +13,7 @@ After scanning the workspace, before calling the skyramp_init_workspace tool, yo
13
13
  "framework": "<framework>",
14
14
  "testDirectory": "<path>",
15
15
  "api": { "schemaPath": "<path-or-url>", "baseUrl": "<url>", "authType": "<type>", "authHeader": "<header>" },
16
- "runtimeDetails": { "runtime": "<runtime>", "serverStartCommand": "<command>", "dockerNetwork": "<network>" }
16
+ "runtimeDetails": { "runtime": "<runtime>", "serverStartCommand": "<command>", "serverStopCommand": "<docker stop command>", "dockerNetwork": "<network>" }
17
17
  }
18
18
  ]
19
19
  \`\`\`
@@ -68,7 +68,7 @@ API schemas (look inside the service directory and check known framework default
68
68
  <runtime_config>
69
69
  Inspect the repo root (and subdirectories like .devcontainer/) for shared runtime configuration:
70
70
  1. CLAUDE.md or AGENTS.md: if either file exists at the repo root, check it for dev/test/CI setup instructions including how to start services, required environment variables, and runtime configuration.
71
- 2. Docker Compose files: scan for ALL compose files including docker-compose.yml, docker-compose.yaml, docker-compose.*.yml (such as docker-compose.testbot.yml or docker-compose.dev.yml), compose.yml, and compose.*.yaml in the repo root and subdirectories. Distinguish between application compose files and infrastructure-only compose files. Infrastructure compose files contain only supporting services like databases, Redis, Minio, mail servers, or message queues and do NOT run the application itself. Application compose files contain the actual application services that build from the repo source code (look for "build:" directives pointing to the repo, or services that map to discovered application directories). Only use application compose files for determining runtime and serverStartCommand. When a non-default compose file is found, the serverStartCommand must reference it explicitly (such as "docker compose -f docker-compose.testbot.yml up -d <service-name>"). Docker Compose ALWAYS prefixes the network name with the project name. If compose has "networks: { my-net: ... }", the actual network name is "<project-name>_my-net". If there is no explicit networks section, the default network is "<project-name>_default". The project name is the basename of the working directory where docker compose runs.
71
+ 2. Docker Compose files: scan for ALL compose files including docker-compose.yml, docker-compose.yaml, docker-compose.*.yml (such as docker-compose.testbot.yml or docker-compose.dev.yml), compose.yml, and compose.*.yaml in the repo root and subdirectories. Distinguish between application compose files and infrastructure-only compose files. Infrastructure compose files contain only supporting services like databases, Redis, Minio, mail servers, or message queues and do NOT run the application itself. Application compose files contain the actual application services that build from the repo source code (look for "build:" directives pointing to the repo, or services that map to discovered application directories). Only use application compose files for determining runtime, serverStartCommand, and serverStopCommand. When a non-default compose file is found, the serverStartCommand and serverStopCommand must reference it explicitly (such as "docker compose -f docker-compose.testbot.yml up -d <service-name>" and "docker compose -f docker-compose.testbot.yml stop <service-name>"). Docker Compose ALWAYS prefixes the network name with the project name. If compose has "networks: { my-net: ... }", the actual network name is "<project-name>_my-net". If there is no explicit networks section, the default network is "<project-name>_default". The project name is the basename of the working directory where docker compose runs.
72
72
  3. Makefile: extract start and dev targets.
73
73
  4. Root package.json scripts: extract workspace-level commands.
74
74
  </runtime_config>
@@ -143,9 +143,10 @@ Create one service entry per deployable unit. You MUST include every backend/API
143
143
  - For "k8s" runtime: Use a deploy command such as "kubectl apply -f deploy/", "helm install myrelease .", or "skaffold run". This is always derivable from the manifests or charts present in the repo.
144
144
  - For "local" runtime: Use an application command such as "uvicorn main:app", "npm run dev", or "java -jar app.jar". Derive from Makefile, package.json scripts, or README. If no start command is discoverable, omit this field entirely.
145
145
  NEVER mix runtime types with incompatible commands (for example, using "uvicorn" with runtime "docker" will cause errors). For "local" runtime, NEVER fabricate a command. Only use commands found in Makefile, package.json scripts, or README.
146
- 3. runtimeDetails.dockerNetwork: Docker network name. ONLY set when runtime is "docker". NEVER set for "local" or "k8s".
147
- 4. runtimeDetails.k8sNamespace: Kubernetes namespace. ONLY set when runtime is "k8s". NEVER set for "local" or "docker".
148
- 5. runtimeDetails.k8sContext: Kubernetes context. ONLY set when runtime is "k8s". NEVER set for "local" or "docker".
146
+ 3. runtimeDetails.serverStopCommand: Optional command to stop the service after test execution. ONLY set when runtime is "docker" and serverStartCommand starts persistent Docker infrastructure. For docker compose starts, prefer matching stop/down commands from the same compose file. Omit this field for "local" and "k8s" runtimes, or when teardown is unsafe or not discoverable.
147
+ 4. runtimeDetails.dockerNetwork: Docker network name. ONLY set when runtime is "docker". NEVER set for "local" or "k8s".
148
+ 5. runtimeDetails.k8sNamespace: Kubernetes namespace. ONLY set when runtime is "k8s". NEVER set for "local" or "docker".
149
+ 6. runtimeDetails.k8sContext: Kubernetes context. ONLY set when runtime is "k8s". NEVER set for "local" or "docker".
149
150
  </runtime_fields>
150
151
 
151
152
  ### Verification
@@ -158,7 +159,7 @@ Before calling skyramp_init_workspace, confirm all of the following:
158
159
  5. Every service with authType apiKey has authHeader explicitly set to the actual custom header name (such as "X-API-Key" or "X-Admin-Key"). If you cannot find the header name in the source code, env vars, or README, do NOT use authType apiKey. Use authType none instead and add a YAML comment explaining auth is unresolved.
159
160
  6. framework matches language (python uses pytest or robot, typescript or javascript uses playwright, java uses junit).
160
161
  7. testDirectory follows the stable resolution rules above: framework config file when present (Playwright testDir in playwright.config.ts, pytest testpaths in pytest.ini or pyproject.toml, JUnit test source dir in pom.xml or build.gradle); otherwise the deterministic default (tests/skyramp for a single service, tests/skyramp/<serviceDirName> for multiple services).
161
- 8. If serverStartCommand is provided, it matches the runtime.
162
+ 8. If serverStartCommand is provided, it matches the runtime. If serverStopCommand is provided, runtime is "docker" and the command is a Docker command.
162
163
  9. For services in docker-compose.yml: runtime MUST be "docker" and the command MUST be a docker command such as "docker compose up -d <service-name>". Always include it since it is derivable from the service name.
163
164
  10. NEVER use application-level commands (uvicorn, npm, node, python, java, etc.) with runtime "docker".
164
165
  11. For "local" runtime: if no start command is discoverable from Makefile, package.json scripts, or README, omit serverStartCommand rather than guessing.
@@ -0,0 +1,25 @@
1
+ /**
2
+ * PromptPlan-based local-dev workflow — 6 phases.
3
+ *
4
+ * Phases: ANALYZE → RECOMMEND → GENERATE → VERIFY → DEPLOY → EXECUTE
5
+ *
6
+ * Key design decisions:
7
+ * - Mock files are generated in the same language as tests (from workspace config) with get_all_mocks()
8
+ * - Tests import get_all_mocks from each mock file, collect into MOCK_SERVICES, deploy collectively
9
+ * - NO YAML mock files, NO CLI commands — ONLY MCP tools
10
+ * - Execution uses skyramp_execute_test MCP tool exclusively
11
+ * - The executor handles mock deployment from the test file automatically
12
+ */
13
+ import { PromptPlan } from "../test-recommendation/promptPlan.js";
14
+ export interface LocalDevPlanCtx {
15
+ repositoryPath: string;
16
+ baseCommit: string;
17
+ maxRecommendations: number;
18
+ maxGenerate: number;
19
+ stateOutputFile?: string;
20
+ sandboxWorkerUrl?: string;
21
+ schemaSourceGuidance: string;
22
+ serviceRoutingBlock: string;
23
+ serviceContext: string;
24
+ }
25
+ export declare const localDevPlan: PromptPlan<LocalDevPlanCtx>;