@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,242 @@
1
+ import { TestAnalysisResult } from "../types/TestAnalysis.js";
2
+ import { RepositoryAnalysis, AnalysisScope } from "../types/RepositoryAnalysis.js";
3
+ import { PRTestContext } from "./pr-comment-parser.js";
4
+ import { ParsedDiffEndpoint } from "./routeParsers.js";
5
+ import type { CandidateUiPage } from "./uiPageEnumerator.js";
6
+ export type { CandidateUiPage } from "./uiPageEnumerator.js";
7
+ export declare function setTestsRepoDir(dir: string | undefined): void;
8
+ export declare function getTestsRepoDir(): string | undefined;
9
+ export declare function setActiveRunStatePath(stateFilePath: string): void;
10
+ export declare function getActiveRunStatePath(): string | undefined;
11
+ /** Reset the run anchor — for tests, and as a safety hook between runs. */
12
+ export declare function clearActiveRunStatePath(): void;
13
+ export declare function registerSession(sessionId: string, stateFilePath: string): void;
14
+ export declare function getSessionFilePath(sessionId: string): string | undefined;
15
+ export declare function getRegisteredSessions(): ReadonlyMap<string, string>;
16
+ export declare function storeSessionData(sessionId: string, data: any): void;
17
+ export declare function getSessionData(sessionId: string): any | undefined;
18
+ export declare function hasSessionData(sessionId: string): boolean;
19
+ /**
20
+ * Normalize a loaded RecommendationState: if the LLM saved a raw
21
+ * RepositoryAnalysis at the root (without the wrapper), detect it
22
+ * and wrap it properly.
23
+ */
24
+ export declare function normalizeRecommendationState(data: RecommendationState): RecommendationState;
25
+ /**
26
+ * State types supported by the manager
27
+ */
28
+ export type StateType = "analysis" | "recommendation";
29
+ /**
30
+ * State data for test maintenance workflow (discovery, drift, execution, health)
31
+ */
32
+ export interface TestAnalysisState {
33
+ tests: TestAnalysisResult[];
34
+ }
35
+ /**
36
+ * State data for test recommendation workflow (analyze repo, recommend tests)
37
+ */
38
+ export interface RecommendationState {
39
+ repositoryPath: string;
40
+ analysisScope?: AnalysisScope;
41
+ analysis: RepositoryAnalysis;
42
+ prContext?: PRTestContext;
43
+ }
44
+ /**
45
+ * Map of test type → numeric score (e.g., priority, relevance, confidence).
46
+ */
47
+ type TestTypeScores = Record<string, number>;
48
+ /**
49
+ * Map of test type → grouping/label information used by downstream tools.
50
+ */
51
+ type TestTypeMapping = Record<string, string | string[]>;
52
+ /**
53
+ * UI-specific analysis context populated when the diff contains frontend files.
54
+ *
55
+ * Computed deterministically by `skyramp_analyze_changes` (via `isFrontendFile`
56
+ * in scopeAssessment.ts). Persisted so downstream consumers —
57
+ * `skyramp_analyze_test_health`'s UI drift detection, recommendation prompt
58
+ * rendering, etc. — don't have to re-derive the classification.
59
+ *
60
+ * Absent on backend-only PRs (no frontend files in the diff).
61
+ */
62
+ export interface UiAnalysisContext {
63
+ /**
64
+ * Subset of `repositoryAnalysis.diff.changedFiles` that classified as
65
+ * frontend by `isFrontendFile()`. Empty when no frontend files changed —
66
+ * callers can derive `hasFrontendChanges` from `changedFrontendFiles.length > 0`.
67
+ */
68
+ changedFrontendFiles: string[];
69
+ /**
70
+ * Candidate pages the testbot agent should capture blueprints for, enumerated
71
+ * by the strategy ladder in `uiPageEnumerator.ts` (framework route grep,
72
+ * source-grounded routes, root fallback). Each entry carries the candidate
73
+ * URL plus the strategy + source files that surfaced it. May be empty when
74
+ * the workspace has no resolvable frontend baseUrl — caller should fall back
75
+ * to the agent's prose-driven enumeration.
76
+ */
77
+ candidateUiPages: CandidateUiPage[];
78
+ }
79
+ /**
80
+ * Unified state data combining test discovery + endpoint scanning
81
+ * Used by the unified test-management workflow
82
+ */
83
+ export interface UnifiedAnalysisState {
84
+ existingTests: TestAnalysisResult[];
85
+ repositoryAnalysis?: any;
86
+ testTypeScores?: TestTypeScores;
87
+ testTypeMapping?: TestTypeMapping;
88
+ analysisScope: AnalysisScope;
89
+ newEndpoints: ParsedDiffEndpoint[];
90
+ /**
91
+ * UI-specific state populated when the diff contains frontend files.
92
+ * Absent on backend-only PRs.
93
+ */
94
+ uiContext?: UiAnalysisContext;
95
+ }
96
+ /**
97
+ * State file metadata
98
+ */
99
+ interface StateMetadata {
100
+ sessionId: string;
101
+ stateType: StateType;
102
+ repositoryPath?: string;
103
+ /** owner/repo of the PRIMARY repo, recorded on the root so related-repo writes
104
+ * can be distinguished from the primary. Absent only for legacy single-repo
105
+ * state files written before explicit tagging. */
106
+ repository?: string;
107
+ createdAt: string;
108
+ updatedAt: string;
109
+ step?: string;
110
+ }
111
+ /**
112
+ * One related repo's analysis section in a run-scoped (multi-repo) state file.
113
+ * The primary repo's analysis stays at the root for backward compatibility; each
114
+ * related repo's analysis is stored here keyed by its `owner/repo`.
115
+ */
116
+ interface RepoStateSection<T> {
117
+ /** Analysis payload — same shape as the root (e.g. UnifiedAnalysisState). */
118
+ data: T;
119
+ /** Absolute checkout path of this repo (used to re-absolutize test paths). */
120
+ repositoryPath?: string;
121
+ }
122
+ /**
123
+ * State file format - data fields at root level with metadata.
124
+ * Example: { tests: [...], metadata: {...} }
125
+ * Example: { repositoryPath: "...", analysis: {...}, mapping: {...}, metadata: {...} }
126
+ *
127
+ * Multi-repo (run-scoped) files additionally carry `relatedRepos`: a map of
128
+ * `owner/repo` → that repo's analysis section. State is per-RUN, not per-repo —
129
+ * one file holds the primary (at root) plus every related repo's section, so
130
+ * there is exactly one stateFile per Testbot run. A file with no `relatedRepos`
131
+ * key is a single-repo file and behaves exactly as before.
132
+ */
133
+ type StateFile<T> = T & {
134
+ metadata: StateMetadata;
135
+ relatedRepos?: Record<string, RepoStateSection<T>>;
136
+ };
137
+ /**
138
+ * Generic State Manager for persisting workflow data
139
+ * Reduces token usage by storing intermediate results in filesystem
140
+ *
141
+ * Supports two state types:
142
+ * - analysis: Test maintenance workflow (discovery, drift, execution, health)
143
+ * - recommendation: Test recommendation workflow (analyze repo, map tests, recommendations)
144
+ */
145
+ export declare class StateManager<T = any> {
146
+ private stateFile;
147
+ private sessionId;
148
+ private stateType;
149
+ /**
150
+ * Create a new state manager
151
+ * @param stateType Type of state (analysis, recommendation)
152
+ * @param sessionId Unique session identifier (defaults to UUID)
153
+ * @param stateDir Directory to store state files (defaults to /tmp)
154
+ */
155
+ constructor(stateType?: StateType, sessionId?: string, stateDir?: string, stateFilePath?: string);
156
+ /**
157
+ * Create state manager from a sessionId (resolves the state file path internally)
158
+ */
159
+ static fromSessionId<T = any>(sessionId: string, stateType?: StateType, stateDir?: string): StateManager<T>;
160
+ /**
161
+ * Create state manager from existing state file path
162
+ */
163
+ static fromStatePath<T = any>(stateFilePath: string): StateManager<T>;
164
+ /**
165
+ * Read data from state file (excludes metadata)
166
+ */
167
+ readData(): Promise<T | null>;
168
+ /**
169
+ * Read full state including metadata
170
+ */
171
+ readFullState(): Promise<StateFile<T> | null>;
172
+ /**
173
+ * Write data to state file
174
+ * Data fields are spread at root level alongside metadata
175
+ */
176
+ writeData(data: T, options?: {
177
+ repositoryPath?: string;
178
+ step?: string;
179
+ /** owner/repo recorded on the root metadata — identifies the PRIMARY repo
180
+ * so later section routing (isPrimaryRepo) can compare against it.
181
+ * Preserved when omitted. */
182
+ repository?: string;
183
+ }): Promise<void>;
184
+ /** Is `repo` the primary section? Primary when `repo` is falsy (single-repo /
185
+ * legacy callers), when no state file exists yet (the FIRST write of a run is
186
+ * the primary — the testbot prompt mandates the primary is analyzed first), or
187
+ * when `repo` matches the `repo` recorded on the root metadata. A non-empty
188
+ * `repo` that doesn't match an already-recorded root is a RELATED repo. */
189
+ private isPrimaryRepo;
190
+ /**
191
+ * Write one repo's analysis into the run-scoped state file. The primary repo
192
+ * (repo absent, or matching the root's recorded repo, or the first write of the
193
+ * run) is written to the root via writeData(); a related repo is upserted into
194
+ * `relatedRepos[repo]`, preserving the primary + other sections. Used by
195
+ * skyramp_analyze_changes — every repo in the run writes the SAME stateFile path.
196
+ */
197
+ writeRepoData(data: T, options: {
198
+ repo?: string;
199
+ repositoryPath?: string;
200
+ step?: string;
201
+ }): Promise<void>;
202
+ /**
203
+ * Read one repo's analysis payload from the run-scoped state file. `repo`
204
+ * undefined → the primary (root) section (identical to readData()).
205
+ */
206
+ readRepoData(repo?: string): Promise<T | null>;
207
+ /**
208
+ * Resolve the checkout path for a repo's section — used to re-absolutize test
209
+ * paths. `repo` undefined → the primary's metadata.repositoryPath.
210
+ */
211
+ getRepoRepositoryPath(repo?: string): Promise<string | undefined>;
212
+ /** List the related repo keys present in the run-scoped file (empty if single-repo). */
213
+ listRelatedRepos(): Promise<string[]>;
214
+ getStatePath(): string;
215
+ getSessionId(): string;
216
+ getStateType(): StateType;
217
+ exists(): boolean;
218
+ delete(): Promise<void>;
219
+ getSize(): Promise<number>;
220
+ getSizeFormatted(): Promise<string>;
221
+ /**
222
+ * Cleanup old state files
223
+ * @param maxAgeHours Maximum age in hours
224
+ * @param stateDir Directory to clean (defaults to /tmp)
225
+ * @param stateTypes Which state types to clean (defaults to all)
226
+ * @returns Number of files deleted
227
+ */
228
+ static cleanupOldFiles(maxAgeHours?: number, stateDir?: string, stateTypes?: StateType[]): Promise<number>;
229
+ /**
230
+ * List all state files in directory
231
+ * @param stateDir Directory to search (defaults to /tmp)
232
+ * @param stateTypes Which state types to list (defaults to all)
233
+ */
234
+ static listStateFiles(stateDir?: string, stateTypes?: StateType[]): Promise<Array<{
235
+ sessionId: string;
236
+ stateType: StateType;
237
+ path: string;
238
+ size: number;
239
+ createdAt: Date;
240
+ modifiedAt: Date;
241
+ }>>;
242
+ }
@@ -22,6 +22,29 @@ export function setTestsRepoDir(dir) {
22
22
  export function getTestsRepoDir() {
23
23
  return _testsRepoDir;
24
24
  }
25
+ /**
26
+ * Multi-repo run anchor: the path of the run-scoped analysis state file for the
27
+ * current Testbot run. One Testbot run = one MCP server process, so a single
28
+ * module-level value is naturally run-scoped (same pattern as _testsRepoDir and
29
+ * processSessionRegistry above).
30
+ *
31
+ * skyramp_analyze_changes sets this on the FIRST call of a multi-repo run (the one
32
+ * carrying a `repository`) and REUSES it on subsequent related-repo calls, so every
33
+ * repo's analysis lands in ONE state file with per-repo sections — without the agent
34
+ * ever passing a file path (which previously caused filename hallucinations).
35
+ * Single-repo / standalone calls (no `repository`) never set or read this.
36
+ */
37
+ let _activeRunStatePath;
38
+ export function setActiveRunStatePath(stateFilePath) {
39
+ _activeRunStatePath = stateFilePath;
40
+ }
41
+ export function getActiveRunStatePath() {
42
+ return _activeRunStatePath;
43
+ }
44
+ /** Reset the run anchor — for tests, and as a safety hook between runs. */
45
+ export function clearActiveRunStatePath() {
46
+ _activeRunStatePath = undefined;
47
+ }
25
48
  /**
26
49
  * In-memory session store: sessionId → { data, storedAt }.
27
50
  * Eliminates the need for the LLM to read/write state files on disk.
@@ -119,6 +142,15 @@ export class StateManager {
119
142
  if (stateFilePath) {
120
143
  this.stateFile = stateFilePath;
121
144
  }
145
+ else if (stateType === "analysis" && process.env.RUNNER_TEMP) {
146
+ // In CI (testbot action), RUNNER_TEMP is a job-isolated dir that both the
147
+ // action and this tool can independently derive — no LLM input needed.
148
+ // TODO: interim step — remove this branch when stateFile and summaryOutputFile are merged
149
+ // (see getTestbotPrompt TODO). RUNNER_TEMP is GitHub Actions-specific; other CI systems
150
+ // (Jenkins, GitLab, Buildkite) must set it explicitly. Once merged, path flows through
151
+ // summaryOutputFile which is already testbot-controlled and CI-agnostic.
152
+ this.stateFile = path.join(process.env.RUNNER_TEMP, "skyramp", "analyze-changes-state.json");
153
+ }
122
154
  else {
123
155
  const baseDir = stateDir || os.tmpdir();
124
156
  const prefix = STATE_FILE_PREFIXES[stateType];
@@ -161,7 +193,11 @@ export class StateManager {
161
193
  }
162
194
  try {
163
195
  const content = await fs.promises.readFile(this.stateFile, "utf-8");
164
- const { metadata, ...data } = JSON.parse(content);
196
+ // Strip both metadata and the multi-repo relatedRepos section so callers
197
+ // get only the PRIMARY repo's analysis payload (unchanged single-repo shape).
198
+ const { metadata, relatedRepos, ...data } = JSON.parse(content);
199
+ void metadata;
200
+ void relatedRepos;
165
201
  logger.debug(`Read data from state file: ${this.stateFile}`);
166
202
  return data;
167
203
  }
@@ -192,11 +228,14 @@ export class StateManager {
192
228
  */
193
229
  async writeData(data, options) {
194
230
  try {
195
- // Read existing metadata if file exists
231
+ // Read existing metadata + any related-repo sections if file exists, so
232
+ // rewriting the primary (root) payload preserves the multi-repo sections.
196
233
  let existingMetadata;
234
+ let existingRelated;
197
235
  if (this.exists()) {
198
236
  const existing = await this.readFullState();
199
237
  existingMetadata = existing?.metadata;
238
+ existingRelated = existing?.relatedRepos;
200
239
  }
201
240
  const state = {
202
241
  ...data,
@@ -204,10 +243,12 @@ export class StateManager {
204
243
  sessionId: this.sessionId,
205
244
  stateType: this.stateType,
206
245
  repositoryPath: options?.repositoryPath || existingMetadata?.repositoryPath,
246
+ repository: options?.repository ?? existingMetadata?.repository,
207
247
  createdAt: existingMetadata?.createdAt || new Date().toISOString(),
208
248
  updatedAt: new Date().toISOString(),
209
249
  step: options?.step,
210
250
  },
251
+ ...(existingRelated ? { relatedRepos: existingRelated } : {}),
211
252
  };
212
253
  await fs.promises.mkdir(path.dirname(this.stateFile), { recursive: true });
213
254
  await fs.promises.writeFile(this.stateFile, JSON.stringify(state, null, 2), "utf-8");
@@ -218,6 +259,106 @@ export class StateManager {
218
259
  throw new Error(`Failed to write state file: ${error.message}`);
219
260
  }
220
261
  }
262
+ // -----------------------------------------------------------------------
263
+ // Multi-repo (run-scoped) section access
264
+ //
265
+ // State is per-RUN: one file holds the primary repo at the root plus each
266
+ // related repo under `relatedRepos[<owner/repo>]`. `repo` undefined (or
267
+ // matching the root's metadata.repo) targets the PRIMARY section, so all
268
+ // single-repo behavior is unchanged.
269
+ // -----------------------------------------------------------------------
270
+ /** Is `repo` the primary section? Primary when `repo` is falsy (single-repo /
271
+ * legacy callers), when no state file exists yet (the FIRST write of a run is
272
+ * the primary — the testbot prompt mandates the primary is analyzed first), or
273
+ * when `repo` matches the `repo` recorded on the root metadata. A non-empty
274
+ * `repo` that doesn't match an already-recorded root is a RELATED repo. */
275
+ async isPrimaryRepo(repo) {
276
+ if (!repo)
277
+ return true;
278
+ // First write of the run is the primary (the prompt mandates the primary is
279
+ // analyzed first), even when it carries an explicit owner/repo.
280
+ if (!this.exists())
281
+ return true;
282
+ const full = await this.readFullState();
283
+ // File already exists: primary only if `repo` matches the repo recorded on
284
+ // the root. An untagged root means the primary was written without a repo
285
+ // (legacy/single-repo), so any non-empty `repo` here is a RELATED repo.
286
+ return full?.metadata.repository === repo;
287
+ }
288
+ /**
289
+ * Write one repo's analysis into the run-scoped state file. The primary repo
290
+ * (repo absent, or matching the root's recorded repo, or the first write of the
291
+ * run) is written to the root via writeData(); a related repo is upserted into
292
+ * `relatedRepos[repo]`, preserving the primary + other sections. Used by
293
+ * skyramp_analyze_changes — every repo in the run writes the SAME stateFile path.
294
+ */
295
+ async writeRepoData(data, options) {
296
+ if (await this.isPrimaryRepo(options.repo)) {
297
+ await this.writeData(data, {
298
+ repositoryPath: options.repositoryPath,
299
+ step: options.step,
300
+ // Record the primary's owner/repo on the root so later related-repo
301
+ // writes can distinguish it from a section by matching this value.
302
+ repository: options.repo,
303
+ });
304
+ return;
305
+ }
306
+ // Related repo: read the current file (must already exist — primary is
307
+ // analyzed first), upsert this repo's section, write back.
308
+ const existing = (await this.readFullState());
309
+ const base = existing ??
310
+ {
311
+ metadata: {
312
+ sessionId: this.sessionId,
313
+ stateType: this.stateType,
314
+ createdAt: new Date().toISOString(),
315
+ updatedAt: new Date().toISOString(),
316
+ },
317
+ };
318
+ const relatedRepos = { ...(base.relatedRepos ?? {}) };
319
+ // Preserve a previously-stored repositoryPath when this write omits it.
320
+ // Callers that update a section without re-supplying the checkout path (e.g.
321
+ // skyramp_execute_test writing execution results) must not clobber the path
322
+ // that skyramp_analyze_changes recorded — getRepoRepositoryPath() relies on
323
+ // it to re-absolutize that repo's test paths.
324
+ const priorRepositoryPath = relatedRepos[options.repo]?.repositoryPath;
325
+ relatedRepos[options.repo] = {
326
+ data,
327
+ repositoryPath: options.repositoryPath ?? priorRepositoryPath,
328
+ };
329
+ base.relatedRepos = relatedRepos;
330
+ base.metadata = { ...base.metadata, updatedAt: new Date().toISOString() };
331
+ await fs.promises.mkdir(path.dirname(this.stateFile), { recursive: true });
332
+ await fs.promises.writeFile(this.stateFile, JSON.stringify(base, null, 2), "utf-8");
333
+ logger.debug(`Wrote ${options.repo} section to state file: ${this.stateFile}`);
334
+ }
335
+ /**
336
+ * Read one repo's analysis payload from the run-scoped state file. `repo`
337
+ * undefined → the primary (root) section (identical to readData()).
338
+ */
339
+ async readRepoData(repo) {
340
+ if (await this.isPrimaryRepo(repo)) {
341
+ return this.readData();
342
+ }
343
+ const full = await this.readFullState();
344
+ return full?.relatedRepos?.[repo]?.data ?? null;
345
+ }
346
+ /**
347
+ * Resolve the checkout path for a repo's section — used to re-absolutize test
348
+ * paths. `repo` undefined → the primary's metadata.repositoryPath.
349
+ */
350
+ async getRepoRepositoryPath(repo) {
351
+ const full = await this.readFullState();
352
+ if (await this.isPrimaryRepo(repo)) {
353
+ return full?.metadata.repositoryPath;
354
+ }
355
+ return full?.relatedRepos?.[repo]?.repositoryPath;
356
+ }
357
+ /** List the related repo keys present in the run-scoped file (empty if single-repo). */
358
+ async listRelatedRepos() {
359
+ const full = await this.readFullState();
360
+ return Object.keys(full?.relatedRepos ?? {});
361
+ }
221
362
  getStatePath() {
222
363
  return this.stateFile;
223
364
  }
@@ -260,7 +401,7 @@ export class StateManager {
260
401
  * @returns Number of files deleted
261
402
  */
262
403
  static async cleanupOldFiles(maxAgeHours = 24, stateDir, stateTypes) {
263
- const baseDir = stateDir || os.tmpdir();
404
+ const baseDir = stateDir || (process.env.RUNNER_TEMP ? path.join(process.env.RUNNER_TEMP, "skyramp") : os.tmpdir());
264
405
  const files = await fs.promises.readdir(baseDir).catch(() => []);
265
406
  const statePrefixes = stateTypes
266
407
  ? stateTypes.map((t) => STATE_FILE_PREFIXES[t])
@@ -0,0 +1 @@
1
+ export {};
@@ -34,3 +34,101 @@ describe("StateManager.writeData", () => {
34
34
  await fs.promises.rm(nestedDir, { recursive: true, force: true });
35
35
  });
36
36
  });
37
+ describe("StateManager — run-scoped per-repo sections (SKYR-3796)", () => {
38
+ let stateFile;
39
+ const mkState = (tag) => ({
40
+ existingTests: [{ testFile: `${tag}.spec.ts` }],
41
+ analysisScope: AnalysisScope.CurrentBranchDiff,
42
+ newEndpoints: [],
43
+ });
44
+ beforeEach(() => {
45
+ stateFile = path.join(os.tmpdir(), `skyramp-analysis-sections-${Date.now()}.json`);
46
+ });
47
+ afterEach(async () => {
48
+ await fs.promises.rm(stateFile, { force: true });
49
+ });
50
+ it("keeps the primary at root and additional repos in relatedRepos, all in ONE file", async () => {
51
+ const mgr = StateManager.fromStatePath(stateFile);
52
+ // Primary (repo omitted) → root; additional repo → section.
53
+ await mgr.writeRepoData(mkState("primary"), { repositoryPath: "/work/primary" });
54
+ await mgr.writeRepoData(mkState("api"), {
55
+ repo: "letsramp/api-insight",
56
+ repositoryPath: "/work/api-insight",
57
+ });
58
+ // Exactly one file on disk.
59
+ expect(fs.existsSync(stateFile)).toBe(true);
60
+ expect(fs.existsSync(`${stateFile}.1.json`)).toBe(false);
61
+ const raw = JSON.parse(await fs.promises.readFile(stateFile, "utf-8"));
62
+ // Primary analysis lives at root (backward-compatible single-repo shape).
63
+ expect(raw.existingTests[0].testFile).toBe("primary.spec.ts");
64
+ expect(raw.metadata.repositoryPath).toBe("/work/primary");
65
+ // Additional repo under relatedRepos, keyed by owner/repo.
66
+ expect(raw.relatedRepos["letsramp/api-insight"].data.existingTests[0].testFile).toBe("api.spec.ts");
67
+ expect(raw.relatedRepos["letsramp/api-insight"].repositoryPath).toBe("/work/api-insight");
68
+ });
69
+ it("reads back the correct section by repo (primary when omitted)", async () => {
70
+ const mgr = StateManager.fromStatePath(stateFile);
71
+ await mgr.writeRepoData(mkState("primary"), { repositoryPath: "/work/primary" });
72
+ await mgr.writeRepoData(mkState("api"), { repo: "letsramp/api-insight", repositoryPath: "/work/api-insight" });
73
+ expect((await mgr.readRepoData()).existingTests[0].testFile).toBe("primary.spec.ts");
74
+ expect((await mgr.readRepoData("letsramp/api-insight")).existingTests[0].testFile).toBe("api.spec.ts");
75
+ expect(await mgr.getRepoRepositoryPath()).toBe("/work/primary");
76
+ expect(await mgr.getRepoRepositoryPath("letsramp/api-insight")).toBe("/work/api-insight");
77
+ expect(await mgr.listRelatedRepos()).toEqual(["letsramp/api-insight"]);
78
+ });
79
+ it("preserves additional sections when the primary root is rewritten (writeData)", async () => {
80
+ const mgr = StateManager.fromStatePath(stateFile);
81
+ await mgr.writeRepoData(mkState("primary"), { repositoryPath: "/work/primary" });
82
+ await mgr.writeRepoData(mkState("api"), { repo: "letsramp/api-insight", repositoryPath: "/work/api-insight" });
83
+ // execute_test-style root rewrite must not drop the additional section.
84
+ const primary = (await mgr.readRepoData());
85
+ primary.existingTests[0].execution = { marker: "ran" };
86
+ await mgr.writeData(primary);
87
+ expect((await mgr.readRepoData("letsramp/api-insight")).existingTests[0].testFile).toBe("api.spec.ts");
88
+ expect((await mgr.readRepoData()).existingTests[0].execution.marker).toBe("ran");
89
+ });
90
+ it("preserves an additional repo's repositoryPath when a later write omits it", async () => {
91
+ const mgr = StateManager.fromStatePath(stateFile);
92
+ await mgr.writeRepoData(mkState("primary"), { repositoryPath: "/work/primary" });
93
+ // analyze_changes records the section WITH its checkout path.
94
+ await mgr.writeRepoData(mkState("api"), {
95
+ repo: "letsramp/api-insight",
96
+ repositoryPath: "/work/api-insight",
97
+ });
98
+ // A later write to the same section (e.g. execute_test) omits repositoryPath.
99
+ await mgr.writeRepoData(mkState("api-ran"), { repo: "letsramp/api-insight" });
100
+ // The path must survive so getRepoRepositoryPath can still re-absolutize.
101
+ expect(await mgr.getRepoRepositoryPath("letsramp/api-insight")).toBe("/work/api-insight");
102
+ expect((await mgr.readRepoData("letsramp/api-insight")).existingTests[0].testFile).toBe("api-ran.spec.ts");
103
+ });
104
+ it("routes the primary to root and a related repo to a section when BOTH pass an explicit repo", async () => {
105
+ const mgr = StateManager.fromStatePath(stateFile);
106
+ // The agent now always passes an explicit owner/repo, including for the
107
+ // primary. The FIRST write is the primary → root (tagged on metadata).
108
+ await mgr.writeRepoData(mkState("primary"), {
109
+ repo: "letsramp/primary-app",
110
+ repositoryPath: "/work/primary",
111
+ });
112
+ await mgr.writeRepoData(mkState("api"), {
113
+ repo: "letsramp/api-insight",
114
+ repositoryPath: "/work/api-insight",
115
+ });
116
+ const raw = JSON.parse(await fs.promises.readFile(stateFile, "utf-8"));
117
+ expect(raw.metadata.repository).toBe("letsramp/primary-app");
118
+ expect(raw.existingTests[0].testFile).toBe("primary.spec.ts");
119
+ expect(raw.relatedRepos["letsramp/api-insight"].data.existingTests[0].testFile).toBe("api.spec.ts");
120
+ // The primary is reachable BOTH by its explicit repo and by omitting it.
121
+ expect((await mgr.readRepoData("letsramp/primary-app")).existingTests[0].testFile).toBe("primary.spec.ts");
122
+ expect((await mgr.readRepoData()).existingTests[0].testFile).toBe("primary.spec.ts");
123
+ expect(await mgr.getRepoRepositoryPath("letsramp/primary-app")).toBe("/work/primary");
124
+ expect(await mgr.listRelatedRepos()).toEqual(["letsramp/api-insight"]);
125
+ });
126
+ it("reads a legacy single-section file (no relatedRepos) as the primary", async () => {
127
+ const mgr = StateManager.fromStatePath(stateFile);
128
+ // Legacy write path — no repo, no sections.
129
+ await mgr.writeData(mkState("legacy"), { repositoryPath: "/work/legacy" });
130
+ expect((await mgr.readRepoData()).existingTests[0].testFile).toBe("legacy.spec.ts");
131
+ expect(await mgr.getRepoRepositoryPath()).toBe("/work/legacy");
132
+ expect(await mgr.listRelatedRepos()).toEqual([]);
133
+ });
134
+ });
@@ -0,0 +1 @@
1
+ export declare function analyzeOpenAPIWithGivenEndpoint(apiSchemaInput: string, uriInput: string, pathParamsInput: string): Promise<string | null>;
@@ -0,0 +1,18 @@
1
+ export interface BranchDiffData {
2
+ currentBranch: string;
3
+ baseBranch: string;
4
+ changedFiles: string[];
5
+ /** Full unified diff content. */
6
+ diffContent: string;
7
+ diffStat: string;
8
+ /** Files newly created in this branch (--- /dev/null in diff header). Relative paths. */
9
+ newFiles: string[];
10
+ /** Files deleted in this branch (+++ /dev/null in diff header). Relative paths. */
11
+ deletedFiles: string[];
12
+ }
13
+ /**
14
+ * Extract every file path mentioned in a unified-diff `diff --git` header.
15
+ * Always uses the `b/` form so renames return the new path.
16
+ */
17
+ export declare function parseChangedFilesFromDiff(rawDiff: string): string[];
18
+ export declare function computeBranchDiff(repositoryPath: string, providedBaseBranch?: string): Promise<BranchDiffData>;
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Dart route extraction for Flutter apps using the `go_router` package.
3
+ *
4
+ * Walks the repo's Dart sources looking for GoRoute declarations with
5
+ * string-literal `path:` arguments:
6
+ *
7
+ * GoRoute(path: '/login', builder: (ctx, st) => LoginScreen())
8
+ * GoRoute(path: "/profile/:id", builder: ...)
9
+ *
10
+ * Why regex (not a Dart compiler): Dart parsers (analyzer, dart-analyzer
11
+ * package) require a Dart SDK install. Regex on the GoRoute string-literal-path
12
+ * subset gives ~95% coverage of real customer apps with zero install cost.
13
+ * Constants like `path: _kHome` are deliberately skipped — emitting them
14
+ * as a URL `_kHome` would mislead the agent worse than dropping them.
15
+ *
16
+ * Each route also carries metadata to support diff-matching in the caller
17
+ * (findCandidatePagesByDartRoute):
18
+ *
19
+ * - `screenWidgets`: identifiers referenced as builder/pageBuilder targets,
20
+ * e.g. `AuthorsScreen`. Resolved through the file's imports to absolute
21
+ * paths in `screenFiles`.
22
+ * - `screenFiles`: the imported source files those identifiers come from.
23
+ * - `isRedirectOnly`: true if the GoRoute has `redirect:` but no
24
+ * `builder:` or `pageBuilder:` — these don't render UI, just redirect.
25
+ *
26
+ * Scope choices for slice 1:
27
+ * - GoRouter only (`MaterialApp.routes` and `auto_route` deferred)
28
+ * - String-literal `path:` only (no constant resolution)
29
+ * - Nested GoRoutes emit each declaration verbatim — the absolute parent
30
+ * plus relative children. Caller may compose them later.
31
+ */
32
+ /** A route declaration discovered in Dart source. */
33
+ export interface DartRoute {
34
+ /** URL path string from the GoRoute(path: ...) declaration (verbatim). */
35
+ path: string;
36
+ /** Dart file the declaration was found in (absolute path). */
37
+ declaredIn: string;
38
+ /** Widget identifiers referenced from builder:/pageBuilder: (e.g. `AuthorsScreen`). */
39
+ screenWidgets: string[];
40
+ /** Resolved absolute paths for screenWidgets via the file's imports. */
41
+ screenFiles: string[];
42
+ /** True when GoRoute has `redirect:` but no `builder:`/`pageBuilder:` — no UI. */
43
+ isRedirectOnly: boolean;
44
+ }
45
+ export declare function extractDartRoutes(repositoryPath: string): DartRoute[];
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Pull a Docker image by name.
3
+ *
4
+ * When `dockerPlatform` is provided, pulls with that exact platform — no
5
+ * fallback logic runs. Use this for images whose platform is known ahead
6
+ * of time (e.g. executor is amd64-only).
7
+ *
8
+ * When `dockerPlatform` is omitted, uses the host architecture and falls
9
+ * back to linux/amd64 if the arm64 pull fails or the image doesn't land
10
+ * locally (Docker Desktop quirk on Apple Silicon).
11
+ */
12
+ export declare function pullDockerImage(imageName: string, dockerPlatform?: string): Promise<void>;
13
+ /**
14
+ * Check if a Docker image exists locally for the given reference (repo:tag or digest).
15
+ * Uses image inspect (daemon name resolution), not listImages tag scanning.
16
+ * Returns true if found, false if the image is not present (404).
17
+ * Throws for any other error (e.g. Docker daemon unreachable) so callers can skip pulling.
18
+ */
19
+ export declare function dockerImageExistsLocally(imageName: string): Promise<boolean>;
@@ -0,0 +1 @@
1
+ export {};