@skyramp/mcp 0.3.8 → 0.4.0-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 (229) hide show
  1. package/build/commands/commandLibrary.d.ts +1 -1
  2. package/build/commands/commandLibrary.js +3 -3
  3. package/build/commands/recommendTestsAndExecuteCommand.d.ts +1 -1
  4. package/build/commands/recommendTestsAndExecuteCommand.js +35 -20
  5. package/build/commands/testThisEndpointCommand.js +35 -19
  6. package/build/index.js +9 -3
  7. package/build/playwright/blueprintDigest.d.ts +15 -0
  8. package/build/playwright/blueprintDigest.js +152 -0
  9. package/build/playwright/blueprintDigestStore.d.ts +31 -0
  10. package/build/playwright/blueprintDigestStore.js +117 -0
  11. package/build/playwright/registerPlaywrightTools.js +60 -12
  12. package/build/playwright/traceRecordingPrompt.js +8 -7
  13. package/build/prompts/enhance-assertions/sharedAssertionRules.js +9 -8
  14. package/build/prompts/enhance-assertions/uiAssertionsPrompt.js +24 -2
  15. package/build/prompts/promptAssets.d.ts +20 -0
  16. package/build/prompts/promptAssets.js +55 -0
  17. package/build/prompts/sut-setup/modes/dockerComposePrompt.js +19 -5
  18. package/build/prompts/test-maintenance/actionsInstructions.d.ts +4 -0
  19. package/build/prompts/test-maintenance/actionsInstructions.js +14 -2
  20. package/build/prompts/test-maintenance/drift-analysis-prompt.d.ts +0 -10
  21. package/build/prompts/test-maintenance/drift-analysis-prompt.js +2 -11
  22. package/build/prompts/test-maintenance/uiDriftAnalysisSections.js +8 -4
  23. package/build/prompts/test-recommendation/diffExecutionPlan.d.ts +5 -22
  24. package/build/prompts/test-recommendation/diffExecutionPlan.js +37 -465
  25. package/build/prompts/test-recommendation/recommendationSections.d.ts +7 -17
  26. package/build/prompts/test-recommendation/recommendationSections.js +67 -309
  27. package/build/prompts/test-recommendation/recommendationShared.d.ts +19 -47
  28. package/build/prompts/test-recommendation/recommendationShared.js +49 -155
  29. package/build/prompts/test-recommendation/registerRecommendTestsPrompt.d.ts +0 -5
  30. package/build/prompts/test-recommendation/registerRecommendTestsPrompt.js +10 -153
  31. package/build/prompts/test-recommendation/test-recommendation-prompt.d.ts +2 -29
  32. package/build/prompts/test-recommendation/test-recommendation-prompt.js +32 -457
  33. package/build/prompts/testbot/planDeclarations.d.ts +6 -0
  34. package/build/prompts/testbot/planDeclarations.js +9 -0
  35. package/build/prompts/testbot/testbot-prompts.d.ts +8 -0
  36. package/build/prompts/testbot/testbot-prompts.js +256 -381
  37. package/build/recommendation/answers.d.ts +35 -0
  38. package/build/recommendation/answers.js +96 -0
  39. package/build/recommendation/registerPlan.d.ts +49 -0
  40. package/build/recommendation/registerPlan.js +117 -0
  41. package/build/recommendation/runVerifiers.d.ts +10 -0
  42. package/build/recommendation/runVerifiers.js +49 -0
  43. package/build/recommendation/subjectStep.d.ts +42 -0
  44. package/build/recommendation/subjectStep.js +86 -0
  45. package/build/recommendation/types.d.ts +163 -0
  46. package/build/recommendation/types.js +20 -0
  47. package/build/recommendation/verifierContracts.d.ts +382 -0
  48. package/build/recommendation/verifierContracts.js +263 -0
  49. package/build/recommendation/verifiers/changedFile.d.ts +2 -0
  50. package/build/recommendation/verifiers/changedFile.js +82 -0
  51. package/build/recommendation/verifiers/citedPath.d.ts +12 -0
  52. package/build/recommendation/verifiers/citedPath.js +35 -0
  53. package/build/recommendation/verifiers/coverage.d.ts +7 -0
  54. package/build/recommendation/verifiers/coverage.js +617 -0
  55. package/build/recommendation/verifiers/deliveredMatchesPlan.d.ts +11 -0
  56. package/build/recommendation/verifiers/deliveredMatchesPlan.js +33 -0
  57. package/build/recommendation/verifiers/endpointGrounded.d.ts +17 -0
  58. package/build/recommendation/verifiers/endpointGrounded.js +128 -0
  59. package/build/recommendation/verifiers/existingCoverage.d.ts +6 -0
  60. package/build/recommendation/verifiers/existingCoverage.js +51 -0
  61. package/build/recommendation/verifiers/expectedOutcome.d.ts +31 -0
  62. package/build/recommendation/verifiers/expectedOutcome.js +105 -0
  63. package/build/recommendation/verifiers/removedElementGuarded.d.ts +2 -0
  64. package/build/recommendation/verifiers/removedElementGuarded.js +57 -0
  65. package/build/recommendation/verifiers/reportedCategory.d.ts +26 -0
  66. package/build/recommendation/verifiers/reportedCategory.js +84 -0
  67. package/build/recommendation/verifiers/screenRoute.d.ts +10 -0
  68. package/build/recommendation/verifiers/screenRoute.js +118 -0
  69. package/build/recommendation/verifiers/statedDifference.d.ts +6 -0
  70. package/build/recommendation/verifiers/statedDifference.js +140 -0
  71. package/build/recommendation/verifiers/uiElementGrounded.d.ts +7 -0
  72. package/build/recommendation/verifiers/uiElementGrounded.js +318 -0
  73. package/build/resources/analysisResources.js +1 -114
  74. package/build/resources/testbotResource.js +23 -13
  75. package/build/services/ModularizationService.js +2 -1
  76. package/build/services/TestDiscoveryService.d.ts +3 -72
  77. package/build/services/TestDiscoveryService.js +10 -303
  78. package/build/services/containerEnv.d.ts +1 -1
  79. package/build/services/containerEnv.js +12 -0
  80. package/build/skills/fixTestImportErrorsSkill.d.ts +13 -0
  81. package/build/skills/fixTestImportErrorsSkill.js +20 -0
  82. package/build/toolNames.d.ts +1 -0
  83. package/build/toolNames.js +1 -0
  84. package/build/tools/code-refactor/enhanceAssertionsTool.js +3 -3
  85. package/build/tools/code-refactor/modularizationTool.js +2 -1
  86. package/build/tools/executeSkyrampTestTool.d.ts +80 -0
  87. package/build/tools/executeSkyrampTestTool.js +246 -19
  88. package/build/tools/generate-tests/generateBatchScenarioRestTool.js +6 -0
  89. package/build/tools/generate-tests/generateContractRestTool.js +3 -3
  90. package/build/tools/generate-tests/planGuard.d.ts +2 -2
  91. package/build/tools/generate-tests/planGuard.js +78 -18
  92. package/build/tools/one-click/oneClickTool.d.ts +0 -1
  93. package/build/tools/one-click/oneClickTool.js +0 -5
  94. package/build/tools/submitReportTool.d.ts +48 -42
  95. package/build/tools/submitReportTool.js +576 -193
  96. package/build/tools/test-management/actionsTool.js +72 -4
  97. package/build/tools/test-management/analyzeChangesTool.d.ts +144 -48
  98. package/build/tools/test-management/analyzeChangesTool.js +212 -1219
  99. package/build/tools/test-management/analyzeTestHealthTool.js +13 -24
  100. package/build/tools/test-management/index.d.ts +1 -0
  101. package/build/tools/test-management/index.js +1 -0
  102. package/build/tools/test-management/registerTestPlanTool.d.ts +795 -172
  103. package/build/tools/test-management/registerTestPlanTool.js +609 -542
  104. package/build/tools/test-management/resolveScreenTool.d.ts +75 -0
  105. package/build/tools/test-management/resolveScreenTool.js +289 -0
  106. package/build/types/BlueprintDigest.d.ts +34 -0
  107. package/build/types/BlueprintDigest.js +1 -0
  108. package/build/types/RepositoryAnalysis.d.ts +20 -1559
  109. package/build/types/RepositoryAnalysis.js +2 -58
  110. package/build/types/StepMethod.d.ts +40 -0
  111. package/build/types/StepMethod.js +77 -0
  112. package/build/types/TestAnalysis.d.ts +12 -0
  113. package/build/types/TestExecution.d.ts +4 -0
  114. package/build/types/TestRecommendation.d.ts +24 -24
  115. package/build/types/TestRecommendation.js +91 -89
  116. package/build/types/TestbotPromptOptions.d.ts +0 -4
  117. package/build/types/TestbotReport.d.ts +64 -2
  118. package/build/utils/AnalysisStateManager.d.ts +79 -113
  119. package/build/utils/AnalysisStateManager.js +147 -57
  120. package/build/utils/assertion-verify/api-shared-lints.js +1 -1
  121. package/build/utils/assertion-verify/metrics.js +85 -36
  122. package/build/utils/assertion-verify/ui-lints.d.ts +0 -5
  123. package/build/utils/assertion-verify/ui-lints.js +32 -0
  124. package/build/utils/branchDiff.d.ts +63 -31
  125. package/build/utils/branchDiff.js +242 -94
  126. package/build/utils/containedPath.d.ts +18 -0
  127. package/build/utils/containedPath.js +73 -0
  128. package/build/utils/dartRouteExtractor.d.ts +18 -34
  129. package/build/utils/dartRouteExtractor.js +101 -173
  130. package/build/utils/featureFlags.d.ts +12 -0
  131. package/build/utils/featureFlags.js +14 -0
  132. package/build/utils/frontendSelectors.d.ts +48 -27
  133. package/build/utils/frontendSelectors.js +241 -80
  134. package/build/utils/pathMatching.d.ts +2 -4
  135. package/build/utils/pathMatching.js +2 -4
  136. package/build/utils/planMatchKeys.d.ts +38 -47
  137. package/build/utils/planMatchKeys.js +143 -81
  138. package/build/utils/rebaselineSnapshots.d.ts +24 -0
  139. package/build/utils/rebaselineSnapshots.js +65 -0
  140. package/build/utils/removedUiElements.d.ts +22 -0
  141. package/build/utils/removedUiElements.js +106 -0
  142. package/build/utils/reportVerification.d.ts +2 -6
  143. package/build/utils/reportVerification.js +61 -2
  144. package/build/utils/screenRoutes.d.ts +66 -0
  145. package/build/utils/screenRoutes.js +727 -0
  146. package/build/utils/sourceRouteExtractor.js +320 -112
  147. package/build/utils/testFileClassification.d.ts +11 -2
  148. package/build/utils/testFileClassification.js +44 -2
  149. package/build/utils/testFixtures.d.ts +5 -0
  150. package/build/utils/testFixtures.js +13 -0
  151. package/build/utils/utils.d.ts +0 -1
  152. package/build/utils/utils.js +0 -11
  153. package/build/utils/versions.d.ts +3 -3
  154. package/build/utils/versions.js +1 -1
  155. package/build/workspace/workspace.d.ts +12 -12
  156. package/node_modules/playwright/lib/mcp/skyramp/assertHiddenTool.js +56 -0
  157. package/node_modules/playwright/lib/mcp/skyramp/assertTool.js +2 -1
  158. package/node_modules/playwright/lib/mcp/skyramp/loadTraceTool.js +10 -0
  159. package/node_modules/playwright/lib/mcp/skyramp/skyRampImport.js +4 -1
  160. package/node_modules/playwright/lib/mcp/skyramp/traceRecordingBackend.js +160 -1
  161. package/node_modules/playwright/lib/mcp/test/skyRampExport.js +4 -2
  162. package/node_modules/playwright/node_modules/playwright-core/lib/server/codegen/skyramp/jsonlReader.js +1 -0
  163. package/node_modules/playwright/node_modules/playwright-core/lib/server/recorder/recorderSignalProcessor.js +2 -0
  164. package/node_modules/playwright/node_modules/playwright-core/lib/server/recorder.js +5 -1
  165. package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/{index.-Id052Lr.js → index.B7KbSQcC.js} +1 -1
  166. package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/index.html +1 -1
  167. package/node_modules/playwright/node_modules/playwright-core/package.json +1 -1
  168. package/node_modules/playwright/node_modules/playwright-core/src/server/codegen/skyramp/jsonlReader.ts +1 -1
  169. package/node_modules/playwright/node_modules/playwright-core/src/server/recorder/recorderSignalProcessor.ts +7 -0
  170. package/node_modules/playwright/node_modules/playwright-core/src/server/recorder.ts +6 -1
  171. package/node_modules/playwright/package.json +1 -1
  172. package/package.json +4 -3
  173. package/plugin/.claude-plugin/plugin.json +8 -0
  174. package/plugin/plugin.json +6 -0
  175. package/plugin/prompts/declaring-a-plan.md +20 -0
  176. package/plugin/prompts/generate-tests/context-fetching.md +4 -0
  177. package/plugin/prompts/generate-tests/execution-plan.md +63 -0
  178. package/plugin/prompts/generate-tests/generation.md +108 -0
  179. package/plugin/prompts/generate-tests/path-parameters.md +1 -0
  180. package/plugin/prompts/generate-tests/reasoning-protocol.md +17 -0
  181. package/plugin/prompts/generate-tests/tool-workflow-variants.md +61 -0
  182. package/plugin/prompts/generate-tests/tool-workflows.md +65 -0
  183. package/plugin/prompts/plan-tests.md +42 -0
  184. package/plugin/prompts/testbot-task1.md +82 -0
  185. package/plugin/skills/fix-test-import-errors/SKILL.md +98 -0
  186. package/build/prompts/test-recommendation/analysisOutputPrompt.d.ts +0 -84
  187. package/build/prompts/test-recommendation/analysisOutputPrompt.js +0 -369
  188. package/build/prompts/test-recommendation/fullRepoCatalog.d.ts +0 -7
  189. package/build/prompts/test-recommendation/fullRepoCatalog.js +0 -283
  190. package/build/prompts/test-recommendation/scopeAssessment.d.ts +0 -81
  191. package/build/prompts/test-recommendation/scopeAssessment.js +0 -359
  192. package/build/recommendation/budgeters/diversityBalancedBudgeter.d.ts +0 -7
  193. package/build/recommendation/budgeters/diversityBalancedBudgeter.js +0 -105
  194. package/build/recommendation/budgeters/fixedNBudgeter.d.ts +0 -7
  195. package/build/recommendation/budgeters/fixedNBudgeter.js +0 -11
  196. package/build/recommendation/budgeters/shared.d.ts +0 -32
  197. package/build/recommendation/budgeters/shared.js +0 -246
  198. package/build/recommendation/discriminators.d.ts +0 -37
  199. package/build/recommendation/discriminators.js +0 -379
  200. package/build/recommendation/diversity.d.ts +0 -47
  201. package/build/recommendation/diversity.js +0 -101
  202. package/build/recommendation/planRanker.d.ts +0 -65
  203. package/build/recommendation/planRanker.js +0 -83
  204. package/build/recommendation/testFixtures.d.ts +0 -25
  205. package/build/recommendation/testFixtures.js +0 -45
  206. package/build/types/FrontendIntegration.d.ts +0 -28
  207. package/build/types/FrontendIntegration.js +0 -22
  208. package/build/types/Recommendation.d.ts +0 -146
  209. package/build/types/Recommendation.js +0 -74
  210. package/build/utils/changedRoutes.d.ts +0 -29
  211. package/build/utils/changedRoutes.js +0 -87
  212. package/build/utils/frontendIntegration.d.ts +0 -9
  213. package/build/utils/frontendIntegration.js +0 -243
  214. package/build/utils/importerHop.d.ts +0 -135
  215. package/build/utils/importerHop.js +0 -489
  216. package/build/utils/pathAffinityClassification.d.ts +0 -49
  217. package/build/utils/pathAffinityClassification.js +0 -180
  218. package/build/utils/pythonMountPrefixes.d.ts +0 -25
  219. package/build/utils/pythonMountPrefixes.js +0 -347
  220. package/build/utils/repoScanner.d.ts +0 -34
  221. package/build/utils/repoScanner.js +0 -300
  222. package/build/utils/routeParsers.d.ts +0 -95
  223. package/build/utils/routeParsers.js +0 -951
  224. package/build/utils/scenarioDrafting.d.ts +0 -92
  225. package/build/utils/scenarioDrafting.js +0 -951
  226. package/build/utils/subjectEndpoints.d.ts +0 -19
  227. package/build/utils/subjectEndpoints.js +0 -98
  228. package/build/utils/uiPageEnumerator.d.ts +0 -172
  229. package/build/utils/uiPageEnumerator.js +0 -474
