@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
@@ -0,0 +1,84 @@
1
+ import { WorkspaceAuthType } from "../../utils/workspaceAuth.js";
2
+ import { ScannedEndpoint } from "../../utils/repoScanner.js";
3
+ /** Lightweight diff summary for the analysis output prompt. */
4
+ interface DiffSummary {
5
+ changedFiles: string[];
6
+ newEndpoints: Array<{
7
+ method: string;
8
+ path: string;
9
+ sourceFile: string;
10
+ }>;
11
+ modifiedEndpoints: Array<{
12
+ method: string;
13
+ path: string;
14
+ sourceFile: string;
15
+ }>;
16
+ removedEndpoints?: Array<{
17
+ method: string;
18
+ path: string;
19
+ sourceFile: string;
20
+ }>;
21
+ }
22
+ interface AnalysisOutputParams {
23
+ sessionId: string;
24
+ stateFile?: string;
25
+ repositoryPath: string;
26
+ analysisScope: string;
27
+ parsedDiff?: DiffSummary;
28
+ /** Path to temp file containing the full unified diff.
29
+ * Content is read once in buildAnalysisOutputText via readDiffFile — callers do not pass diffContent. */
30
+ diffFilePath?: string;
31
+ /** Resolved diff content — populated internally by buildAnalysisOutputText, not by callers. */
32
+ diffContent?: string;
33
+ /**
34
+ * Relative paths of all files whose names look like route/controller definitions.
35
+ * For full-repo scope: the LLM reads these to find endpoints from frameworks the
36
+ * static regex doesn't cover (Django, Axum, Ktor, FastAPI without prefix, etc.).
37
+ */
38
+ candidateRouteFiles?: string[];
39
+ scannedEndpoints: ScannedEndpoint[];
40
+ wsBaseUrl: string;
41
+ wsAuthHeader: string;
42
+ wsAuthType: WorkspaceAuthType | "";
43
+ wsSchemaPath: string;
44
+ /** True when wsSchemaPath was successfully fetched and parsed at analysis time */
45
+ specFetchSucceeded?: boolean;
46
+ routerMountContext: string[];
47
+ /** Content of key routing/mount files, read server-side (size-capped).
48
+ * Present when no spec is available — inlined so LLM doesn't need an extra read step. */
49
+ routerFileContents?: Array<{
50
+ file: string;
51
+ content: string;
52
+ }>;
53
+ nextTool?: string;
54
+ /**
55
+ * Changed files that contain no HTTP endpoint registrations (utilities, helpers,
56
+ * services, models). When non-empty the LLM is instructed to trace callers of
57
+ * the changed functions to find the correct HTTP surface (Fix 2 / Bug 5).
58
+ * Only meaningful in CurrentBranchDiff scope.
59
+ */
60
+ unmatchedFiles?: string[];
61
+ }
62
+ /** "1" — Read source files / changed files */
63
+ export declare const ANALYSIS_STEP_READ_FILES: string;
64
+ /** "1.1" — Verify/build/validate endpoint path resolution table */
65
+ export declare const ANALYSIS_STEP_RESOLVE_PATHS: string;
66
+ /** "2" — Extract or map endpoints */
67
+ export declare const ANALYSIS_STEP_EXTRACT: string;
68
+ /** "3" — Draft scenarios / present catalog / identify drift */
69
+ export declare const ANALYSIS_STEP_DRAFT: string;
70
+ /** "4" — Call the downstream tool (recommend_tests / analyze_test_health) */
71
+ export declare const ANALYSIS_STEP_CALL_TOOL: string;
72
+ /**
73
+ * Returns the step label for the trace-callers sub-step given runtime params.
74
+ * Returns "2.1" when the step is active (non-frontend unmatched backend files
75
+ * present in diff scope), or null when the step is absent.
76
+ *
77
+ * Use in tests instead of hardcoding "2.1":
78
+ * const label = getStepTraceCallers(params);
79
+ * if (label) expect(output).toContain(`### Step ${label}: Trace callers`);
80
+ * else expect(output).not.toContain("Trace callers");
81
+ */
82
+ export declare function getStepTraceCallers(p: AnalysisOutputParams): string | null;
83
+ export declare function buildAnalysisOutputText(p: AnalysisOutputParams): string;
84
+ export {};
@@ -127,7 +127,7 @@ An unintegrated non-route component has no DOM node in the running app and canno
127
127
  return `${canInline ? "Read the \`<diff>\` above" : `Read the diff file at \`${p.diffFilePath}\``} and identify every new or modified API endpoint — route registrations, handler methods, controller annotations. Then use the **Router Mounting / Nesting** section above to reconstruct the full URL path for each endpoint by chaining all parent router prefixes down to the handler (e.g. a handler in a file with \`prefix="/reviews"\` that is mounted at \`/{product_id}\` under a router mounted at \`/api/v1/products\` → full path \`/api/v1/products/{product_id}/reviews\`).
128
128
  ${diffHasJavaFiles ? JAVA_SPRING_NOTE : ""}
129
129
  For each endpoint found: note the HTTP method, full path, and source file.
130
- ${preDetectedEndpoints ? "The endpoint catalog above already lists some changed endpoints — verify and augment with anything it missed." : "No endpoints were pre-detected in the changed files — extract them from the diff."}
130
+ ${preDetectedEndpoints ? "The endpoint catalog above already lists some changed endpoints — verify and augment with anything it missed. **If a changed file contains many endpoints (hub/routing files like api.go, routes.py, router.ts), cross-reference against the diff hunks to identify only the endpoints whose handler code actually changed — do not include neighboring routes whose code was untouched.**" : "No endpoints were pre-detected in the changed files — extract them from the diff."}
131
131
  **Also identify removed endpoints**: Look for deleted route annotations (lines starting with \`-\` in the diff) in modified files (files that still exist but had routes deleted). A removed endpoint is a route definition present in the base branch but absent in the current branch. Cross-reference against the scanned endpoint listing below — if a deleted route annotation's endpoint still appears there (e.g. moved to another file), it is NOT removed. Only flag endpoints that are truly gone from the codebase.
132
132
  **CRITICAL — Query params vs body:** For GET endpoints (especially search/filter/list),
133
133
  identify which parameters are URL query params vs request body. Look at framework-specific
@@ -1,3 +1,4 @@
1
+ import { jest } from "@jest/globals";
1
2
  jest.mock("@skyramp/skyramp", () => ({
2
3
  WorkspaceConfigManager: { create: jest.fn() },
3
4
  }));
@@ -0,0 +1,17 @@
1
+ import { DraftedScenario } from "../../types/RepositoryAnalysis.js";
2
+ import { PriorityTier } from "../../types/TestRecommendation.js";
3
+ /** "0" — Code Review: correctness analysis */
4
+ export declare const EXEC_STEP_CODE_REVIEW: string;
5
+ /** "1" — External test coverage verification */
6
+ export declare const EXEC_STEP_COVERAGE: string;
7
+ /** "2" — Parameter grounding & priority assignment */
8
+ export declare const EXEC_STEP_ENRICH: string;
9
+ /** "3" — Diversity check */
10
+ export declare const EXEC_STEP_DIVERSITY: string;
11
+ /** "4" — Execute merged plan */
12
+ export declare const EXEC_STEP_EXECUTE: string;
13
+ export declare function buildExecutionPlan(scored: Array<{
14
+ scenario: DraftedScenario;
15
+ priority: PriorityTier;
16
+ novelty: string;
17
+ }>, maxGen: number, topN: number, baseUrl: string, authHeaderValue: string, authSchemeSnippet: string, authTypeValue: string, seed: string, endpointCount: number, isUIOnlyPR: boolean, hasFrontendChanges?: boolean, hasTraces?: boolean, externalCoverage?: Set<string>, relevantExternalTestPaths?: string[]): string;
@@ -154,6 +154,69 @@ function prioritizeAttackSurfaceBundles(items) {
154
154
  }
155
155
  return reordered;
156
156
  }
157
+ /**
158
+ * Select `count` items from a rank-ordered list, distributing GENERATE slots
159
+ * EVENLY across the test types present, with spillover.
160
+ *
161
+ * Policy (kept identical to the multi-repo prose in testbot-prompts.ts's
162
+ * "Cross-repo test generation" block — change both together):
163
+ * - Protected items first: CRITICAL-priority and attack-surface security_boundary
164
+ * scenarios always take a slot before round-robin (they must stay in GENERATE).
165
+ * - Bucket the rest by inferred test type (contract vs integration — the same
166
+ * inference used when rendering: `testType ?? (steps===1 ? contract : integration)`).
167
+ * - Round-robin one item per non-empty bucket per round, in the buckets' order of
168
+ * first appearance in the rank-ordered list (so the highest-ranked type wins
169
+ * round 1), preserving rank order within each bucket.
170
+ * - Spillover: an exhausted bucket is skipped on later rounds, so its freed slots
171
+ * go to the next type's next-highest item.
172
+ *
173
+ * Degenerate cases match the previous pure rank-order slice exactly: a single type
174
+ * present, or `count >= items.length`, returns the same items in the same order —
175
+ * so backend-only / single-type runs are unchanged (no regression).
176
+ */
177
+ function roundRobinByType(rankOrdered, count) {
178
+ if (count <= 0)
179
+ return [];
180
+ // Everything fits → no need to bucket; identical to the old slice.
181
+ if (count >= rankOrdered.length)
182
+ return rankOrdered.slice(0, count);
183
+ const inferType = (s) => s.testType ?? (s.steps.length === 1 ? "contract" : "integration");
184
+ // Protected items occupy GENERATE slots first, in rank order.
185
+ const selected = [];
186
+ const remaining = [];
187
+ for (const item of rankOrdered) {
188
+ if (selected.length < count &&
189
+ (item.priority === "CRITICAL" || isAttackSurfaceSecurityBoundary(item.scenario))) {
190
+ selected.push(item);
191
+ }
192
+ else {
193
+ remaining.push(item);
194
+ }
195
+ }
196
+ // Bucket the remainder by inferred type, preserving rank order and first-appearance
197
+ // bucket order.
198
+ const order = [];
199
+ const buckets = new Map();
200
+ for (const item of remaining) {
201
+ const t = inferType(item.scenario);
202
+ if (!buckets.has(t)) {
203
+ buckets.set(t, []);
204
+ order.push(t);
205
+ }
206
+ buckets.get(t).push(item);
207
+ }
208
+ // Round-robin one per non-empty bucket per round until full.
209
+ while (selected.length < count && order.some((t) => buckets.get(t).length > 0)) {
210
+ for (const t of order) {
211
+ if (selected.length >= count)
212
+ break;
213
+ const bucket = buckets.get(t);
214
+ if (bucket.length > 0)
215
+ selected.push(bucket.shift());
216
+ }
217
+ }
218
+ return selected;
219
+ }
157
220
  export function buildExecutionPlan(scored, maxGen, topN, baseUrl, authHeaderValue, authSchemeSnippet, authTypeValue, seed, endpointCount, isUIOnlyPR, hasFrontendChanges = false, hasTraces = false, externalCoverage = new Set(), relevantExternalTestPaths = []) {
158
221
  const frontendUrl = "<frontend_url>";
159
222
  // Slot allocation:
@@ -183,8 +246,19 @@ export function buildExecutionPlan(scored, maxGen, topN, baseUrl, authHeaderValu
183
246
  })
184
247
  : scored;
185
248
  const slotOrderedItems = prioritizeAttackSurfaceBundles(scoredAfterExternalDedup);
186
- const generateItems = slotOrderedItems.slice(0, Math.min(backendGenerateCount, slotOrderedItems.length));
187
- const rawAdditionalItems = slotOrderedItems.slice(backendGenerateCount, topN);
249
+ // Distribute the backend GENERATE slots evenly across the test types present
250
+ // (contract vs integration) with spillover, instead of a pure top-N rank slice —
251
+ // so a backend change isn't starved of (say) integration coverage just because
252
+ // contract scenarios out-ranked it. See roundRobinByType.
253
+ const generateItems = roundRobinByType(slotOrderedItems, Math.min(backendGenerateCount, slotOrderedItems.length));
254
+ // ADDITIONAL = everything not chosen for GENERATE, in original rank order, capped
255
+ // at the remaining budget. Computed by set-difference (not a positional slice)
256
+ // because round-robin may pick items that aren't the first backendGenerateCount by
257
+ // rank — a positional slice would double-list a generated item or drop one.
258
+ const generateSet = new Set(generateItems);
259
+ const rawAdditionalItems = slotOrderedItems
260
+ .filter((it) => !generateSet.has(it))
261
+ .slice(0, Math.max(0, topN - backendGenerateCount));
188
262
  // Filter additional items whose primary resource + test type already appear in GENERATE
189
263
  const generatedCoverage = new Set(generateItems.map((item) => scenarioCoverageKey(item.scenario)));
190
264
  const additionalItems = rawAdditionalItems.filter((item) => !generatedCoverage.has(scenarioCoverageKey(item.scenario)));
@@ -345,27 +419,18 @@ export function buildExecutionPlan(scored, maxGen, topN, baseUrl, authHeaderValu
345
419
  // frozen, but the `reasoning` CONTENT for UI entries should be blueprint-
346
420
  // grounded using concrete elements the agent captured via browser_blueprint.
347
421
  const uiGroundingGuidance = hasFrontendChanges ? `
348
- **UI recommendation grounding — applies to \`testType: "ui"\` entries.** The \`reasoning\` field for \`testType: "ui"\` entries MUST contain at least three of {\`role\`, \`accessibleName\`, \`testId\`, \`stableId\`, \`logicalName\`} cited in key=value form. Entries that omit the tuple are not valid output for UI test types. The agent should call \`browser_blueprint\` on affected pages (if it hasn't already) and use the captured data.
349
-
350
- **Field stability note for consumers:** \`testId\` and \`stableId\` are stable identifiers (from \`data-testid\` and unique \`id\` attributes respectively) — code-generation consumers can key off them. \`role\` and \`accessibleName\` are derived from ARIA and survive DOM reshuffles. \`logicalName\` is a **display handle, not a stable identifier** — it's derived from role + accessibleName + section context and drifts when the accessibleName text changes. Cite \`logicalName\` for readability in the \`reasoning\` field, but downstream consumers (test generators, scoping tools) should NOT key off it. Prefer \`testId\` > \`stableId\` > \`role + accessibleName\` > \`fingerprint\` for identity.
351
-
352
- **Format for singular elements:**
353
- > role=<role>, accessibleName="<Accessible Name>", testId=<test-id-or-null>, stableId=<id-or-null>, logicalName=<logical_name>
354
-
355
- **Format for repeating elements (table rows, list items):** include \`contextText\` values from the row when the recommendation targets a specific row:
356
- > role=<role>, accessibleName="<template>", testId=<null>, stableId=<null>, logicalName=<name>, contextText=["customer@example.com", "$129.99", "Pending"]
357
-
358
- **Example (Edit Order form's Save button on /orders/{id}):**
359
- > role=button, accessibleName="Save changes", testId=null, stableId=null, logicalName=save_changes_btn — verifies boundary-value clamping on discount_percent (0..100)
422
+ **UI recommendation grounding — applies to \`testType: "ui"\` entries in BOTH \`newTestsCreated\` and \`additionalRecommendations\`.** UI recommendations must populate three structured fields plus prose fields, all derived from captured \`browser_blueprint\` elements:
360
423
 
361
- **Example (new-order-row recommendation on /orders):**
362
- > role=button, accessibleName="View details for order 13", testId=null, stableId=null, logicalName=view_details_for_order_btn, contextText=["customer@example.com", "$129.99", "Pending"] — verifies new order row renders with correct customer + total + status
424
+ - **\`targetElements\`** — array of one or more elements (\`role\`, \`accessibleName\`, \`testId\`, \`stableId\`, \`contextText\`, \`mutability\`, \`widgetType\`). Single-element for tests with one dominant target; multi-element for render-state, multi-step, or form tests.
425
+ - **\`pageContext\`** \`url\` and \`pageHash\` from the BlueprintCapture.
426
+ - **\`description\`** — walkthrough of the test steps and assertions (what the test does, not why it is valuable).
427
+ - **\`reasoning\`** — free-form prose grounded in \`targetElements\` explaining why this test is valuable. Every element cited must appear in \`targetElements\` by its \`accessibleName\` (bolded, character-for-character). See the Blueprint Citation Invariant in the Testbot prompt (step 4) for citation rules, identifier priority (\`testId\` > \`stableId\` > \`role + accessibleName\`), repeating-element format (\`contextText\` parenthetical), and self-check guidance.
363
428
 
364
429
  **Validates line — applies to \`testType: "ui"\` entries.** The \`Validates:\` line for UI entries should describe an observable behavior the test verifies — what changes on the page after the action, or what state the user can see. Ground this description in the captured blueprint when possible. Reference structural facts (an element appears, a count changes, a status text updates, a URL transitions) rather than implementation language (component names, props, internal state). The line should be readable to someone who has not seen the source diff.
365
430
 
366
- **Scope clarification:** this grounding format applies **only** to \`testType: "ui"\` entries. Contract, integration, e2e, batch-scenario \`reasoning\` and \`Validates:\` fields use their existing conventions (endpoint paths, schemas, fixture chains) — do NOT reformat those. The "Fill in placeholders, then display verbatim" rule above refers to the CATALOG STRUCTURE (sections, ordering, test types); UI entries' \`reasoning\` and \`Validates:\` CONTENT follows this grounding format.
431
+ **Scope clarification:** this grounding rule applies **only** to \`testType: "ui"\` entries. Contract, integration, e2e, batch-scenario \`reasoning\` and \`Validates:\` fields use their existing conventions (endpoint paths, schemas, fixture chains) — do NOT reformat those. The "Fill in placeholders, then display verbatim" rule above refers to the CATALOG STRUCTURE (sections, ordering, test types); UI entries' \`reasoning\` and \`Validates:\` CONTENT follows this grounding rule.
367
432
 
368
- **If blueprint data isn't available** — agent skipped pre-scan, app unreachable, \`BlueprintInvariantError\`, or no candidate page covers the changed component — UI entries may fall back to source-grounded reasoning. Each such entry MUST be flagged with a leading \`[no-blueprint-data]\` marker in the \`reasoning\` field, and the failure mode must be logged in \`issuesFound\` with \`info\` severity naming the entry. Do NOT silently produce ungrounded reasoning without the marker.
433
+ **If blueprint data isn't available** — agent skipped pre-scan, app unreachable, \`BlueprintInvariantError\`, or no candidate page covers the changed component — UI entries must fall back as follows: set \`targetElements: null\`, omit \`pageContext\`, prefix BOTH \`description\` and \`reasoning\` with \`[no-blueprint-data]\`, and log the failure in \`issuesFound\` with \`info\` severity. Do NOT silently produce ungrounded reasoning without the marker in both fields.
369
434
  ` : "";
370
435
  // UI/E2E guidance — the LLM adds as many as its Budget Plan calls for.
371
436
  // Note: if a UI test already occupies a GENERATE slot (uiPlaceholderBlock), that slot
@@ -0,0 +1,7 @@
1
+ import { DraftedScenario } from "../../types/RepositoryAnalysis.js";
2
+ import { PriorityTier } from "../../types/TestRecommendation.js";
3
+ export declare function buildFullRepoRecommendations(scored: Array<{
4
+ scenario: DraftedScenario;
5
+ priority: PriorityTier;
6
+ novelty: string;
7
+ }>, topN: number, baseUrl: string, authHeaderValue: string, authSchemeSnippet: string, authTypeValue: string, isFrontendProject?: boolean, isFrontendOnlyProject?: boolean, externalCoverage?: Set<string>): string;
@@ -1,3 +1,4 @@
1
+ import { jest } from "@jest/globals";
1
2
  jest.mock("@skyramp/skyramp", () => ({ Skyramp: class {
2
3
  } }));
3
4
  import { mergeEnrichedScenarios } from "./registerRecommendTestsPrompt.js";
@@ -0,0 +1,203 @@
1
+ /**
2
+ * PromptPlan — structured prompt composition with phases, steps, and sub-steps.
3
+ *
4
+ * Design goals:
5
+ * - Main steps are always non-conditional; their labels (1, 2, 3, …) are
6
+ * static string constants usable in tests without calling any function.
7
+ * - Sub-steps are sequentially numbered within their parent (1.1, 1.2, 2.1, …).
8
+ * No manually chosen suffixes — insertion or removal automatically renumbers.
9
+ * - Conditional sub-steps: when absent, remaining siblings re-number so the
10
+ * LLM never sees a gap in the step sequence.
11
+ * - Phases group steps under an optional named heading with a configurable
12
+ * rendering style.
13
+ * - preview(params) returns a structured snapshot for developer inspection:
14
+ * which steps are active, what their labels are, and why conditional ones
15
+ * are absent.
16
+ *
17
+ * Usage:
18
+ * const _plan = new PromptPlan<MyParams>()
19
+ * .addPhase("main", "Task Instructions", { headerLevel: "##", stepFormat: "hash" })
20
+ * .step("READ", "Read the files", (p) => buildReadBody(p))
21
+ * .subStep("PATHS", "Resolve paths", (p) => buildPathsBody(p))
22
+ * .step("EXTRACT", "Extract items", (p) => buildExtractBody(p))
23
+ * .subStep("TRACE", "Trace callers", (p) => buildTraceBody(p),
24
+ * { when: (p) => p.hasUnmatched, whenDesc: "unmatched backend files present" })
25
+ * .step("CALL", "Call the tool", (p) => buildCallBody(p))
26
+ * .done();
27
+ *
28
+ * // Static constants for non-conditional steps (safe to export at module level)
29
+ * export const STEP_READ = _plan.labels.READ; // "1"
30
+ * export const STEP_PATHS = _plan.labels.PATHS; // "1.1"
31
+ * export const STEP_EXTRACT = _plan.labels.EXTRACT; // "2"
32
+ * export const STEP_CALL = _plan.labels.CALL; // "3"
33
+ *
34
+ * // Dynamic accessor for the conditional sub-step
35
+ * export const getStepTrace = (p: MyParams) => _plan.labelFor("TRACE", p); // "2.1" | null
36
+ *
37
+ * // Render for the LLM
38
+ * const promptText = _plan.render(params);
39
+ *
40
+ * // Developer review
41
+ * console.log(_plan.preview(params));
42
+ */
43
+ export type StepBody<P> = (params: P) => string;
44
+ /** Step title: either a static string or a function for dynamic titles. */
45
+ export type StepTitle<P> = string | ((params: P) => string);
46
+ export type StepCondition<P> = (params: P) => boolean;
47
+ interface SubStepDef<P> {
48
+ key: string;
49
+ title: StepTitle<P>;
50
+ body: StepBody<P>;
51
+ condition?: StepCondition<P>;
52
+ conditionDesc?: string;
53
+ }
54
+ interface StepDef<P> {
55
+ key: string;
56
+ title: StepTitle<P>;
57
+ body: StepBody<P>;
58
+ subSteps: SubStepDef<P>[];
59
+ }
60
+ interface PhaseDef<P> {
61
+ key: string;
62
+ title: string;
63
+ headerLevel: "##" | "###" | "hidden";
64
+ stepFormat: "hash" | "bold";
65
+ steps: StepDef<P>[];
66
+ }
67
+ export interface SubStepPreview {
68
+ key: string;
69
+ /** Computed label if active (e.g. "2.1"), or "N.?" if conditional and absent. */
70
+ label: string;
71
+ title: string;
72
+ active: boolean;
73
+ /** Human-readable description of the condition gating this sub-step. */
74
+ conditionDesc?: string;
75
+ }
76
+ export interface StepPreview {
77
+ key: string;
78
+ label: string;
79
+ title: string;
80
+ subSteps: SubStepPreview[];
81
+ }
82
+ export interface PhasePreview {
83
+ key: string;
84
+ title: string;
85
+ steps: StepPreview[];
86
+ }
87
+ export interface PlanPreview {
88
+ phases: PhasePreview[];
89
+ /** Total steps (main + sub-steps) that are active for the given params. */
90
+ activeStepCount: number;
91
+ /** Total steps registered in the plan, including conditional ones. */
92
+ totalStepCount: number;
93
+ }
94
+ export declare class PhaseBuilder<P extends object> {
95
+ private readonly _plan;
96
+ private readonly _phase;
97
+ private _lastStep;
98
+ constructor(plan: PromptPlan<P>, phase: PhaseDef<P>);
99
+ /**
100
+ * Add a non-conditional main step.
101
+ * Main steps always appear in the rendered output and always carry a stable
102
+ * label (1, 2, 3, …) regardless of which sub-steps are active.
103
+ */
104
+ step(key: string, title: StepTitle<P>, body: StepBody<P>): this;
105
+ /**
106
+ * Add a sub-step under the most recently declared main step.
107
+ * Sub-steps are sequentially numbered within their parent (1.1, 1.2, …).
108
+ *
109
+ * When opts.when is provided the sub-step is conditional: if the condition
110
+ * returns false for the given params, the step is omitted and remaining
111
+ * siblings re-number so the LLM never sees a gap.
112
+ *
113
+ * @throws when called before any step() in this phase.
114
+ */
115
+ subStep(key: string, title: StepTitle<P>, body: StepBody<P>, opts?: {
116
+ when?: StepCondition<P>;
117
+ whenDesc?: string;
118
+ }): this;
119
+ /** Finish building this phase and return the parent PromptPlan. */
120
+ done(): PromptPlan<P>;
121
+ }
122
+ export declare class PromptPlan<P extends object> {
123
+ private readonly _phases;
124
+ private readonly _startFrom;
125
+ /** Track registered step/sub-step keys for duplicate detection. */
126
+ private readonly _registeredKeys;
127
+ /**
128
+ * @param opts.startFrom - Label of the first main step. Defaults to 1.
129
+ * Use 0 for execution-plan style (Step 0, Step 1, Step 2, …).
130
+ */
131
+ constructor(opts?: {
132
+ startFrom?: number;
133
+ });
134
+ /** @internal Register a key and throw if already taken. */
135
+ _registerKey(key: string): void;
136
+ /**
137
+ * Add a phase and return a PhaseBuilder to register its steps.
138
+ * Multiple phases share a single global main-step counter, so step labels
139
+ * increment continuously across phase boundaries.
140
+ */
141
+ addPhase(key: string, title: string, opts?: {
142
+ /** Markdown heading level for the phase header. "hidden" emits no header. */
143
+ headerLevel?: "##" | "###" | "hidden";
144
+ /**
145
+ * Step header rendering style.
146
+ * "hash" => "### Step N: Title" (analysis prompt style)
147
+ * "bold" => "**Step N — Title**" (execution plan style)
148
+ */
149
+ stepFormat?: "hash" | "bold";
150
+ }): PhaseBuilder<P>;
151
+ /**
152
+ * Static label map for all non-conditional steps (main steps and
153
+ * unconditional sub-steps). Conditional sub-steps are not present here —
154
+ * use labelFor() for those.
155
+ *
156
+ * Safe to export as module-level constants for use in tests.
157
+ *
158
+ * **Ordering constraint**: unconditional sub-steps that appear *after* a
159
+ * conditional sibling will have a stable label here only if no conditional
160
+ * siblings precede them. When a conditional sibling is active at runtime,
161
+ * labelFor() will assign a higher number than the static label here. To
162
+ * avoid this mismatch, place conditional sub-steps AFTER all unconditional
163
+ * siblings, or use labelFor() for any sub-step with a conditional predecessor.
164
+ *
165
+ * O(steps) per call — intentionally avoids a stale-cache window during plan
166
+ * construction. Plans have ~6 steps so the cost is negligible; exported
167
+ * constants are evaluated once at module-load time.
168
+ */
169
+ get labels(): Record<string, string>;
170
+ /**
171
+ * Dynamic label for any registered step given runtime params.
172
+ *
173
+ * Returns null when the step has a condition that evaluates to false.
174
+ * Throws when key is not registered in this plan.
175
+ *
176
+ * For non-conditional steps the result equals labels[key], but using
177
+ * labels is preferred for those since it avoids passing params.
178
+ */
179
+ labelFor(key: string, params: P): string | null;
180
+ /**
181
+ * Render the full prompt for the given params.
182
+ *
183
+ * - Phase headers are emitted when headerLevel is not "hidden".
184
+ * - Steps are formatted per the phase's stepFormat.
185
+ * - Conditional sub-steps absent for these params are skipped; remaining
186
+ * siblings re-number with no gaps.
187
+ * - Each section (header + body) is separated by a single blank line.
188
+ */
189
+ render(params: P): string;
190
+ /**
191
+ * Return a structured preview of all steps and their active state for the
192
+ * given params. Useful in development, tests, and debugging to verify exactly
193
+ * which steps would be emitted and what labels they carry.
194
+ *
195
+ * @example
196
+ * const pv = _plan.preview({ ...params, unmatchedFiles: [] });
197
+ * // pv.phases[0].steps[1].subSteps[0] =>
198
+ * // { key: "TRACE_CALLERS", label: "2.?", active: false,
199
+ * // conditionDesc: "non-frontend unmatched backend files present" }
200
+ */
201
+ preview(params: P): PlanPreview;
202
+ }
203
+ export {};
@@ -0,0 +1,35 @@
1
+ export declare const MAX_TESTS_TO_GENERATE = 3;
2
+ export declare const MAX_RECOMMENDATIONS = 20;
3
+ export declare const MAX_CRITICAL_TESTS = 3;
4
+ /**
5
+ * Error string emitted by skyramp_integration_test_generation when both
6
+ * an explicit authHeader and a workspace api.authType are passed simultaneously.
7
+ * Both the tool description and all prompt locations import this constant,
8
+ * so every occurrence is character-for-character identical by construction.
9
+ */
10
+ export declare const AUTH_CONFLICT_ERROR_MSG = "Auth header and auth type cannot be supported at the same time.";
11
+ export declare function buildArchitectPreamble(isDiffScope: boolean): string;
12
+ export declare function buildContextFetchingGuidance(sessionId?: string): string;
13
+ export declare function buildReasoningProtocol(): string;
14
+ export interface AuthCallParams {
15
+ authHeader: string;
16
+ authScheme?: string;
17
+ authToken?: string;
18
+ }
19
+ /**
20
+ * Returns the authScheme snippet for a given authHeader + authType combination.
21
+ *
22
+ * Delegates scheme resolution to getAuthScheme() in workspaceAuth.ts — the single
23
+ * source of truth — so this file no longer maintains its own slug→scheme table.
24
+ */
25
+ export declare function getAuthSnippets(authHeaderValue: string, authType?: string, explicitScheme?: string): {
26
+ authSchemeSnippet: string;
27
+ authTokenSnippet: string;
28
+ };
29
+ export declare const PATH_PARAM_UUID_GUIDANCE: string;
30
+ export declare function buildTestPatternGuidelines(): string;
31
+ export declare function buildTestQualityCriteria(): string;
32
+ export declare function buildGenerationRules(isUIOnlyPR: boolean): string;
33
+ export declare function buildVerificationChecklist(topN: number, maxGen: number, codeReviewStepLabel?: string): string;
34
+ export declare function buildFewShotExamples(): string;
35
+ export declare function buildToolWorkflows(authHeaderValue: string, authTypeValue?: string, explicitScheme?: string): string;
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Shared dedup primitives used by both diff-mode (diffExecutionPlan.ts) and
3
+ * full-repo mode (fullRepoCatalog.ts). Extracted here to avoid circular imports.
4
+ */
5
+ import { DraftedScenario } from "../../types/RepositoryAnalysis.js";
6
+ export declare function scenarioCoverageKey(scenario: DraftedScenario): string;
7
+ /**
8
+ * Method-aware coverage key for external test dedup.
9
+ * Unlike scenarioCoverageKey (resource::testType), this includes the HTTP method
10
+ * so that e.g. an external test covering "GET /orders" doesn't block generating
11
+ * a test for "PUT /orders" — a different operation on the same resource.
12
+ */
13
+ export declare function externalDedupKey(scenario: DraftedScenario): string;
14
+ export declare function isAttackSurfaceSecurityBoundary(scenario: DraftedScenario): boolean;
15
+ export declare function isOrdinaryDirectAuthBoundary(scenario: DraftedScenario): boolean;
16
+ /**
17
+ * Build a set of coverage keys from external (non-Skyramp) tests.
18
+ * Parses `testLocations` entries tagged with `[external]` to extract the
19
+ * method-aware `METHOD::resource::testType` keys they cover.
20
+ */
21
+ export declare function buildExternalCoverageSet(testLocations: Record<string, string>): Set<string>;
22
+ export declare const TASK_UI_PRESCAN = "0";
23
+ export declare const TASK_ANALYZE_MAINTAIN = "1";
24
+ export declare const TASK_GENERATE = "2";
25
+ export declare const TASK_SUBMIT = "3";
26
+ export declare const TESTBOT_TASK1_STEP_ANALYZE = "1";
27
+ export declare const TESTBOT_TASK1_STEP_MAINTAIN = "2";
28
+ export declare const TESTBOT_TASK1_STEP_CODE_REVIEW = "3";
29
+ export declare const TESTBOT_TASK1_STEP_UI_GROUNDING = "4";
30
+ export declare const taskRef: (taskId: string) => string;
31
+ export declare const taskStepRef: (taskId: string, stepId: string) => string;
@@ -0,0 +1,7 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { DraftedScenario } from "../../types/RepositoryAnalysis.js";
3
+ export declare function mergeEnrichedScenarios(serverScenarios: DraftedScenario[], raw: string): {
4
+ scenarios: DraftedScenario[];
5
+ rejectionNotes: string[];
6
+ };
7
+ export declare function registerRecommendTestsPrompt(server: McpServer): void;
@@ -0,0 +1,55 @@
1
+ export declare function hasFlutterSdkDep(repositoryPath: string): boolean;
2
+ /**
3
+ * Returns true if the file path is a frontend file.
4
+ *
5
+ * Four tiers:
6
+ * 1. .vue / .svelte — always frontend; these file types cannot be route handlers.
7
+ * 2. .dart — always frontend IFF `hasFlutterSdkDep` is true (Flutter project).
8
+ * Sits in tier 1 because there's no server-side Dart web framework in scope
9
+ * when the Flutter SDK dep is present, so `API_DIR_PATTERN` doesn't apply.
10
+ * 3. .tsx / .jsx — frontend unless in an API/backend directory (e.g. pages/api/).
11
+ * 4. Ambiguous extensions (.ts, .js, .css, .html, …) — require a recognised
12
+ * frontend directory AND must not be in an API/backend directory.
13
+ *
14
+ * The `hasFlutterSdkDep` opt is passed in by the budget-driving caller
15
+ * (analyzeChangesTool) which has repository context.
16
+ * Pure-path callers (test-recommendation-prompt, analysisOutputPrompt,
17
+ * scopeAssessment's own buildScopeAssessmentSection) default to false —
18
+ * they don't drive the budget.
19
+ */
20
+ export declare function isFrontendFile(filePath: string, { hasFlutterSdkDep }?: {
21
+ hasFlutterSdkDep?: boolean;
22
+ }): boolean;
23
+ /**
24
+ * Returns true if the file path looks like a test file rather than UI source.
25
+ *
26
+ * Catches:
27
+ * - Skyramp-generated tests (`*_test.ts`, `*_smoke.ts`, `*_contract.ts`,
28
+ * `*_fuzz.ts`, `*_integration.ts`, `*_load.ts`, `*_e2e.ts`, `*_ui.ts`)
29
+ * - Skyramp scenario files (`scenario_*.json`)
30
+ * - Conventional Vitest/Jest/Playwright spec naming
31
+ * (`*.spec.{ts,tsx,js,jsx}`, `*.test.{ts,tsx,js,jsx}`)
32
+ * - Files inside `__tests__/` directories (Jest convention)
33
+ *
34
+ * Used by callers that want to filter test files OUT of frontend-source
35
+ * processing — `isFrontendFile` returns true for `.spec.ts` under a
36
+ * frontend directory because the rule is "tier-3 ambiguous + frontend dir,"
37
+ * but those tests aren't UI source we'd want to ground recommendations in.
38
+ */
39
+ export declare function isTestFile(filePath: string): boolean;
40
+ /**
41
+ * Builds the PR scope assessment section.
42
+ *
43
+ * When `precomputedUIPct` is provided (0 = backend-only, 100 = UI-only) the server
44
+ * has already determined the split unambiguously — skip Steps A–C and emit one line.
45
+ *
46
+ * For mixed PRs (`precomputedUIPct` is undefined, `hasFrontendChanges` is true) skip
47
+ * Steps A–C but keep Step D so the LLM can apply judgment to determine the UI%.
48
+ *
49
+ * Falls back to the full four-step assessment when no precomputed data is available.
50
+ */
51
+ export declare function buildScopeAssessmentSection(maxTotal?: number, maxGenerate?: number, isUIOnly?: boolean,
52
+ /** Server-determined UI/E2E percentage. `undefined` = mixed PR or unknown. */
53
+ precomputedUIPct?: number,
54
+ /** Whether the diff contains frontend files (true ↔ mixed PR when precomputedUIPct is undefined). */
55
+ hasFrontendChanges?: boolean): string;
@@ -109,8 +109,8 @@ function checkPubspec(pubspecPath) {
109
109
  * 4. Ambiguous extensions (.ts, .js, .css, .html, …) — require a recognised
110
110
  * frontend directory AND must not be in an API/backend directory.
111
111
  *
112
- * The `hasFlutterSdkDep` opt is passed in by the budget-driving callers
113
- * (uiAnalyzeChangesTool, analyzeChangesTool) which have repository context.
112
+ * The `hasFlutterSdkDep` opt is passed in by the budget-driving caller
113
+ * (analyzeChangesTool) which has repository context.
114
114
  * Pure-path callers (test-recommendation-prompt, analysisOutputPrompt,
115
115
  * scopeAssessment's own buildScopeAssessmentSection) default to false —
116
116
  * they don't drive the budget.
@@ -1,3 +1,4 @@
1
+ import { jest } from "@jest/globals";
1
2
  jest.mock("@skyramp/skyramp", () => ({
2
3
  WorkspaceConfigManager: { create: jest.fn() },
3
4
  }));
@@ -0,0 +1,6 @@
1
+ import { RepositoryAnalysis, AnalysisScope } from "../../types/RepositoryAnalysis.js";
2
+ import { WorkspaceAuthType } from "../../utils/workspaceAuth.js";
3
+ import { PRTestContext } from "../../utils/pr-comment-parser.js";
4
+ import { buildExternalCoverageSet, externalDedupKey } from "./recommendationShared.js";
5
+ export { buildExternalCoverageSet, externalDedupKey };
6
+ export declare function buildRecommendationPrompt(analysis: RepositoryAnalysis, analysisScope?: AnalysisScope, topN?: number, prContext?: PRTestContext, workspaceAuthHeader?: string, workspaceAuthType?: WorkspaceAuthType, workspaceAuthScheme?: string, maxGenerateOverride?: number, sessionId?: string): string;