@skyramp/mcp 0.2.6 → 0.2.8-rc.1

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 (248) hide show
  1. package/build/commands/commandLibrary.d.ts +12 -0
  2. package/build/commands/recommendTestsAndExecuteCommand.d.ts +2 -0
  3. package/build/commands/testThisEndpointCommand.d.ts +2 -0
  4. package/build/index.d.ts +2 -0
  5. package/build/index.js +6 -4
  6. package/build/playwright/PlaywrightTraceService.d.ts +32 -0
  7. package/build/playwright/index.d.ts +3 -0
  8. package/build/playwright/registerPlaywrightTools.d.ts +8 -0
  9. package/build/playwright/registerPlaywrightTools.js +12 -11
  10. package/build/playwright/traceRecordingPrompt.d.ts +13 -0
  11. package/build/playwright/traceRecordingPrompt.js +3 -11
  12. package/build/prompts/code-reuse.d.ts +1 -0
  13. package/build/prompts/enhance-assertions/contractProviderAssertionsPrompt.d.ts +2 -0
  14. package/build/prompts/enhance-assertions/integrationAssertionsPrompt.d.ts +2 -0
  15. package/build/prompts/enhance-assertions/sharedAssertionRules.d.ts +15 -0
  16. package/build/prompts/enhance-assertions/uiAssertionsPrompt.d.ts +2 -0
  17. package/build/prompts/enhance-assertions/uiAssertionsPrompt.js +24 -5
  18. package/build/prompts/fix-error-prompt.d.ts +1 -0
  19. package/build/prompts/initialize-workspace/initializeWorkspacePrompt.d.ts +1 -0
  20. package/build/prompts/modularization/integration-test-modularization.d.ts +1 -0
  21. package/build/prompts/modularization/ui-test-modularization.d.ts +1 -0
  22. package/build/prompts/personas.d.ts +21 -0
  23. package/build/prompts/pom-aware-code-reuse.d.ts +1 -0
  24. package/build/prompts/startTraceCollectionPrompts.d.ts +2 -0
  25. package/build/prompts/sut-setup/modes/adaptWorkflowPrompt.d.ts +2 -0
  26. package/build/prompts/sut-setup/modes/adaptWorkflowPrompt.js +114 -0
  27. package/build/prompts/sut-setup/modes/dockerComposePrompt.d.ts +2 -0
  28. package/build/prompts/sut-setup/modes/dockerComposePrompt.js +292 -0
  29. package/build/prompts/sut-setup/shared.d.ts +39 -0
  30. package/build/prompts/sut-setup/shared.js +132 -0
  31. package/build/prompts/test-maintenance/actionsInstructions.d.ts +35 -0
  32. package/build/prompts/test-maintenance/drift-analysis-prompt.d.ts +4 -0
  33. package/build/prompts/test-maintenance/driftAnalysisSections.d.ts +13 -0
  34. package/build/prompts/test-maintenance/driftAnalysisSections.js +9 -3
  35. package/build/prompts/test-recommendation/analysisOutputPrompt.d.ts +84 -0
  36. package/build/prompts/test-recommendation/analysisOutputPrompt.js +1 -1
  37. package/build/prompts/test-recommendation/analysisOutputPrompt.test.d.ts +1 -0
  38. package/build/prompts/test-recommendation/analysisOutputPrompt.test.js +1 -0
  39. package/build/prompts/test-recommendation/diffExecutionPlan.d.ts +17 -0
  40. package/build/prompts/test-recommendation/diffExecutionPlan.js +83 -18
  41. package/build/prompts/test-recommendation/fullRepoCatalog.d.ts +7 -0
  42. package/build/prompts/test-recommendation/mergeEnrichedScenarios.test.d.ts +1 -0
  43. package/build/prompts/test-recommendation/mergeEnrichedScenarios.test.js +1 -0
  44. package/build/prompts/test-recommendation/promptPlan.d.ts +203 -0
  45. package/build/prompts/test-recommendation/promptPlan.test.d.ts +1 -0
  46. package/build/prompts/test-recommendation/recommendationSections.d.ts +35 -0
  47. package/build/prompts/test-recommendation/recommendationShared.d.ts +31 -0
  48. package/build/prompts/test-recommendation/registerRecommendTestsPrompt.d.ts +7 -0
  49. package/build/prompts/test-recommendation/scopeAssessment.d.ts +55 -0
  50. package/build/prompts/test-recommendation/scopeAssessment.js +2 -2
  51. package/build/prompts/test-recommendation/scopeAssessment.test.d.ts +1 -0
  52. package/build/prompts/test-recommendation/scopeAssessment.test.js +1 -0
  53. package/build/prompts/test-recommendation/test-recommendation-prompt.d.ts +6 -0
  54. package/build/prompts/test-recommendation/test-recommendation-prompt.js +4 -4
  55. package/build/prompts/test-recommendation/test-recommendation-prompt.test.d.ts +1 -0
  56. package/build/prompts/test-recommendation/test-recommendation-prompt.test.js +97 -99
  57. package/build/prompts/testbot/testbot-prompts.d.ts +26 -0
  58. package/build/prompts/testbot/testbot-prompts.js +301 -46
  59. package/build/prompts/testbot/testbot-prompts.test.d.ts +1 -0
  60. package/build/prompts/testbot/testbot-prompts.test.js +109 -117
  61. package/build/resources/analysisResources.d.ts +17 -0
  62. package/build/resources/progressResource.d.ts +2 -0
  63. package/build/resources/sutSetupResource.d.ts +2 -0
  64. package/build/resources/sutSetupResource.js +45 -0
  65. package/build/resources/testbotResource.d.ts +2 -0
  66. package/build/resources/testbotResource.js +2 -2
  67. package/build/services/AnalyticsService.d.ts +13 -0
  68. package/build/services/AnalyticsService.test.d.ts +1 -0
  69. package/build/services/AnalyticsService.test.js +86 -0
  70. package/build/services/ModularizationService.d.ts +11 -0
  71. package/build/services/ScenarioGenerationService.d.ts +37 -0
  72. package/build/services/ScenarioGenerationService.integration.test.d.ts +1 -0
  73. package/build/services/ScenarioGenerationService.integration.test.js +4 -0
  74. package/build/services/ScenarioGenerationService.test.d.ts +1 -0
  75. package/build/services/TestDiscoveryService.d.ts +128 -0
  76. package/build/services/TestDiscoveryService.js +22 -12
  77. package/build/services/TestDiscoveryService.test.d.ts +1 -0
  78. package/build/services/TestDiscoveryService.test.js +4 -3
  79. package/build/services/TestExecutionService.d.ts +55 -0
  80. package/build/services/TestExecutionService.js +158 -26
  81. package/build/services/TestExecutionService.test.d.ts +1 -0
  82. package/build/services/TestExecutionService.test.js +339 -25
  83. package/build/services/TestGenerationService.d.ts +56 -0
  84. package/build/services/TestGenerationService.test.d.ts +1 -0
  85. package/build/services/TestGenerationService.test.js +10 -8
  86. package/build/services/containerEnv.d.ts +14 -0
  87. package/build/tool-phase-coverage.test.d.ts +1 -0
  88. package/build/tool-phase-coverage.test.js +2 -0
  89. package/build/tool-phases.d.ts +38 -0
  90. package/build/tools/auth/loginTool.d.ts +2 -0
  91. package/build/tools/auth/logoutTool.d.ts +2 -0
  92. package/build/tools/code-refactor/codeReuseTool.d.ts +2 -0
  93. package/build/tools/code-refactor/enhanceAssertionsTool.d.ts +2 -0
  94. package/build/tools/code-refactor/modularizationTool.d.ts +2 -0
  95. package/build/tools/executeSkyrampTestTool.d.ts +2 -0
  96. package/build/tools/executeSkyrampTestTool.js +26 -7
  97. package/build/tools/fixErrorTool.d.ts +2 -0
  98. package/build/tools/generate-tests/generateBatchScenarioRestTool.d.ts +45 -0
  99. package/build/tools/generate-tests/generateBatchScenarioRestTool.js +1 -1
  100. package/build/tools/generate-tests/generateBatchScenarioRestTool.test.d.ts +1 -0
  101. package/build/tools/generate-tests/generateBatchScenarioRestTool.test.js +11 -9
  102. package/build/tools/generate-tests/generateContractRestTool.d.ts +31 -0
  103. package/build/tools/generate-tests/generateE2ERestTool.d.ts +13 -0
  104. package/build/tools/generate-tests/generateFuzzRestTool.d.ts +10 -0
  105. package/build/tools/generate-tests/generateIntegrationRestTool.d.ts +16 -0
  106. package/build/tools/generate-tests/generateLoadRestTool.d.ts +18 -0
  107. package/build/tools/generate-tests/generateLoadRestTool.test.d.ts +1 -0
  108. package/build/tools/generate-tests/generateMockRestTool.d.ts +64 -0
  109. package/build/tools/generate-tests/generateSmokeRestTool.d.ts +8 -0
  110. package/build/tools/generate-tests/generateUIRestTool.d.ts +14 -0
  111. package/build/tools/generate-tests/generateUIRestTool.js +1 -1
  112. package/build/tools/generate-tests/loadTestSchema.d.ts +39 -0
  113. package/build/tools/one-click/oneClickTool.d.ts +11 -0
  114. package/build/tools/submitReportTool.d.ts +394 -0
  115. package/build/tools/submitReportTool.js +250 -41
  116. package/build/tools/submitReportTool.test.d.ts +1 -0
  117. package/build/tools/submitReportTool.test.js +511 -55
  118. package/build/tools/test-management/actionsTool.d.ts +10 -0
  119. package/build/tools/test-management/actionsTool.js +21 -7
  120. package/build/tools/test-management/analyzeChangesTool.d.ts +27 -0
  121. package/build/tools/test-management/analyzeChangesTool.js +150 -48
  122. package/build/tools/test-management/analyzeChangesTool.test.d.ts +1 -0
  123. package/build/tools/test-management/analyzeChangesTool.test.js +95 -78
  124. package/build/tools/test-management/analyzeTestHealthTool.d.ts +2 -0
  125. package/build/tools/test-management/analyzeTestHealthTool.js +16 -9
  126. package/build/tools/test-management/index.d.ts +3 -0
  127. package/build/tools/test-management/index.js +0 -1
  128. package/build/tools/trace/resolveSaveStoragePath.d.ts +7 -0
  129. package/build/tools/trace/resolveSaveStoragePath.test.d.ts +1 -0
  130. package/build/tools/trace/resolveSessionPaths.d.ts +40 -0
  131. package/build/tools/trace/resolveSessionPaths.test.d.ts +1 -0
  132. package/build/tools/trace/sessionState.d.ts +2 -0
  133. package/build/tools/trace/sessionState.test.d.ts +1 -0
  134. package/build/tools/trace/startTraceCollectionTool.d.ts +2 -0
  135. package/build/tools/trace/stopTraceCollectionTool.d.ts +2 -0
  136. package/build/tools/workspace/initScanWorkspaceTool.d.ts +4 -0
  137. package/build/tools/workspace/initializeWorkspaceTool.d.ts +2 -0
  138. package/build/tools/workspace/initializeWorkspaceTool.js +39 -22
  139. package/build/tools/workspace/initializeWorkspaceTool.test.d.ts +1 -0
  140. package/build/tools/workspace/initializeWorkspaceTool.test.js +134 -0
  141. package/build/tools/workspace/serviceUpsert.d.ts +12 -0
  142. package/build/tools/workspace/serviceUpsert.js +23 -0
  143. package/build/tools/workspace/serviceUpsert.test.d.ts +1 -0
  144. package/build/tools/workspace/serviceUpsert.test.js +50 -0
  145. package/build/types/OneClickCommands.d.ts +68 -0
  146. package/build/types/RepositoryAnalysis.d.ts +3004 -0
  147. package/build/types/TestAnalysis.d.ts +97 -0
  148. package/build/types/TestExecution.d.ts +63 -0
  149. package/build/types/TestExecution.js +11 -1
  150. package/build/types/TestRecommendation.d.ts +12 -0
  151. package/build/types/TestTypes.d.ts +234 -0
  152. package/build/types/TestbotReport.d.ts +68 -0
  153. package/build/types/TestbotReport.js +1 -0
  154. package/build/types/index.d.ts +4 -0
  155. package/build/types/index.js +3 -0
  156. package/build/utils/AnalysisStateManager.d.ts +242 -0
  157. package/build/utils/AnalysisStateManager.js +144 -3
  158. package/build/utils/AnalysisStateManager.test.d.ts +1 -0
  159. package/build/utils/AnalysisStateManager.test.js +98 -0
  160. package/build/utils/analyze-openapi.d.ts +1 -0
  161. package/build/utils/branchDiff.d.ts +18 -0
  162. package/build/utils/dartRouteExtractor.d.ts +45 -0
  163. package/build/utils/dartRouteExtractor.test.d.ts +1 -0
  164. package/build/utils/docker.d.ts +19 -0
  165. package/build/utils/docker.test.d.ts +1 -0
  166. package/build/utils/docker.test.js +10 -9
  167. package/build/utils/featureFlags.d.ts +37 -0
  168. package/build/utils/featureFlags.test.d.ts +1 -0
  169. package/build/utils/gitStaging.d.ts +24 -0
  170. package/build/utils/gitStaging.test.d.ts +1 -0
  171. package/build/utils/gitStaging.test.js +13 -7
  172. package/build/utils/httpDefaults.d.ts +10 -0
  173. package/build/utils/httpDefaults.test.d.ts +1 -0
  174. package/build/utils/initAgent.d.ts +36 -0
  175. package/build/utils/language-helper.d.ts +6 -0
  176. package/build/utils/logger.d.ts +11 -0
  177. package/build/utils/normalizeParams.d.ts +14 -0
  178. package/build/utils/normalizeSkyrampImports.d.ts +13 -0
  179. package/build/utils/pr-comment-parser.d.ts +31 -0
  180. package/build/utils/pr-comment-parser.test.d.ts +1 -0
  181. package/build/utils/pr-comment-parser.test.js +6 -5
  182. package/build/utils/projectMetadata.d.ts +15 -0
  183. package/build/utils/projectMetadata.test.d.ts +1 -0
  184. package/build/utils/proxy-terminal.d.ts +2 -0
  185. package/build/utils/repoScanner.d.ts +27 -0
  186. package/build/utils/repoScanner.test.d.ts +1 -0
  187. package/build/utils/routeParsers.d.ts +53 -0
  188. package/build/utils/routeParsers.js +31 -38
  189. package/build/utils/routeParsers.test.d.ts +1 -0
  190. package/build/utils/scenarioDrafting.d.ts +71 -0
  191. package/build/utils/scenarioDrafting.js +23 -1
  192. package/build/utils/scenarioDrafting.test.d.ts +1 -0
  193. package/build/utils/skyrampMdContent.d.ts +5 -0
  194. package/build/utils/sourceRouteExtractor.d.ts +48 -0
  195. package/build/utils/sourceRouteExtractor.test.d.ts +1 -0
  196. package/build/utils/telemetry.d.ts +32 -0
  197. package/build/utils/telemetry.test.d.ts +1 -0
  198. package/build/utils/trace-parser.d.ts +32 -0
  199. package/build/utils/trace-parser.test.d.ts +6 -0
  200. package/build/utils/uiPageEnumerator.d.ts +146 -0
  201. package/build/utils/uiPageEnumerator.js +58 -14
  202. package/build/utils/uiPageEnumerator.test.d.ts +1 -0
  203. package/build/utils/uiPageEnumerator.test.js +223 -43
  204. package/build/utils/utils.d.ts +45 -0
  205. package/build/utils/utils.js +10 -0
  206. package/build/utils/utils.test.d.ts +1 -0
  207. package/build/utils/versions.d.ts +3 -0
  208. package/build/utils/versions.js +1 -1
  209. package/build/utils/workspaceAuth.d.ts +127 -0
  210. package/build/utils/workspaceAuth.test.d.ts +1 -0
  211. package/build/utils/workspaceAuth.test.js +25 -25
  212. package/build/workspace/index.d.ts +1 -0
  213. package/build/workspace/workspace.d.ts +390 -0
  214. package/build/workspace/workspace.js +7 -7
  215. package/build/workspace/workspace.test.d.ts +1 -0
  216. package/build/workspace/workspace.test.js +6 -6
  217. package/node_modules/playwright/lib/mcp/skyramp/loadTraceTool.js +31 -13
  218. package/node_modules/playwright/lib/mcp/skyramp/traceRecordingBackend.js +88 -15
  219. package/node_modules/playwright/lib/mcp/test/skyRampExport.js +4 -3
  220. package/node_modules/playwright/node_modules/playwright-core/ThirdPartyNotices.txt +23 -126
  221. package/node_modules/playwright/node_modules/playwright-core/lib/generated/injectedScriptSource.js +1 -1
  222. package/node_modules/playwright/node_modules/playwright-core/lib/server/recorder/recorderApp.js +79 -19
  223. package/node_modules/playwright/node_modules/playwright-core/lib/server/recorder/skyramp/aliasRemapper.js +84 -0
  224. package/node_modules/playwright/node_modules/playwright-core/lib/server/recorder/skyramp/replayEngine.js +15 -4
  225. package/node_modules/playwright/node_modules/playwright-core/lib/vite/recorder/assets/{codeMirrorModule-CPZKmjFR.js → codeMirrorModule-D0BjbCb7.js} +1 -1
  226. package/node_modules/playwright/node_modules/playwright-core/lib/vite/recorder/assets/index-lvTRGFx-.js +193 -0
  227. package/node_modules/playwright/node_modules/playwright-core/lib/vite/recorder/index.html +1 -1
  228. package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/assets/{codeMirrorModule-B0JOjboO.js → codeMirrorModule-Cqp3cwEJ.js} +1 -1
  229. package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/assets/{defaultSettingsView-1anWeyDf.js → defaultSettingsView-CYf9adZh.js} +79 -79
  230. package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/{index.Cc5029a3.js → index.B_7ywgmr.js} +1 -1
  231. package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/index.html +2 -2
  232. package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/uiMode.html +2 -2
  233. package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/{uiMode.Wo5yvvVh.js → uiMode.u4_8VnCV.js} +1 -1
  234. package/node_modules/playwright/node_modules/playwright-core/package.json +1 -1
  235. package/node_modules/playwright/node_modules/playwright-core/src/generated/injectedScriptSource.ts +1 -1
  236. package/node_modules/playwright/node_modules/playwright-core/src/server/recorder/recorderApp.ts +129 -43
  237. package/node_modules/playwright/node_modules/playwright-core/src/server/recorder/skyramp/aliasRemapper.ts +119 -0
  238. package/node_modules/playwright/node_modules/playwright-core/src/server/recorder/skyramp/replayEngine.ts +33 -10
  239. package/node_modules/playwright/package.json +1 -1
  240. package/package.json +16 -4
  241. package/build/tools/test-management/uiAnalyzeChangesTool.js +0 -155
  242. package/build/tools/test-management/uiAnalyzeChangesTool.test.js +0 -147
  243. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/.bin/node-which +0 -52
  244. package/node_modules/playwright/node_modules/playwright-core/bundles/utils/node_modules/.bin/is-docker +0 -5
  245. package/node_modules/playwright/node_modules/playwright-core/bundles/utils/node_modules/.bin/mime +0 -46
  246. package/node_modules/playwright/node_modules/playwright-core/bundles/utils/node_modules/.bin/yaml +0 -11
  247. package/node_modules/playwright/node_modules/playwright-core/lib/vite/recorder/assets/index-CsgPR2wy.js +0 -193
  248. package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/index.CP3Y8Zsb.js +0 -2