@@ -1,300 +0,0 @@
1
- import * as fs from "fs";
2
- import * as path from "path";
3
- import { nextjsFileToApiPath, parseFileEndpoints, } from "./routeParsers.js";
4
- import { resolvePythonMountPrefixes } from "./pythonMountPrefixes.js";
5
- import { walkSourceFiles } from "./fileWalk.js";
6
- import { logger } from "./logger.js";
7
- const GLOB_SKIP_DIRS = new Set(["node_modules", ".git", ".next", "dist", "build"]);
8
- function globRecursive(dir, extensions) {
9
- return walkSourceFiles(dir, { extensions, skipDirs: GLOB_SKIP_DIRS });
10
- }
11
- function safeReadFile(filePath) {
12
- try {
13
- return fs.readFileSync(filePath, "utf-8");
14
- }
15
- catch {
16
- return null;
17
- }
18
- }
19
- /**
20
- * Returns the relative paths of entry-point files that define the routing
21
- * hierarchy for this repository. The LLM reads these files in Step 1.5 to
22
- * trace router/module nesting and build an authoritative path resolution table.
23
- *
24
- * Intentionally returns only file paths — no extracted lines. Static regex
25
- * extraction is fragile and framework-specific; the LLM reads the files directly
26
- * and understands any framework's routing syntax.
27
- */
28
- export function grepRouterMountingContext(repositoryPath) {
29
- // NestJS root module (app.module.ts) is included alongside conventional
30
- // entry-point names because NestJS routing is implicit through module
31
- // composition rather than explicit mount calls.
32
- const entryCandidates = new Set([
33
- "main.py", "app.py", "server.py", "asgi.py", "wsgi.py",
34
- "app.ts", "app.js", "server.ts", "server.js", "index.ts", "index.js",
35
- "urls.py",
36
- "main.go", "server.go", "router.go", "routes.go",
37
- "routes.rb", "web.php", "api.php",
38
- "app.module.ts", "app.module.js",
39
- "router.ex", // Phoenix/Elixir — defines all scopes and pipelines
40
- "main.rs", "lib.rs", // Rust (Axum, Actix, Rocket)
41
- ]);
42
- // Content signals that indicate a file wires routers/modules together.
43
- const mountPatterns = [
44
- /include_router/i,
45
- /register_blueprint/i,
46
- /\.use\s*\(\s*["']\//,
47
- /urlpatterns/,
48
- /path\s*\(.+include\s*\(/,
49
- /\.Group\s*\(/,
50
- /Route::prefix/,
51
- /namespace\s+(?::[A-Za-z0-9_]+|["'][^"']+["'])/, // Rails: namespace :api or namespace "/api"
52
- /scope\s+["']\//,
53
- /@Module\s*\(/,
54
- /scope\s+["'].*,\s*do/, // Phoenix: scope "/api", MyApp.Router, do
55
- ];
56
- const found = [];
57
- const seenAbsPaths = new Set();
58
- // Single glob from repo root — globRecursive already recurses so scanning
59
- // each subdir separately would redundantly re-traverse those subtrees.
60
- const allFiles = globRecursive(repositoryPath, [".py", ".ts", ".js", ".go", ".rb", ".php", ".rs", ".ex"]);
61
- for (const f of allFiles) {
62
- if (/test/i.test(f))
63
- continue;
64
- if (seenAbsPaths.has(f))
65
- continue;
66
- const basename = path.basename(f);
67
- const isNamedEntry = entryCandidates.has(basename);
68
- const isNestModule = /\.module\.(ts|js)$/.test(f);
69
- if (!isNamedEntry && !isNestModule)
70
- continue;
71
- const fc = safeReadFile(f);
72
- if (fc === null)
73
- continue;
74
- if (isNestModule) {
75
- if (!/@Module\s*\(/.test(fc) || !/imports\s*:\s*\[/.test(fc))
76
- continue;
77
- }
78
- else {
79
- if (!mountPatterns.some((p) => p.test(fc)))
80
- continue;
81
- }
82
- seenAbsPaths.add(f);
83
- found.push(f.startsWith(repositoryPath) ? f.slice(repositoryPath.length + 1) : f);
84
- }
85
- return found;
86
- }
87
- function addEndpointToMap(endpointMap, apiPath, method, sourceFile, repositoryPath, line) {
88
- const relative = sourceFile.startsWith(repositoryPath)
89
- ? sourceFile.slice(repositoryPath.length + 1) : sourceFile;
90
- const normalizedPath = apiPath.startsWith("/") ? apiPath : `/${apiPath}`;
91
- const key = `${relative}::${normalizedPath}`;
92
- const existing = endpointMap.get(key);
93
- if (existing) {
94
- existing.methods.add(method);
95
- // Keep the earliest declaration line so ownership-range scoping starts there.
96
- if (typeof line === "number" && (existing.line === undefined || line < existing.line)) {
97
- existing.line = line;
98
- }
99
- }
100
- else {
101
- endpointMap.set(key, { path: normalizedPath, methods: new Set([method]), sourceFile: relative, line });
102
- }
103
- }
104
- function scanNextjsFile(file, repositoryPath, endpointMap) {
105
- const relative = file.startsWith(repositoryPath)
106
- ? file.slice(repositoryPath.length + 1) : file;
107
- const apiPath = nextjsFileToApiPath(relative);
108
- if (!apiPath)
109
- return false;
110
- const content = safeReadFile(file);
111
- if (content === null) {
112
- addEndpointToMap(endpointMap, apiPath, "MULTI", file, repositoryPath);
113
- return true;
114
- }
115
- const detectedMethods = new Set();
116
- for (const line of content.split("\n")) {
117
- const mc = line.match(/req\.method\s*===?\s*["'](GET|POST|PUT|PATCH|DELETE)["']/i);
118
- if (mc)
119
- detectedMethods.add(mc[1].toUpperCase());
120
- const em = line.match(/export\s+(?:async\s+)?function\s+(GET|POST|PUT|PATCH|DELETE)\s*\(/i);
121
- if (em)
122
- detectedMethods.add(em[1].toUpperCase());
123
- }
124
- if (detectedMethods.size > 0) {
125
- for (const m of detectedMethods)
126
- addEndpointToMap(endpointMap, apiPath, m, file, repositoryPath);
127
- }
128
- else {
129
- addEndpointToMap(endpointMap, apiPath, "MULTI", file, repositoryPath);
130
- }
131
- return true;
132
- }
133
- /** Filename pattern used to identify candidate route/handler files. */
134
- // Extended to cover NestJS (service, gateway, resolver) and other frameworks.
135
- // Tested against the full relative path — terms here are specific enough not to over-match.
136
- const ROUTE_FILE_PATTERN = /route|controller|endpoint|handler|view|urls|api|router|service|gateway|resolver|\bserver\b/i;
137
- // Generic terms like "app" and "main" must only match the basename (filename),
138
- // not directory names — otherwise every file under src/app/ would match by-name
139
- // and fill MAX_CANDIDATE_FILES, potentially skipping the content pass.
140
- const ROUTE_FILE_BASENAME_PATTERN = /\bapp\b|\bmain\b/i;
141
- /**
142
- * Content-based routing signature: a file is a route file if it contains BOTH
143
- * a URL-path-like string literal AND an HTTP method registration, regardless of
144
- * framework or language. This catches any framework whose files don't have
145
- * route/controller/handler-like names (e.g. Rust `main.rs`, .NET `Startup.cs`,
146
- * Axum, Ktor, Hono, Elysia, etc.).
147
- *
148
- * Two separate patterns are intentional — requiring both suppresses false
149
- * positives from files that merely reference HTTP methods or paths in other
150
- * contexts (test fixtures, config, documentation).
151
- */
152
- const ROUTING_URL_PATTERN = /["'`](\/[a-zA-Z0-9_{}:*-][a-zA-Z0-9_{}/:*-]*)/;
153
- const ROUTING_METHOD_PATTERN = /\.(get|post|put|patch|delete|head|options)\s*\(|@(Get|Post|Put|Patch|Delete|Route|Controller|Handler|RequestMapping)\b|route\s*\(["']|path\s*\(["']|HandleFunc|ServeHTTP|\.(Map|map)(Get|Post|Put|Patch|Delete)\s*\(|\brouting\s*\{/i;
154
- /** Maximum files to return — keeps the LLM prompt size bounded. */
155
- const MAX_CANDIDATE_FILES = 60;
156
- /**
157
- * Return relative paths of all source files that are likely to define API
158
- * routes. Two-pass strategy:
159
- * 1. Fast: filename matches ROUTE_FILE_PATTERN (no file I/O needed).
160
- * 2. Content: files not matched by name are read and checked for both a
161
- * URL-path literal AND an HTTP method registration. This covers any
162
- * framework the filename filter misses — Axum, Ktor, .NET, Hono, etc.
163
- * Result is capped at MAX_CANDIDATE_FILES to keep the LLM prompt bounded.
164
- */
165
- export function findCandidateRouteFiles(repositoryPath) {
166
- const sourceFiles = globRecursive(repositoryPath, [
167
- ".ts", ".tsx", ".js", ".jsx",
168
- ".py", ".java", ".kt",
169
- ".go", ".rb", ".php",
170
- ".rs", ".cs", ".ex", ".exs",
171
- ]);
172
- const byName = [];
173
- const unchecked = [];
174
- for (const f of sourceFiles) {
175
- if (/test/i.test(f))
176
- continue;
177
- const relative = f.startsWith(repositoryPath) ? f.slice(repositoryPath.length + 1) : f;
178
- if (ROUTE_FILE_PATTERN.test(relative) || ROUTE_FILE_BASENAME_PATTERN.test(path.basename(relative))) {
179
- byName.push(relative);
180
- }
181
- else {
182
- unchecked.push(f); // store absolute path for readFileSync
183
- }
184
- }
185
- // Content pass — only run if name pass left budget remaining
186
- const byContent = [];
187
- if (byName.length < MAX_CANDIDATE_FILES) {
188
- const budget = MAX_CANDIDATE_FILES - byName.length;
189
- let scanned = 0;
190
- const MAX_SCAN = 2000; // cap file reads; the unchecked array can be much larger
191
- for (const f of unchecked) {
192
- if (byContent.length >= budget || scanned++ >= MAX_SCAN)
193
- break;
194
- const content = safeReadFile(f);
195
- if (content === null)
196
- continue;
197
- if (ROUTING_URL_PATTERN.test(content) && ROUTING_METHOD_PATTERN.test(content)) {
198
- const relative = f.startsWith(repositoryPath) ? f.slice(repositoryPath.length + 1) : f;
199
- byContent.push(relative);
200
- }
201
- }
202
- }
203
- const combined = [...byName, ...byContent];
204
- const contentPassSkipped = byName.length >= MAX_CANDIDATE_FILES;
205
- const result = combined.slice(0, MAX_CANDIDATE_FILES);
206
- if (combined.length > MAX_CANDIDATE_FILES) {
207
- logger.warning("findCandidateRouteFiles: hit MAX_CANDIDATE_FILES cap — some route files may be excluded from LLM analysis", {
208
- cap: MAX_CANDIDATE_FILES,
209
- byName: byName.length,
210
- byContent: byContent.length,
211
- contentPassSkipped,
212
- totalFound: combined.length,
213
- });
214
- }
215
- return result;
216
- }
217
- /**
218
- * Cross-file FastAPI mount-prefix lookup, resolved lazily: `resolvePythonMountPrefixes`
219
- * walks the whole repo's Python, so it's only invoked the first time a `.py` file is
220
- * actually scanned — non-Python repos pay nothing. Returns undefined for non-`.py`
221
- * files (they never carry Python mount prefixes).
222
- */
223
- function makeMountPrefixLookup(repositoryPath) {
224
- let resolved = null;
225
- return (relative) => {
226
- if (!relative.endsWith(".py"))
227
- return undefined;
228
- if (resolved === null)
229
- resolved = resolvePythonMountPrefixes(repositoryPath);
230
- return resolved.get(relative.replace(/\\/g, "/"));
231
- };
232
- }
233
- export function scanAllRepoEndpoints(repositoryPath) {
234
- // Normalize up-front so relative-path derivation (and mount-prefix lookups) don't
235
- // depend on a caller-supplied trailing separator.
236
- repositoryPath = path.resolve(repositoryPath);
237
- const endpointMap = new Map();
238
- const mountPrefixFor = makeMountPrefixLookup(repositoryPath);
239
- const sourceFiles = globRecursive(repositoryPath, [".ts", ".tsx", ".js", ".jsx", ".py", ".java", ".kt", ".go", ".rb", ".php", ".rs", ".cs"]);
240
- for (const file of sourceFiles) {
241
- if (scanNextjsFile(file, repositoryPath, endpointMap))
242
- continue;
243
- const relative = file.startsWith(repositoryPath)
244
- ? file.slice(repositoryPath.length + 1) : file;
245
- if (!ROUTE_FILE_PATTERN.test(relative) && !ROUTE_FILE_BASENAME_PATTERN.test(path.basename(relative)))
246
- continue;
247
- const content = safeReadFile(file);
248
- if (content === null)
249
- continue;
250
- for (const ep of parseFileEndpoints(content, relative, mountPrefixFor(relative))) {
251
- addEndpointToMap(endpointMap, ep.path, ep.method, file, repositoryPath, ep.line);
252
- }
253
- }
254
- return Array.from(endpointMap.values()).map((data) => ({
255
- path: data.path,
256
- methods: Array.from(data.methods),
257
- sourceFile: data.sourceFile,
258
- ...(data.line !== undefined ? { line: data.line } : {}),
259
- }));
260
- }
261
- export function scanRelatedEndpoints(repositoryPath, changedFiles) {
262
- repositoryPath = path.resolve(repositoryPath);
263
- const relatedDirs = new Set();
264
- for (const f of changedFiles) {
265
- const absDir = path.dirname(path.join(repositoryPath, f));
266
- relatedDirs.add(absDir);
267
- const parentDir = path.dirname(absDir);
268
- if (parentDir !== repositoryPath)
269
- relatedDirs.add(parentDir);
270
- }
271
- const endpointMap = new Map();
272
- // Mount prefixes are resolved over the whole repo (aggregators may live outside
273
- // the related dirs) but lazily — only once a Python file is actually scanned.
274
- const mountPrefixFor = makeMountPrefixLookup(repositoryPath);
275
- for (const dir of relatedDirs) {
276
- if (!fs.existsSync(dir))
277
- continue;
278
- const files = globRecursive(dir, [".ts", ".tsx", ".js", ".jsx", ".py", ".java", ".kt", ".go", ".rb", ".php", ".rs", ".cs"]);
279
- for (const file of files) {
280
- if (scanNextjsFile(file, repositoryPath, endpointMap))
281
- continue;
282
- const relative = file.startsWith(repositoryPath)
283
- ? file.slice(repositoryPath.length + 1) : file;
284
- if (!ROUTE_FILE_PATTERN.test(relative) && !ROUTE_FILE_BASENAME_PATTERN.test(path.basename(relative)))
285
- continue;
286
- const fileContent = safeReadFile(file);
287
- if (fileContent === null)
288
- continue;
289
- for (const ep of parseFileEndpoints(fileContent, relative, mountPrefixFor(relative))) {
290
- addEndpointToMap(endpointMap, ep.path, ep.method, file, repositoryPath, ep.line);
291
- }
292
- }
293
- }
294
- return Array.from(endpointMap.values()).map((data) => ({
295
- path: data.path,
296
- methods: Array.from(data.methods),
297
- sourceFile: data.sourceFile,
298
- ...(data.line !== undefined ? { line: data.line } : {}),
299
- }));
300
- }
@@ -1,95 +0,0 @@
1
- import { BranchDiffData } from "./branchDiff.js";
2
- import type { ScannedEndpoint } from "./repoScanner.js";
3
- export interface ParsedDiffEndpoint {
4
- method: string;
5
- path: string;
6
- sourceFile: string;
7
- /** 1-based line where the route declaration starts, when parsed from full file content. */
8
- line?: number;
9
- }
10
- export declare function nextjsFileToApiPath(filePath: string): string | null;
11
- export declare const UI_FILE_EXTENSIONS: readonly ["tsx", "jsx", "vue", "svelte", "html", "xml"];
12
- export declare const UI_FILE_GIT_PATHSPEC: string;
13
- export declare const UI_FILE_EXTENSIONS_PIPED: string;
14
- export declare function parseRouteLine(line: string, sourceFile: string): ParsedDiffEndpoint | null;
15
- /**
16
- * Scan all route definitions in a single source file, combining prefix declarations
17
- * with method-level annotations. Two orthogonal state machines run in parallel:
18
- *
19
- * 1. Class-prefix (Spring, NestJS, ASP.NET, Kotlin):
20
- * @Controller/@RestController/@RequestMapping (class level) + class declaration →
21
- * activates classPrefix, applied to all @Get/@Post/@GetMapping etc. below.
22
- *
23
- * 2. Router-variable prefix (FastAPI, Flask Blueprint):
24
- * varName = APIRouter(prefix=...) or Blueprint(url_prefix=...) — handles both
25
- * single-line and multi-line declarations — maps the variable name to its prefix.
26
- * Applied when @varName.method(...) is seen.
27
- *
28
- * Falls back to pure parseRouteLine for all other frameworks (Express inline, Gin,
29
- * Rails, Laravel, Actix, Hapi, Go stdlib, etc.), which remain unaffected.
30
- *
31
- * `mountPrefixes` optionally supplies, per router variable, the **ancestor**
32
- * prefix that router inherits from cross-file `include_router(...)` mount points
33
- * (see `resolvePythonMountPrefixes`). It is composed *ahead* of the router's own
34
- * in-file prefix, so a `router = APIRouter(prefix="/recipes")` mounted under an
35
- * aggregator's `APIRouter(prefix="/api")` yields `/api/recipes`, not `/recipes`.
36
- */
37
- export declare function parseFileEndpoints(content: string, sourceFile: string, mountPrefixes?: Map<string, string>): ParsedDiffEndpoint[];
38
- export declare function isParamSegment(segment: string): boolean;
39
- export declare function isOpaqueIdSegment(segment: string): boolean;
40
- export declare const SKIP_PATH_SEGMENTS: Set<string>;
41
- /**
42
- * True for version/prefix path segments — "v1".."v99" and the literal "api".
43
- * `SKIP_PATH_SEGMENTS` enumerates only v1-v3, so resource-token derivation uses
44
- * this predicate as well; without it any endpoint versioned beyond v3 would
45
- * derive a nonsense token ("v4") as its resource (SKYR-3857).
46
- */
47
- export declare function isVersionLikeSegment(segment: string): boolean;
48
- /**
49
- * Extract the primary resource key from an endpoint path for use as a dedup key.
50
- *
51
- * For top-level resources (e.g. "/api/v1/orders/{id}") returns the resource name ("orders").
52
- * For sub-resources (e.g. "/orders/{id}/items") returns "orders_items" — including the
53
- * parent prevents "/orders/{id}/items" and "/products/{id}/items" from colliding on "items".
54
- */
55
- export declare function extractResourceFromPath(endpointPath: string): string;
56
- export declare function isRouteLikeFile(filePath: string): boolean;
57
- /**
58
- * Files that may contain endpoints removed in this branch: every changed file
59
- * except newly-created ones. Both whole-file deletions AND line-edits that drop a
60
- * route from a surviving file belong here — newly-created files cannot contain a
61
- * route that existed in the base branch.
62
- */
63
- export declare function selectRemovalCandidateFiles(diffData: BranchDiffData): string[];
64
- export declare function recoverRemovedEndpointsFromBase(candidateFiles: string[], fetchBaseContent: (file: string) => Promise<string>, repositoryPath?: string, baseBranch?: string): Promise<ScannedEndpoint[]>;
65
- /** Strip git's `a/` / `b/` diff-path prefix and normalize separators. */
66
- export declare function normalizeDiffPath(spec: string): string;
67
- /**
68
- * Parse a unified diff into the set of new-side (post-change) line numbers that
69
- * were touched, keyed by normalized file path.
70
- *
71
- * Context and added (`+`) lines advance the new-side cursor; added lines are
72
- * recorded. Removed (`-`) lines don't advance the cursor but record the current
73
- * new-side position, so deletion-only hunks still map to a location (otherwise a
74
- * pure deletion would scope to nothing and fall back to whole-file matching).
75
- * Feeds hunk-aware scoping in `classifyEndpointsByChangedFiles` so a
76
- * change can be attributed to the specific route whose declaration owns those
77
- * lines — instead of flagging every route in a touched file (the failure mode
78
- * on centralized-routing SUTs that register the whole API in one hub file).
79
- */
80
- export declare function parseChangedLinesByFile(diffContent: string): Map<string, Set<number>>;
81
- export interface ClassifiedEndpoints {
82
- /** Endpoints in scanned catalog whose sourceFile is in changedFiles (not new/deleted). */
83
- changedEndpoints: ScannedEndpoint[];
84
- /** Endpoints whose sourceFile is in a newly-created file. */
85
- newEndpoints: ScannedEndpoint[];
86
- /** Endpoints whose sourceFile is in a deleted file (recovered from base branch). */
87
- removedEndpoints: ScannedEndpoint[];
88
- /** Changed files that don't map to any scanned endpoint. */
89
- unmatchedFiles: string[];
90
- changedFiles: string[];
91
- currentBranch: string;
92
- baseBranch: string;
93
- affectedServices: string[];
94
- }
95
- export declare function classifyEndpointsByChangedFiles(diffData: BranchDiffData, scannedEndpoints: ScannedEndpoint[], deletedFileEndpoints?: ScannedEndpoint[]): ClassifiedEndpoints;