@@ -1,13 +1,14 @@
1
- jest.mock("../../workspace/workspace.js", () => ({
1
+ import { jest } from "@jest/globals";
2
+ jest.unstable_mockModule("../../workspace/workspace.js", () => ({
2
3
  WorkspaceConfigManager: jest.fn(),
3
4
  }));
4
- jest.mock("../../services/AnalyticsService.js", () => ({
5
+ jest.unstable_mockModule("../../services/AnalyticsService.js", () => ({
5
6
  AnalyticsService: { pushMCPToolEvent: jest.fn() },
6
7
  }));
7
- jest.mock("../../playwright/traceRecordingPrompt.js", () => ({
8
+ jest.unstable_mockModule("../../playwright/traceRecordingPrompt.js", () => ({
8
9
  getTraceRecordingPromptText: () => "",
9
10
  }));
10
- import { getTestbotPrompt } from "./testbot-prompts.js";
11
+ const { getTestbotPrompt, buildWorkspaceRecoveryPrefix, parseRelatedRepositories } = await import("./testbot-prompts.js");
11
12
  // Minimal args to invoke getTestbotPrompt — only services matter for these tests
12
13
  const baseArgs = {
13
14
  prTitle: "Test PR",
@@ -24,26 +25,6 @@ function callWithServices(services) {
24
25
  undefined, // userPrompt
25
26
  services);
26
27
  }
27
- function callWithStateOutputFile(stateOutputFile) {
28
- return getTestbotPrompt(baseArgs.prTitle, baseArgs.prDescription, baseArgs.summaryOutputFile, baseArgs.repositoryPath, undefined, // baseBranch
29
- undefined, // maxRecommendations
30
- undefined, // maxGenerate
31
- undefined, // maxCritical
32
- undefined, // prNumber
33
- undefined, // userPrompt
34
- undefined, // services
35
- stateOutputFile);
36
- }
37
- function callFollowUpWithStateOutputFile(stateOutputFile) {
38
- return getTestbotPrompt(baseArgs.prTitle, baseArgs.prDescription, baseArgs.summaryOutputFile, baseArgs.repositoryPath, undefined, // baseBranch
39
- undefined, // maxRecommendations
40
- undefined, // maxGenerate
41
- undefined, // maxCritical
42
- undefined, // prNumber
43
- "add more tests", // userPrompt — triggers follow-up path
44
- undefined, // services
45
- stateOutputFile);
46
- }
47
28
  describe("buildServiceContext (via getTestbotPrompt)", () => {
48
29
  it("renders full service with all fields", () => {
49
30
  const prompt = callWithServices([
@@ -121,27 +102,6 @@ describe("buildServiceContext (via getTestbotPrompt)", () => {
121
102
  expect(prompt).toContain("</REPOSITORY PATH>\nUse the Skyramp MCP server tools");
122
103
  });
123
104
  });
124
- describe("stateOutputFile in getTestbotPrompt", () => {
125
- it("includes stateOutputFile in skyramp_analyze_changes call for first-run prompt", () => {
126
- const stateFile = "/tmp/skyramp/analyze-changes-state.json";
127
- const prompt = callWithStateOutputFile(stateFile);
128
- // The prompt must pass stateOutputFile to skyramp_analyze_changes
129
- expect(prompt).toContain(`\`stateOutputFile\`: "${stateFile}"`);
130
- });
131
- it("includes stateOutputFile in skyramp_analyze_changes call for follow-up prompt", () => {
132
- const stateFile = "/tmp/skyramp/analyze-changes-state.json";
133
- const prompt = callFollowUpWithStateOutputFile(stateFile);
134
- expect(prompt).toContain(`\`stateOutputFile\`: "${stateFile}"`);
135
- });
136
- it("omits stateOutputFile from skyramp_analyze_changes call when not provided", () => {
137
- const prompt = callWithStateOutputFile(undefined);
138
- expect(prompt).not.toContain("stateOutputFile");
139
- });
140
- it("omits stateOutputFile from follow-up prompt when not provided", () => {
141
- const prompt = callFollowUpWithStateOutputFile(undefined);
142
- expect(prompt).not.toContain("stateOutputFile");
143
- });
144
- });
145
105
  describe("uiCredentials in getTestbotPrompt", () => {
146
106
  function callWithUiCredentials(uiCredentials) {
147
107
  return getTestbotPrompt(baseArgs.prTitle, baseArgs.prDescription, baseArgs.summaryOutputFile, baseArgs.repositoryPath, undefined, // baseBranch
@@ -151,7 +111,6 @@ describe("uiCredentials in getTestbotPrompt", () => {
151
111
  undefined, // prNumber
152
112
  undefined, // userPrompt
153
113
  undefined, // services
154
- undefined, // stateOutputFile
155
114
  uiCredentials);
156
115
  }
157
116
  // The instructions text references `<ui-credentials>` in prose (backticked);
@@ -216,13 +175,11 @@ describe("drift analysis — runtime tool call (step 3)", () => {
216
175
  const prompt = basePrompt();
217
176
  expect(prompt).toContain("skyramp_actions");
218
177
  });
219
- it("step 3 appears inside Task 1, before Task 2", () => {
178
+ it("skyramp_analyze_test_health is referenced in the prompt", () => {
179
+ // After the unified maintenance refactor, analyze_test_health is referenced
180
+ // in the testMaintenance submission guidance rather than as an explicit step.
220
181
  const prompt = basePrompt();
221
- const task1Pos = prompt.indexOf("## Task 1");
222
- const healthPos = prompt.indexOf("skyramp_analyze_test_health");
223
- const task2Pos = prompt.indexOf("## Task 2");
224
- expect(healthPos).toBeGreaterThan(task1Pos);
225
- expect(healthPos).toBeLessThan(task2Pos);
182
+ expect(prompt).toContain("skyramp_analyze_test_health");
226
183
  });
227
184
  it("does not contain the build-time embedded drift_analysis_rules content (Action Decision Tree)", () => {
228
185
  // The rules are now fetched at runtime via skyramp_analyze_test_health —
@@ -237,57 +194,7 @@ describe("drift analysis — runtime tool call (step 3)", () => {
237
194
  expect(prompt).not.toContain("You are acting as a Skyramp Integration Architect");
238
195
  });
239
196
  });
240
- describe("UI grounding via Task 2 capture-act-capture", () => {
241
- it("surfaces uiContext as guidance, not a contract", () => {
242
- const prompt = getTestbotPrompt(baseArgs.prTitle, baseArgs.prDescription, baseArgs.summaryOutputFile, baseArgs.repositoryPath);
243
- // uiContext fields are explained inline so the agent knows what to do with
244
- // them. Step 1 provides candidate URLs but gives fallback instructions
245
- // ("navigate from the workspace baseUrl and explore") for 404s/redirects,
246
- // treating candidates as guidance not a rigid contract.
247
- expect(prompt).toContain("uiContext");
248
- expect(prompt).toContain("candidateUiPages");
249
- expect(prompt).toContain("changedFrontendFiles");
250
- expect(prompt).toMatch(/navigate from the workspace baseUrl and explore/i);
251
- });
252
- it("step 5 enforces Blueprint Citation Invariant in natural prose", () => {
253
- const prompt = getTestbotPrompt(baseArgs.prTitle, baseArgs.prDescription, baseArgs.summaryOutputFile, baseArgs.repositoryPath);
254
- // Step 5 is the citation-invariant guardrail, not a "fill in tuples"
255
- // post-processing step (slice 4 cleanup: recs are grounded upstream).
256
- expect(prompt).toContain("Blueprint Citation Invariant");
257
- // Reasoning must be natural prose, NOT internal-identifier syntax.
258
- expect(prompt).toMatch(/natural prose/i);
259
- expect(prompt).toMatch(/internal-identifier syntax/i);
260
- });
261
- it("Task 2 no longer instructs the agent to fill in tuples post-hoc", () => {
262
- const prompt = getTestbotPrompt(baseArgs.prTitle, baseArgs.prDescription, baseArgs.summaryOutputFile, baseArgs.repositoryPath);
263
- // After slice 4 cleanup: Task 2 captures are for trace recording's own
264
- // assertions, not for retroactively rewriting recommendation reasoning.
265
- // The phrase "fill in tuples" must NOT appear anywhere in the prompt.
266
- expect(prompt).not.toMatch(/fill in tuples/i);
267
- expect(prompt).not.toMatch(/return to step 5 and fill/i);
268
- });
269
- it("Task 2 step 5 mentions possibleAssertions as available, NOT as required (slice 5.5 softening)", () => {
270
- const prompt = getTestbotPrompt(baseArgs.prTitle, baseArgs.prDescription, baseArgs.summaryOutputFile, baseArgs.repositoryPath);
271
- // Slice 5: AFTER-action browser_blueprint response includes
272
- // possibleAssertions[] — mechanically translated candidates.
273
- expect(prompt).toContain("possibleAssertions");
274
- // Slice 5.5: prompt explicitly tells the agent NOT to feel obligated.
275
- // Two P09 runs with the prior "emit at least one" directive showed the
276
- // agent over-using shallow visibility assertions at the expense of
277
- // integration-test depth. The softened version says: read them, use
278
- // when they happen to match what you'd write anyway, ignore otherwise.
279
- expect(prompt).toMatch(/do not feel obligated/i);
280
- expect(prompt).toMatch(/biased toward visibility/i);
281
- // The candidate format is still documented.
282
- expect(prompt).toMatch(/\bcode\b.*\brationale\b.*\btier\b/i);
283
- // The pre-existing "at least one browser_assert per page navigated"
284
- // rule should be preserved (it's about meaningful business-outcome
285
- // assertions, not about possibleAssertions).
286
- expect(prompt).toMatch(/at least one .browser_assert. per page navigated/i);
287
- });
288
- });
289
197
  describe("buildWorkspaceRecoveryPrefix", () => {
290
- const { buildWorkspaceRecoveryPrefix } = require("./testbot-prompts.js");
291
198
  it("includes repositoryPath in both init_scan and init_workspace instructions", () => {
292
199
  const prefix = buildWorkspaceRecoveryPrefix("/home/user/repo");
293
200
  expect(prefix).toContain('skyramp_init_scan with workspacePath "/home/user/repo"');
@@ -312,7 +219,6 @@ describe("testsRepoDir in getTestbotPrompt", () => {
312
219
  undefined, // prNumber
313
220
  undefined, // userPrompt
314
221
  undefined, // services
315
- undefined, // stateOutputFile
316
222
  undefined, // uiCredentials
317
223
  testsRepoDir);
318
224
  }
@@ -324,7 +230,6 @@ describe("testsRepoDir in getTestbotPrompt", () => {
324
230
  undefined, // prNumber
325
231
  "add more tests", // userPrompt — triggers follow-up path
326
232
  undefined, // services
327
- undefined, // stateOutputFile
328
233
  undefined, // uiCredentials
329
234
  testsRepoDir);
330
235
  }
@@ -348,20 +253,13 @@ describe("testsRepoDir in getTestbotPrompt", () => {
348
253
  });
349
254
  });
350
255
  describe("testbot prompt blueprint-grounded recommendations (slice 4)", () => {
351
- it("instructs the agent to call skyramp_ui_analyze_changes before skyramp_analyze_changes", () => {
352
- const prompt = getTestbotPrompt(baseArgs.prTitle, baseArgs.prDescription, baseArgs.summaryOutputFile, baseArgs.repositoryPath);
353
- const uiacIdx = prompt.indexOf("skyramp_ui_analyze_changes");
354
- const acIdx = prompt.indexOf("skyramp_analyze_changes");
355
- expect(uiacIdx).toBeGreaterThan(-1);
356
- expect(acIdx).toBeGreaterThan(uiacIdx);
357
- });
358
- it("Task 1 step 1 instructs the agent to capture blueprints (without threading them through a param)", () => {
256
+ it("uses a single skyramp_analyze_changes call (no separate pre-flight tool, no blueprint threading)", () => {
359
257
  const prompt = getTestbotPrompt(baseArgs.prTitle, baseArgs.prDescription, baseArgs.summaryOutputFile, baseArgs.repositoryPath);
360
- // Captures stay in tool-result history; analyze_changes returns the
361
- // authoring rules and the agent supplies the captured vocabulary.
362
- expect(prompt).toMatch(/browser_blueprint`?\s*to capture/i);
363
- expect(prompt).toMatch(/tool-result history/i);
364
- // Make sure we removed the old capturedBlueprints threading directive.
258
+ // The standalone pre-flight tool is gone its work folded into analyze_changes.
259
+ expect(prompt).not.toContain("skyramp_ui_analyze_changes");
260
+ expect(prompt).toContain("skyramp_analyze_changes");
261
+ // Captures stay in tool-result history — the old capturedBlueprints
262
+ // threading directive must be gone.
365
263
  expect(prompt).not.toMatch(/capturedBlueprints/);
366
264
  });
367
265
  // Flutter support — both the generalised UI trigger wording and the
@@ -386,3 +284,97 @@ describe("testbot prompt blueprint-grounded recommendations (slice 4)", () => {
386
284
  expect(exceptionBlock).toContain(".vue");
387
285
  });
388
286
  });
287
+ describe("relatedRepositories in getTestbotPrompt (multi-repo mode)", () => {
288
+ const PRIMARY_REPO = "letsramp/primary-app";
289
+ const related = [
290
+ { repo: "letsramp/backend", repositoryPath: "/tmp/skyramp/related-repo-0-backend", baseBranch: "main" },
291
+ { repo: "letsramp/shared", repositoryPath: "/tmp/skyramp/related-repo-1-shared", baseBranch: "master" },
292
+ ];
293
+ function callWithRelated(repos, opts) {
294
+ return getTestbotPrompt(baseArgs.prTitle, baseArgs.prDescription, baseArgs.summaryOutputFile, baseArgs.repositoryPath, undefined, // baseBranch
295
+ undefined, // maxRecommendations
296
+ undefined, // maxGenerate
297
+ undefined, // maxCritical
298
+ undefined, // prNumber
299
+ undefined, // userPrompt
300
+ undefined, // services
301
+ undefined, // uiCredentials
302
+ opts?.testsRepoDir, // testsRepoDir
303
+ repos, opts?.primaryRepo ?? PRIMARY_REPO);
304
+ }
305
+ it("emits a <related_repositories> XML block with repository + path + base_branch per repo", () => {
306
+ const prompt = callWithRelated(related);
307
+ expect(prompt).toContain("<related_repositories>");
308
+ expect(prompt).toContain("</related_repositories>");
309
+ expect(prompt).toContain('<repository repository="letsramp/backend" path="/tmp/skyramp/related-repo-0-backend" base_branch="main" />');
310
+ expect(prompt).toContain('<repository repository="letsramp/shared" path="/tmp/skyramp/related-repo-1-shared" base_branch="master" />');
311
+ // Pure XML — no free-form "repositoryPath:" / "baseBranch:" colon syntax.
312
+ expect(prompt).not.toContain("repositoryPath:");
313
+ expect(prompt).not.toMatch(/baseBranch:/);
314
+ });
315
+ it("emits the primary repo's owner/repo in a <REPOSITORY> block", () => {
316
+ const prompt = callWithRelated(related);
317
+ expect(prompt).toContain(`<REPOSITORY>${PRIMARY_REPO}</REPOSITORY>`);
318
+ });
319
+ it("always emits a base_branch attribute, even when no explicit baseBranch is provided", () => {
320
+ const prompt = callWithRelated([
321
+ { repo: "letsramp/backend", repositoryPath: "/tmp/skyramp/related-repo-0-backend" },
322
+ ]);
323
+ expect(prompt).toContain("<related_repositories>");
324
+ // Absent value → explicit auto-detect marker in the attribute.
325
+ expect(prompt).toContain('<repository repository="letsramp/backend" path="/tmp/skyramp/related-repo-0-backend" base_branch="auto-detect" />');
326
+ });
327
+ it("keeps the unified workspace in the PRIMARY repo even when testsRepoDir is set", () => {
328
+ const prompt = callWithRelated(related, { testsRepoDir: "/tmp/skyramp/test-repo" });
329
+ // workspace.yml is registered in the primary repo regardless of the test repo.
330
+ expect(prompt).toContain(`\`workspacePath\`: "${baseArgs.repositoryPath}"`);
331
+ expect(prompt).not.toContain('`workspacePath`: "/tmp/skyramp/test-repo"');
332
+ // And it makes the primary-vs-test-repo distinction explicit to the agent.
333
+ expect(prompt).toMatch(/NOT in the configured test repo/);
334
+ });
335
+ it("leaves the single-repo prompt unchanged when no related repos are provided", () => {
336
+ const prompt = callWithRelated(undefined);
337
+ expect(prompt).not.toContain("<related_repositories>");
338
+ expect(prompt).not.toContain("MULTI-REPO CONTEXT");
339
+ expect(prompt).not.toContain("MULTI-REPO attribution");
340
+ // The cross-repo round-robin / candidate-pooling language must not leak into
341
+ // single-repo runs (the as-is GENERATE mandate stays authoritative there).
342
+ expect(prompt).not.toMatch(/round-robin/i);
343
+ expect(prompt).not.toContain("Multi-repo exception");
344
+ expect(prompt).not.toMatch(/candidates, not a fixed selection/i);
345
+ });
346
+ it("treats an empty array as single-repo", () => {
347
+ const prompt = callWithRelated([]);
348
+ expect(prompt).not.toContain("<related_repositories>");
349
+ });
350
+ });
351
+ describe("parseRelatedRepositories", () => {
352
+ it("parses a valid JSON array", () => {
353
+ const parsed = parseRelatedRepositories(JSON.stringify([
354
+ { repo: "o/a", repositoryPath: "/a", baseBranch: "main" },
355
+ { repo: "o/b", repositoryPath: "/b" },
356
+ ]));
357
+ expect(parsed).toEqual([
358
+ { repo: "o/a", repositoryPath: "/a", baseBranch: "main" },
359
+ { repo: "o/b", repositoryPath: "/b", baseBranch: undefined },
360
+ ]);
361
+ });
362
+ it("returns undefined for missing input", () => {
363
+ expect(parseRelatedRepositories(undefined)).toBeUndefined();
364
+ expect(parseRelatedRepositories("")).toBeUndefined();
365
+ });
366
+ it("returns undefined for malformed JSON", () => {
367
+ expect(parseRelatedRepositories("{not json")).toBeUndefined();
368
+ });
369
+ it("returns undefined for an empty array", () => {
370
+ expect(parseRelatedRepositories("[]")).toBeUndefined();
371
+ });
372
+ it("drops entries missing a repositoryPath or repo", () => {
373
+ const parsed = parseRelatedRepositories(JSON.stringify([
374
+ { repo: "o/x", baseBranch: "main" }, // no repositoryPath
375
+ { repositoryPath: "/no-repo" }, // no repo
376
+ { repo: "o/ok", repositoryPath: "/ok" },
377
+ ]));
378
+ expect(parsed).toEqual([{ repo: "o/ok", repositoryPath: "/ok", baseBranch: undefined }]);
379
+ });
380
+ });
@@ -0,0 +1,17 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare const ANALYSIS_URI_PREFIX = "skyramp://analysis";
3
+ /**
4
+ * Register MCP Resources for analysis data access.
5
+ *
6
+ * Resources provide read-only, token-efficient access to analysis data
7
+ * via sessionId. The state file is an internal backing store.
8
+ *
9
+ * URI scheme:
10
+ * skyramp://analysis/{sessionId}/summary
11
+ * skyramp://analysis/{sessionId}/endpoints
12
+ * skyramp://analysis/{sessionId}/endpoints/{+path}
13
+ * skyramp://analysis/{sessionId}/endpoints/{+path}/{method}
14
+ * skyramp://analysis/{sessionId}/scenarios
15
+ * skyramp://analysis/{sessionId}/diff
16
+ */
17
+ export declare function registerAnalysisResources(server: McpServer): void;
@@ -0,0 +1,2 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerProgressResource(server: McpServer): void;
@@ -0,0 +1,2 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerSutSetupResource(server: McpServer): void;
@@ -0,0 +1,45 @@
1
+ import { ResourceTemplate, } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { logger } from "../utils/logger.js";
3
+ import { resourceText } from "../utils/utils.js";
4
+ import { AnalyticsService } from "../services/AnalyticsService.js";
5
+ import { getAdaptWorkflowPrompt } from "../prompts/sut-setup/modes/adaptWorkflowPrompt.js";
6
+ import { getDockerComposePrompt } from "../prompts/sut-setup/modes/dockerComposePrompt.js";
7
+ import { SutSetupMode } from "../prompts/sut-setup/shared.js";
8
+ export function registerSutSetupResource(server) {
9
+ logger.info("Registering SUT setup resource");
10
+ // RFC 6570 {+rest} (reserved expansion) captures the entire query string
11
+ // including the leading "?". This avoids the SDK's per-param regex which
12
+ // fails on empty query-param values.
13
+ const template = new ResourceTemplate("skyramp://prompts/skyramp_testbot_sut{+rest}", { list: undefined });
14
+ server.registerResource("skyramp_testbot_sut", template, {
15
+ title: "Skyramp Testbot SUT Setup",
16
+ description: "Returns mode-specific instructions for SUT (System Under Test) setup. " +
17
+ "Modes: none, adapt_workflow, docker_compose.",
18
+ mimeType: "text/plain",
19
+ }, async (uri) => {
20
+ const param = (name, fallback) => uri.searchParams.get(name) ?? fallback;
21
+ const mode = param("sutSetupMode", SutSetupMode.None);
22
+ const args = {
23
+ repositoryPath: param("repositoryPath", "."),
24
+ sutSetupMode: mode,
25
+ sutSourceWorkflowFile: param("sutSourceWorkflowFile", ""),
26
+ sutSourceDockerComposeFile: param("sutSourceDockerComposeFile", ""),
27
+ };
28
+ let prompt;
29
+ switch (mode) {
30
+ case SutSetupMode.AdaptWorkflow:
31
+ prompt = getAdaptWorkflowPrompt(args);
32
+ break;
33
+ case SutSetupMode.DockerCompose:
34
+ prompt = getDockerComposePrompt(args);
35
+ break;
36
+ case SutSetupMode.None:
37
+ prompt = "sutSetupMode is 'none'. No SUT setup required.";
38
+ break;
39
+ default:
40
+ prompt = `Unknown SUT setup mode: '${mode}'. Valid modes: none, adapt_workflow, docker_compose.`;
41
+ }
42
+ AnalyticsService.pushMCPToolEvent("skyramp_testbot_sut_resource", undefined, { mode }).catch(() => { });
43
+ return resourceText(uri.toString(), prompt);
44
+ });
45
+ }
@@ -0,0 +1,2 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerTestbotResource(server: McpServer): void;
@@ -2,7 +2,7 @@ import { ResourceTemplate, } from "@modelcontextprotocol/sdk/server/mcp.js";
2
2
  import { logger } from "../utils/logger.js";
3
3
  import { AnalyticsService } from "../services/AnalyticsService.js";
4
4
  import { MAX_TESTS_TO_GENERATE, MAX_RECOMMENDATIONS, MAX_CRITICAL_TESTS, } from "../prompts/test-recommendation/recommendationSections.js";
5
- import { getTestbotPrompt, readWorkspaceServices } from "../prompts/testbot/testbot-prompts.js";
5
+ import { getTestbotPrompt, readWorkspaceServices, parseRelatedRepositories, } from "../prompts/testbot/testbot-prompts.js";
6
6
  export function registerTestbotResource(server) {
7
7
  logger.info("Registering testbot resource");
8
8
  // RFC 6570 {+rest} (reserved expansion) captures the entire query string
@@ -25,7 +25,7 @@ export function registerTestbotResource(server) {
25
25
  const maxCrit = parseInt(uri.searchParams.get("maxCritical") || "", 10);
26
26
  const repositoryPath = param("repositoryPath", ".");
27
27
  const services = await readWorkspaceServices(repositoryPath);
28
- const prompt = getTestbotPrompt(param("prTitle", ""), param("prDescription", ""), param("summaryOutputFile", ""), repositoryPath, uri.searchParams.get("baseBranch") || undefined, isNaN(maxRec) ? MAX_RECOMMENDATIONS : maxRec, isNaN(maxGen) ? MAX_TESTS_TO_GENERATE : maxGen, isNaN(maxCrit) ? MAX_CRITICAL_TESTS : maxCrit, isNaN(prNum) ? undefined : prNum, uri.searchParams.get("userPrompt") || undefined, services.length ? services : undefined, uri.searchParams.get("stateOutputFile") || undefined, uri.searchParams.get("uiCredentials") || undefined, uri.searchParams.get("testsRepoDir") || undefined);
28
+ const prompt = getTestbotPrompt(param("prTitle", ""), param("prDescription", ""), param("summaryOutputFile", ""), repositoryPath, uri.searchParams.get("baseBranch") || undefined, isNaN(maxRec) ? MAX_RECOMMENDATIONS : maxRec, isNaN(maxGen) ? MAX_TESTS_TO_GENERATE : maxGen, isNaN(maxCrit) ? MAX_CRITICAL_TESTS : maxCrit, isNaN(prNum) ? undefined : prNum, uri.searchParams.get("userPrompt") || undefined, services.length ? services : undefined, uri.searchParams.get("uiCredentials") || undefined, uri.searchParams.get("testsRepoDir") || undefined, parseRelatedRepositories(uri.searchParams.get("relatedRepositories") || undefined), uri.searchParams.get("primaryRepo") || undefined);
29
29
  AnalyticsService.pushMCPToolEvent("skyramp_testbot_prompt", undefined, {}).catch(() => { });
30
30
  // Return the original URI — clients may use it to re-fetch the resource,
31
31
  // and the caller already has these params. Credentials never appear in
@@ -0,0 +1,13 @@
1
+ import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
2
+ export declare class AnalyticsService {
3
+ static pushTestGenerationToolEvent(toolName: string, result: CallToolResult, params: Record<string, any>): Promise<void>;
4
+ static pushMCPToolEvent(toolName: string, result: CallToolResult | undefined, params: Record<string, string>): Promise<void>;
5
+ /**
6
+ * Track server crash events
7
+ */
8
+ static pushServerCrashEvent(crashType: string, errorMessage: string, errorStack?: string): Promise<void>;
9
+ /**
10
+ * Track tool timeout events
11
+ */
12
+ static pushToolTimeoutEvent(toolName: string, timeoutMs: number, params: Record<string, string>): Promise<void>;
13
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,86 @@
1
+ // @ts-nocheck - Jest ESM type inference issues
2
+ import { jest } from "@jest/globals";
3
+ import * as fs from "fs";
4
+ import * as path from "path";
5
+ import { fileURLToPath } from "url";
6
+ // AnalyticsService resolves the MCP version with
7
+ // `fileURLToPath(import.meta.url)` at module scope. Under ts-jest's old
8
+ // CommonJS transform that top-level import.meta failed to compile with TS1343,
9
+ // so this module — and therefore this test — could not exist. The native-ESM
10
+ // Jest config (SKYR-3799) is what makes it loadable.
11
+ // pushToolEvent lives in @skyramp/skyramp, which pulls the native dylib. Mock
12
+ // it so we can assert on the event payload without loading the binary. Under
13
+ // native ESM jest.mock is not hoisted — use unstable_mockModule + dynamic
14
+ // import of the subject below.
15
+ const pushToolEvent = jest.fn();
16
+ jest.unstable_mockModule("@skyramp/skyramp", () => ({ pushToolEvent }));
17
+ jest.unstable_mockModule("../utils/telemetry.js", () => ({
18
+ getEntryPoint: jest.fn(() => "mcp"),
19
+ getCIPlatform: jest.fn(() => undefined),
20
+ getRepositoryInfo: jest.fn(async () => ({})),
21
+ }));
22
+ jest.unstable_mockModule("../utils/logger.js", () => ({
23
+ logger: { info: jest.fn(), error: jest.fn(), debug: jest.fn(), warn: jest.fn() },
24
+ }));
25
+ const { AnalyticsService } = await import("./AnalyticsService.js");
26
+ const telemetry = await import("../utils/telemetry.js");
27
+ // The version the production code should resolve: package.json two levels up
28
+ // from this module's own location — the same lookup getMCPPackageVersion does
29
+ // via import.meta.url. Computing it the same way here proves the test harness
30
+ // itself supports import.meta now.
31
+ const expectedVersion = JSON.parse(fs.readFileSync(path.join(path.dirname(fileURLToPath(import.meta.url)), "../../package.json"), "utf8")).version;
32
+ describe("AnalyticsService — import.meta-based version resolution", () => {
33
+ beforeEach(() => {
34
+ jest.clearAllMocks();
35
+ telemetry.getEntryPoint.mockReturnValue("mcp");
36
+ telemetry.getCIPlatform.mockReturnValue(undefined);
37
+ telemetry.getRepositoryInfo.mockResolvedValue({});
38
+ });
39
+ it("stamps the package.json version (resolved via import.meta.url) into the event params", async () => {
40
+ const params = {};
41
+ await AnalyticsService.pushMCPToolEvent("skyramp_smoke_test_generation", undefined, params);
42
+ expect(pushToolEvent).toHaveBeenCalledTimes(1);
43
+ const [, toolName, , sentParams] = pushToolEvent.mock.calls[0];
44
+ expect(toolName).toBe("skyramp_smoke_test_generation");
45
+ expect(sentParams.mcpServerVersion).toBe(expectedVersion);
46
+ // Sanity: a real semver-ish string, not an empty/undefined fallback.
47
+ expect(sentParams.mcpServerVersion).toMatch(/^\d+\.\d+\.\d+/);
48
+ });
49
+ it("concatenates error text from an isError result into the event error message", async () => {
50
+ const result = {
51
+ isError: true,
52
+ content: [
53
+ { type: "text", text: "first failure" },
54
+ { type: "text", text: "second failure" },
55
+ ],
56
+ };
57
+ await AnalyticsService.pushMCPToolEvent("skyramp_execute_test", result, {});
58
+ const [, , errorMessage] = pushToolEvent.mock.calls[0];
59
+ expect(errorMessage).toBe("first failure, second failure");
60
+ });
61
+ it("sends an empty error message for a successful result", async () => {
62
+ const result = { isError: false, content: [{ type: "text", text: "ok" }] };
63
+ await AnalyticsService.pushMCPToolEvent("skyramp_execute_test", result, {});
64
+ const [, , errorMessage] = pushToolEvent.mock.calls[0];
65
+ expect(errorMessage).toBe("");
66
+ });
67
+ it("includes ciPlatform in the params when running in CI", async () => {
68
+ telemetry.getCIPlatform.mockReturnValue("github_actions");
69
+ const params = {};
70
+ await AnalyticsService.pushMCPToolEvent("skyramp_init_workspace", undefined, params);
71
+ const [, , , sentParams] = pushToolEvent.mock.calls[0];
72
+ expect(sentParams.ciPlatform).toBe("github_actions");
73
+ });
74
+ it("merges repository info into the params", async () => {
75
+ telemetry.getRepositoryInfo.mockResolvedValue({ repoName: "letsramp/mcp", repoOwner: "letsramp" });
76
+ const params = { repositoryPath: "/repo" };
77
+ await AnalyticsService.pushMCPToolEvent("skyramp_analyze_changes", undefined, params);
78
+ expect(telemetry.getRepositoryInfo).toHaveBeenCalledWith("/repo");
79
+ const [, , , sentParams] = pushToolEvent.mock.calls[0];
80
+ expect(sentParams.repoName).toBe("letsramp/mcp");
81
+ });
82
+ it("swallows errors from pushToolEvent (telemetry must never break a tool call)", async () => {
83
+ pushToolEvent.mockRejectedValueOnce(new Error("network down"));
84
+ await expect(AnalyticsService.pushMCPToolEvent("skyramp_smoke_test_generation", undefined, {})).resolves.toBeUndefined();
85
+ });
86
+ });
@@ -0,0 +1,11 @@
1
+ import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
2
+ export interface ModularizationParams {
3
+ testFile: string;
4
+ testType?: string;
5
+ language?: string;
6
+ isTraceBased?: boolean;
7
+ prompt?: string;
8
+ }
9
+ export declare class ModularizationService {
10
+ processModularizationRequest(params: ModularizationParams): Promise<CallToolResult>;
11
+ }
@@ -0,0 +1,37 @@
1
+ import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
2
+ export interface TraceRequest {
3
+ Source: string;
4
+ Destination: string;
5
+ RequestBody: string;
6
+ ResponseBody: string;
7
+ RequestHeaders: Record<string, string[]>;
8
+ ResponseHeaders: Record<string, string[]>;
9
+ Method: string;
10
+ Path: string;
11
+ QueryParams: Record<string, string[]>;
12
+ StatusCode: number;
13
+ Port: number;
14
+ Timestamp: string;
15
+ Scheme: string;
16
+ }
17
+ export interface ScenarioParams {
18
+ scenarioName: string;
19
+ destination: string;
20
+ apiSchema?: string;
21
+ baseURL?: string;
22
+ method: string;
23
+ path: string;
24
+ requestBody?: string;
25
+ queryParams?: string;
26
+ responseBody?: string;
27
+ statusCode?: number;
28
+ outputDir: string;
29
+ authHeader?: string;
30
+ authScheme?: string;
31
+ authToken?: string;
32
+ responseHeaders?: Record<string, string[]>;
33
+ }
34
+ export declare class ScenarioGenerationService {
35
+ parseScenario(params: ScenarioParams): Promise<CallToolResult>;
36
+ generateTraceRequestFromInput(params: ScenarioParams): TraceRequest | null;
37
+ }
@@ -4,10 +4,14 @@
4
4
  *
5
5
  * Requires: @skyramp/skyramp native dylib (skips if not available)
6
6
  */
7
+ import { jest } from "@jest/globals";
7
8
  import fs from "fs";
8
9
  import path from "path";
9
10
  import os from "os";
11
+ import { createRequire } from "module";
10
12
  import { ScenarioGenerationService } from "./ScenarioGenerationService.js";
13
+ // Native dylib is a CommonJS addon — load it via createRequire under ESM.
14
+ const require = createRequire(import.meta.url);
11
15
  let SkyrampClient;
12
16
  try {
13
17
  SkyrampClient = require("@skyramp/skyramp").SkyrampClient;
@@ -0,0 +1 @@
1
+ export {};