@skyramp/mcp 0.3.0 → 0.3.1-ldw-beta

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 (112) hide show
  1. package/build/commands/commandLibrary.d.ts +1 -0
  2. package/build/commands/commandLibrary.js +19 -13
  3. package/build/commands/commandLibrary.test.d.ts +1 -0
  4. package/build/commands/commandLibrary.test.js +59 -0
  5. package/build/commands/localDevTestChangesCommand.d.ts +15 -0
  6. package/build/commands/localDevTestChangesCommand.js +201 -0
  7. package/build/index.js +80 -6
  8. package/build/playwright/traceRecordingPrompt.js +2 -0
  9. package/build/prompts/enhance-assertions/sharedAssertionRules.d.ts +1 -0
  10. package/build/prompts/enhance-assertions/sharedAssertionRules.js +17 -0
  11. package/build/prompts/initialize-workspace/initializeWorkspacePrompt.js +11 -9
  12. package/build/prompts/local-dev/local-dev-plan.d.ts +35 -0
  13. package/build/prompts/local-dev/local-dev-plan.js +466 -0
  14. package/build/prompts/local-dev/local-dev-prompts.d.ts +4 -0
  15. package/build/prompts/local-dev/local-dev-prompts.js +190 -0
  16. package/build/prompts/prompt-utils.d.ts +8 -0
  17. package/build/prompts/prompt-utils.js +33 -0
  18. package/build/prompts/test-recommendation/analysisOutputPrompt.js +21 -29
  19. package/build/prompts/test-recommendation/analysisOutputPrompt.test.js +0 -28
  20. package/build/prompts/test-recommendation/test-recommendation-prompt.js +41 -4
  21. package/build/prompts/test-recommendation/test-recommendation-prompt.test.js +16 -1
  22. package/build/prompts/testbot/testbot-prompts.d.ts +0 -5
  23. package/build/prompts/testbot/testbot-prompts.js +5 -34
  24. package/build/resources/testbotResource.js +2 -1
  25. package/build/services/AnalyticsService.d.ts +1 -1
  26. package/build/services/TestExecutionService.d.ts +2 -1
  27. package/build/services/TestExecutionService.js +8 -3
  28. package/build/services/TestExecutionService.test.js +83 -3
  29. package/build/services/TestGenerationService.d.ts +2 -2
  30. package/build/services/TestGenerationService.js +7 -21
  31. package/build/services/containerEnv.js +3 -1
  32. package/build/tool-phases.js +6 -0
  33. package/build/tools/code-refactor/enhanceAssertionsTool.js +47 -18
  34. package/build/tools/enrichTestWithMocksTool.d.ts +28 -0
  35. package/build/tools/enrichTestWithMocksTool.js +726 -0
  36. package/build/tools/enrichTestWithMocksTool.test.d.ts +1 -0
  37. package/build/tools/enrichTestWithMocksTool.test.js +266 -0
  38. package/build/tools/executeSkyrampTestTool.d.ts +11 -0
  39. package/build/tools/executeSkyrampTestTool.js +63 -22
  40. package/build/tools/executeSkyrampTestTool.test.d.ts +1 -0
  41. package/build/tools/executeSkyrampTestTool.test.js +36 -0
  42. package/build/tools/generate-tests/batchMockGenerationTool.d.ts +85 -0
  43. package/build/tools/generate-tests/batchMockGenerationTool.js +432 -0
  44. package/build/tools/generate-tests/generateBatchScenarioRestTool.js +6 -21
  45. package/build/tools/generate-tests/generateBatchScenarioRestTool.test.js +1 -0
  46. package/build/tools/generate-tests/generateContractRestTool.js +2 -2
  47. package/build/tools/generate-tests/generateMockRestTool.d.ts +129 -6
  48. package/build/tools/generate-tests/generateMockRestTool.js +234 -22
  49. package/build/tools/generate-tests/loadTestSchema.d.ts +1 -1
  50. package/build/tools/generateEnrichedIntegrationTestTool.d.ts +25 -0
  51. package/build/tools/generateEnrichedIntegrationTestTool.js +235 -0
  52. package/build/tools/generateEnrichedIntegrationTestTool.test.d.ts +1 -0
  53. package/build/tools/generateEnrichedIntegrationTestTool.test.js +44 -0
  54. package/build/tools/localDevWorkerComposeTool.d.ts +24 -0
  55. package/build/tools/localDevWorkerComposeTool.js +224 -0
  56. package/build/tools/localDevWorkerComposeTool.test.d.ts +1 -0
  57. package/build/tools/localDevWorkerComposeTool.test.js +129 -0
  58. package/build/tools/localDevWorkflowFixes.test.d.ts +1 -0
  59. package/build/tools/localDevWorkflowFixes.test.js +255 -0
  60. package/build/tools/one-click/oneClickTool.d.ts +13 -0
  61. package/build/tools/one-click/oneClickTool.js +195 -24
  62. package/build/tools/one-click/oneClickTool.test.d.ts +1 -0
  63. package/build/tools/one-click/oneClickTool.test.js +172 -0
  64. package/build/tools/preflightMockCheckTool.d.ts +2 -0
  65. package/build/tools/preflightMockCheckTool.js +96 -0
  66. package/build/tools/queryProxyMocksTool.d.ts +70 -0
  67. package/build/tools/queryProxyMocksTool.js +522 -0
  68. package/build/tools/queryProxyMocksTool.test.d.ts +1 -0
  69. package/build/tools/queryProxyMocksTool.test.js +164 -0
  70. package/build/tools/test-management/analyzeChangesTool.d.ts +2 -1
  71. package/build/tools/test-management/analyzeChangesTool.js +61 -38
  72. package/build/tools/trace/startTraceCollectionTool.js +3 -3
  73. package/build/types/OneClickCommands.d.ts +1 -1
  74. package/build/types/RepositoryAnalysis.d.ts +117 -0
  75. package/build/types/RepositoryAnalysis.js +16 -2
  76. package/build/types/TestExecution.d.ts +1 -0
  77. package/build/types/TestTypes.d.ts +25 -7
  78. package/build/types/TestTypes.js +19 -5
  79. package/build/utils/analyze-openapi.js +18 -1
  80. package/build/utils/analyze-openapi.test.d.ts +1 -0
  81. package/build/utils/analyze-openapi.test.js +19 -0
  82. package/build/utils/branchDiff.d.ts +17 -1
  83. package/build/utils/branchDiff.js +99 -14
  84. package/build/utils/branchDiff.test.d.ts +1 -0
  85. package/build/utils/branchDiff.test.js +109 -0
  86. package/build/utils/featureFlags.d.ts +16 -0
  87. package/build/utils/featureFlags.js +20 -0
  88. package/build/utils/featureFlags.test.js +57 -2
  89. package/build/utils/grpcMockValidation.d.ts +1 -0
  90. package/build/utils/grpcMockValidation.js +49 -0
  91. package/build/utils/grpcMockValidation.test.d.ts +1 -0
  92. package/build/utils/grpcMockValidation.test.js +41 -0
  93. package/build/utils/httpMethodValidation.d.ts +4 -0
  94. package/build/utils/httpMethodValidation.js +15 -0
  95. package/build/utils/mockCompatibility.d.ts +49 -0
  96. package/build/utils/mockCompatibility.js +82 -0
  97. package/build/utils/mockCompatibility.test.d.ts +1 -0
  98. package/build/utils/mockCompatibility.test.js +79 -0
  99. package/build/utils/progress.js +10 -5
  100. package/build/utils/versions.d.ts +1 -0
  101. package/build/utils/versions.js +2 -0
  102. package/build/utils/workspaceAuth.d.ts +9 -0
  103. package/build/utils/workspaceAuth.js +51 -1
  104. package/build/workspace/workspace.d.ts +28 -14
  105. package/build/workspace/workspace.js +3 -1
  106. package/node_modules/playwright/lib/mcp/browser/tools/screenshot.js +1 -1
  107. package/node_modules/playwright/lib/mcp/skyramp/resultCode.js +45 -0
  108. package/node_modules/playwright/lib/mcp/skyramp/traceRecordingBackend.js +6 -22
  109. package/node_modules/playwright/lib/mcp/test/resultCode.test.js +48 -0
  110. package/node_modules/playwright/node_modules/playwright-core/package.json +1 -1
  111. package/node_modules/playwright/package.json +1 -1
  112. package/package.json +2 -2
@@ -16,11 +16,12 @@ export declare const analyzeChangesInputSchema: {
16
16
  scope: z.ZodOptional<z.ZodDefault<z.ZodEnum<["full_repo", "branch_diff"]>>>;
17
17
  baseBranch: z.ZodOptional<z.ZodString>;
18
18
  testDirectory: z.ZodOptional<z.ZodString>;
19
- topN: z.ZodOptional<z.ZodDefault<z.ZodNumber>>;
19
+ topN: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
20
20
  maxGenerate: z.ZodOptional<z.ZodNumber>;
21
21
  prNumber: z.ZodOptional<z.ZodNumber>;
22
22
  repository: z.ZodOptional<z.ZodString>;
23
23
  testsRepoDir: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
24
+ includeUncommitted: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
24
25
  };
25
26
  export declare const NO_UI_INSTRUCTIONS = "No UI changes detected \u2014 no blueprint capture needed.";
26
27
  export declare const NO_RESOLVABLE_URLS_INSTRUCTIONS = "Frontend changes detected but no candidate URLs could be resolved (workspace baseUrl missing or no router files matched). UI recommendations will be source-grounded only.";
@@ -283,8 +283,8 @@ export const analyzeChangesInputSchema = {
283
283
  .describe("Directory containing existing tests (auto-detected if omitted)"),
284
284
  topN: z
285
285
  .number()
286
- .default(MAX_RECOMMENDATIONS)
287
286
  .optional()
287
+ .default(MAX_RECOMMENDATIONS)
288
288
  .describe(`Number of ranked test recommendations to generate. Defaults to ${MAX_RECOMMENDATIONS}.`),
289
289
  maxGenerate: z
290
290
  .number()
@@ -305,6 +305,11 @@ export const analyzeChangesInputSchema = {
305
305
  .refine((v) => path.isAbsolute(v), { message: "testsRepoDir must be an absolute path" })
306
306
  .optional()
307
307
  .describe("Absolute path to a separate test repository clone. When set, existing test discovery scans this directory instead of repositoryPath. Used in cross-repo test delivery mode where tests live in a separate repo."),
308
+ includeUncommitted: z
309
+ .boolean()
310
+ .optional()
311
+ .default(false)
312
+ .describe("When true, diffs the base ref against the working tree (captures uncommitted and unstaged changes). Use for local-dev workflows. Defaults to false (CI mode — committed changes only)."),
308
313
  };
309
314
  // ── UI blueprint-capture instructions ──
310
315
  // Moved here from the former skyramp_ui_analyze_changes pre-flight tool. These
@@ -387,7 +392,7 @@ export function registerAnalyzeChangesTool(server) {
387
392
  if (analysisScope === AnalysisScope.CurrentBranchDiff) {
388
393
  await sendProgress(10, 100, "Computing branch diff...");
389
394
  try {
390
- diffData = await computeBranchDiff(params.repositoryPath, params.baseBranch);
395
+ diffData = await computeBranchDiff(params.repositoryPath, params.baseBranch, params.includeUncommitted ?? false);
391
396
  logger.info("Branch diff computed", {
392
397
  currentBranch: diffData.currentBranch,
393
398
  baseBranch: diffData.baseBranch,
@@ -1177,6 +1182,56 @@ export function registerAnalyzeChangesTool(server) {
1177
1182
  affectedServices: classifiedEndpoints.affectedServices,
1178
1183
  summary: "",
1179
1184
  } : undefined;
1185
+ // ── Route discovery context for LLM grounding and state persistence ──
1186
+ // fullAnalysis lives only in inMemorySessionStore (for MCP resources
1187
+ // and registerRecommendTestsPrompt). The disk state carries only the
1188
+ // slim fields that downstream tools (health, execute, actions) need.
1189
+ // routerMountContext and candidateRouteFiles are computed here so they
1190
+ // can be persisted to the state file for downstream tools (health, drift).
1191
+ // Without them, analyzeTestHealth would work only off the static catalog
1192
+ // which has wrong paths for nested resources and unsupported frameworks.
1193
+ const routeLikeUnmatchedFiles = [];
1194
+ for (const file of classifiedEndpoints?.unmatchedFiles ?? []) {
1195
+ const routeLike = SOURCE_EXTS.test(file) &&
1196
+ (ROUTE_FILE_PATTERN.test(file) || ROUTE_FILE_BASENAME_PATTERN.test(path.basename(file)));
1197
+ if (routeLike && !(await isGraphQLFile(file, params.repositoryPath))) {
1198
+ routeLikeUnmatchedFiles.push(file);
1199
+ }
1200
+ }
1201
+ const shouldIncludeCandidateRouteFiles = analysisScope !== AnalysisScope.CurrentBranchDiff ||
1202
+ rawRelatedEndpointCount === 0 ||
1203
+ scannedEndpoints.length === 0 ||
1204
+ routeLikeUnmatchedFiles.length > 0;
1205
+ let candidateRouteFiles;
1206
+ if (shouldIncludeCandidateRouteFiles) {
1207
+ candidateRouteFiles = [];
1208
+ for (const file of findCandidateRouteFiles(params.repositoryPath)) {
1209
+ if (!(await isGraphQLFile(file, params.repositoryPath))) {
1210
+ candidateRouteFiles.push(file);
1211
+ }
1212
+ }
1213
+ }
1214
+ // Write the full diff to a temp file before building state so the path
1215
+ // can be persisted and read by analyzeTestHealthTool for per-line detection.
1216
+ let diffFilePath;
1217
+ if (diffData?.diffContent) {
1218
+ diffFilePath = path.join(os.tmpdir(), `skyramp-diff-${sessionId}.diff`);
1219
+ await fs.promises.writeFile(diffFilePath, diffData.diffContent, { encoding: "utf-8", mode: 0o600 });
1220
+ }
1221
+ const routeDiscovery = {
1222
+ candidateFiles: [
1223
+ ...(diffData?.changedFiles ?? []),
1224
+ ...(candidateRouteFiles ?? []),
1225
+ ].filter((file, index, files) => files.indexOf(file) === index),
1226
+ staticHints: scannedEndpoints.map((ep) => ({
1227
+ path: ep.path,
1228
+ methods: ep.methods,
1229
+ sourceFile: ep.sourceFile,
1230
+ })),
1231
+ openApiPaths: specPaths ? [...specPaths] : [],
1232
+ routerMountContext,
1233
+ ...(diffFilePath ? { diffFilePath } : {}),
1234
+ };
1180
1235
  const fullAnalysis = {
1181
1236
  metadata: {
1182
1237
  repositoryName: path.basename(params.repositoryPath),
@@ -1233,6 +1288,7 @@ export function registerAnalyzeChangesTool(server) {
1233
1288
  hasCoverageReports: false,
1234
1289
  relevantExternalTestPaths,
1235
1290
  },
1291
+ routeDiscovery,
1236
1292
  ...(diffContext ? { branchDiffContext: diffContext } : {}),
1237
1293
  };
1238
1294
  // Store RecommendationState in memory so it's compatible with skyramp_recommend_tests if needed
@@ -1242,42 +1298,6 @@ export function registerAnalyzeChangesTool(server) {
1242
1298
  analysis: fullAnalysis,
1243
1299
  };
1244
1300
  storeSessionData(sessionId, recommendationState);
1245
- // ── Step 11: Build UnifiedAnalysisState and save ──
1246
- // fullAnalysis lives only in inMemorySessionStore (for MCP resources
1247
- // and registerRecommendTestsPrompt). The disk state carries only the
1248
- // slim fields that downstream tools (health, execute, actions) need.
1249
- // routerMountContext and candidateRouteFiles are computed here so they
1250
- // can be persisted to the state file for downstream tools (health, drift).
1251
- // Without them, analyzeTestHealth would work only off the static catalog
1252
- // which has wrong paths for nested resources and unsupported frameworks.
1253
- const routeLikeUnmatchedFiles = [];
1254
- for (const file of classifiedEndpoints?.unmatchedFiles ?? []) {
1255
- const routeLike = SOURCE_EXTS.test(file) &&
1256
- (ROUTE_FILE_PATTERN.test(file) || ROUTE_FILE_BASENAME_PATTERN.test(path.basename(file)));
1257
- if (routeLike && !(await isGraphQLFile(file, params.repositoryPath))) {
1258
- routeLikeUnmatchedFiles.push(file);
1259
- }
1260
- }
1261
- const shouldIncludeCandidateRouteFiles = analysisScope !== AnalysisScope.CurrentBranchDiff ||
1262
- rawRelatedEndpointCount === 0 ||
1263
- scannedEndpoints.length === 0 ||
1264
- routeLikeUnmatchedFiles.length > 0;
1265
- let candidateRouteFiles;
1266
- if (shouldIncludeCandidateRouteFiles) {
1267
- candidateRouteFiles = [];
1268
- for (const file of findCandidateRouteFiles(params.repositoryPath)) {
1269
- if (!(await isGraphQLFile(file, params.repositoryPath))) {
1270
- candidateRouteFiles.push(file);
1271
- }
1272
- }
1273
- }
1274
- // Write the full diff to a temp file before building state so the path
1275
- // can be persisted and read by analyzeTestHealthTool for per-line detection.
1276
- let diffFilePath;
1277
- if (diffData?.diffContent) {
1278
- diffFilePath = path.join(os.tmpdir(), `skyramp-diff-${sessionId}.diff`);
1279
- await fs.promises.writeFile(diffFilePath, diffData.diffContent, { encoding: "utf-8", mode: 0o600 });
1280
- }
1281
1301
  // Compute UI context from the diff's changed files using the shared
1282
1302
  // `isFrontendFile` classifier. Persisting this in the stateFile lets
1283
1303
  // skyramp_analyze_test_health and the recommendation prompt consume the
@@ -1376,6 +1396,7 @@ export function registerAnalyzeChangesTool(server) {
1376
1396
  sessionId,
1377
1397
  routerMountContext,
1378
1398
  candidateRouteFiles,
1399
+ routeDiscovery,
1379
1400
  relevantExternalTestPaths,
1380
1401
  },
1381
1402
  };
@@ -1473,6 +1494,8 @@ export function registerAnalyzeChangesTool(server) {
1473
1494
  modifiedEndpointCount: classifiedEndpoints?.changedEndpoints.length ?? 0,
1474
1495
  removedEndpointCount: classifiedEndpoints?.removedEndpoints.length ?? 0,
1475
1496
  endpointCount: skeletonEndpoints.reduce((acc, ep) => acc + ep.methods.length, 0),
1497
+ newEndpoints: (classifiedEndpoints?.newEndpoints ?? []).flatMap((ep) => ep.methods.map((m) => `${m} ${ep.path}`)),
1498
+ modifiedEndpoints: (classifiedEndpoints?.changedEndpoints ?? []).flatMap((ep) => ep.methods.map((m) => `${m} ${ep.path}`)),
1476
1499
  },
1477
1500
  // Surface uiContext inline so the testbot prompt can iterate
1478
1501
  // candidateUiPages and inspect changedFrontendFiles without
@@ -5,7 +5,7 @@ import { SkyrampClient } from "@skyramp/skyramp";
5
5
  import openProxyTerminalTracked from "../../utils/proxy-terminal.js";
6
6
  import { getEntryPoint } from "../../utils/telemetry.js";
7
7
  import { logger } from "../../utils/logger.js";
8
- import { basePlaywrightSchema, baseSchema, SESSION_STORAGE_FILENAME, } from "../../types/TestTypes.js";
8
+ import { basePlaywrightSchema, baseSchema, RuntimeEnvironment, SESSION_STORAGE_FILENAME, } from "../../types/TestTypes.js";
9
9
  import { AnalyticsService } from "../../services/AnalyticsService.js";
10
10
  import { resolveSessionPaths } from "./resolveSessionPaths.js";
11
11
  import { setSavedSessionPath } from "./sessionState.js";
@@ -67,8 +67,8 @@ For detailed documentation visit: https://www.skyramp.dev/docs/load-test/advance
67
67
  playwrightSaveStoragePath: basePlaywrightSchema.shape.playwrightSaveStoragePath,
68
68
  playwrightViewportSize: basePlaywrightSchema.shape.playwrightViewportSize,
69
69
  runtime: z
70
- .string()
71
- .default("docker")
70
+ .literal(RuntimeEnvironment.DOCKER)
71
+ .default(RuntimeEnvironment.DOCKER)
72
72
  .describe("Runtime environment for trace collection. Currently only 'docker' is supported and is used as the default."),
73
73
  include: z
74
74
  .array(z.string())
@@ -4,7 +4,7 @@
4
4
  * and executed by the coding assistant following the workflow instructions.
5
5
  */
6
6
  /** Unique identifier for a one-click command */
7
- export type OneClickCommandId = "test_given_endpoint_comprehensively" | "full_repo_scan_recommend_generate_and_execute_top_n_tests";
7
+ export type OneClickCommandId = "test_given_endpoint_comprehensively" | "full_repo_scan_recommend_generate_and_execute_top_n_tests" | "local_dev_test_changes";
8
8
  /** Metadata for intent recognition by coding assistants */
9
9
  export interface CommandIntentMetadata {
10
10
  /** Context indicators describing when to use and when NOT to use this workflow */
@@ -145,6 +145,22 @@ export interface BranchDiffContext {
145
145
  affectedServices: string[];
146
146
  summary?: string;
147
147
  }
148
+ export interface RouteDiscoveryInfo {
149
+ /** Files the LLM should inspect to infer endpoint methods and paths from source. */
150
+ candidateFiles: string[];
151
+ /** Static scanner output. Best-effort hints only, not an authoritative catalog. */
152
+ staticHints: Array<{
153
+ path: string;
154
+ methods: string[];
155
+ sourceFile: string;
156
+ }>;
157
+ /** OpenAPI paths from workspace config. Authoritative when specFetchSucceeded is true. */
158
+ openApiPaths: string[];
159
+ /** Router/module entry points that help the LLM reconstruct full mounted paths. */
160
+ routerMountContext: string[];
161
+ /** Full diff file path when available; read this to ground endpoint extraction in changed code. */
162
+ diffFilePath?: string;
163
+ }
148
164
  export interface RepositoryAnalysis {
149
165
  metadata: AnalysisMetadata;
150
166
  projectClassification: ProjectClassification;
@@ -155,6 +171,7 @@ export interface RepositoryAnalysis {
155
171
  authentication: AuthenticationInfo;
156
172
  infrastructure: InfrastructureInfo;
157
173
  existingTests: ExistingTestInfo;
174
+ routeDiscovery?: RouteDiscoveryInfo;
158
175
  branchDiffContext?: BranchDiffContext;
159
176
  }
160
177
  export interface AnalysisMetadata {
@@ -1423,6 +1440,45 @@ export declare const branchDiffContextSchema: z.ZodObject<{
1423
1440
  }[] | undefined;
1424
1441
  summary?: string | undefined;
1425
1442
  }>;
1443
+ export declare const routeDiscoveryInfoSchema: z.ZodObject<{
1444
+ candidateFiles: z.ZodArray<z.ZodString, "many">;
1445
+ staticHints: z.ZodArray<z.ZodObject<{
1446
+ path: z.ZodString;
1447
+ methods: z.ZodArray<z.ZodString, "many">;
1448
+ sourceFile: z.ZodString;
1449
+ }, "strip", z.ZodTypeAny, {
1450
+ path: string;
1451
+ sourceFile: string;
1452
+ methods: string[];
1453
+ }, {
1454
+ path: string;
1455
+ sourceFile: string;
1456
+ methods: string[];
1457
+ }>, "many">;
1458
+ openApiPaths: z.ZodArray<z.ZodString, "many">;
1459
+ routerMountContext: z.ZodArray<z.ZodString, "many">;
1460
+ diffFilePath: z.ZodOptional<z.ZodString>;
1461
+ }, "strip", z.ZodTypeAny, {
1462
+ candidateFiles: string[];
1463
+ staticHints: {
1464
+ path: string;
1465
+ sourceFile: string;
1466
+ methods: string[];
1467
+ }[];
1468
+ openApiPaths: string[];
1469
+ routerMountContext: string[];
1470
+ diffFilePath?: string | undefined;
1471
+ }, {
1472
+ candidateFiles: string[];
1473
+ staticHints: {
1474
+ path: string;
1475
+ sourceFile: string;
1476
+ methods: string[];
1477
+ }[];
1478
+ openApiPaths: string[];
1479
+ routerMountContext: string[];
1480
+ diffFilePath?: string | undefined;
1481
+ }>;
1426
1482
  export declare const analysisMetadataSchema: z.ZodObject<{
1427
1483
  repositoryName: z.ZodString;
1428
1484
  analysisDate: z.ZodString;
@@ -2440,6 +2496,45 @@ export declare const repositoryAnalysisSchema: z.ZodObject<{
2440
2496
  estimatedCoverage?: number | undefined;
2441
2497
  relevantExternalTestPaths?: string[] | undefined;
2442
2498
  }>;
2499
+ routeDiscovery: z.ZodOptional<z.ZodObject<{
2500
+ candidateFiles: z.ZodArray<z.ZodString, "many">;
2501
+ staticHints: z.ZodArray<z.ZodObject<{
2502
+ path: z.ZodString;
2503
+ methods: z.ZodArray<z.ZodString, "many">;
2504
+ sourceFile: z.ZodString;
2505
+ }, "strip", z.ZodTypeAny, {
2506
+ path: string;
2507
+ sourceFile: string;
2508
+ methods: string[];
2509
+ }, {
2510
+ path: string;
2511
+ sourceFile: string;
2512
+ methods: string[];
2513
+ }>, "many">;
2514
+ openApiPaths: z.ZodArray<z.ZodString, "many">;
2515
+ routerMountContext: z.ZodArray<z.ZodString, "many">;
2516
+ diffFilePath: z.ZodOptional<z.ZodString>;
2517
+ }, "strip", z.ZodTypeAny, {
2518
+ candidateFiles: string[];
2519
+ staticHints: {
2520
+ path: string;
2521
+ sourceFile: string;
2522
+ methods: string[];
2523
+ }[];
2524
+ openApiPaths: string[];
2525
+ routerMountContext: string[];
2526
+ diffFilePath?: string | undefined;
2527
+ }, {
2528
+ candidateFiles: string[];
2529
+ staticHints: {
2530
+ path: string;
2531
+ sourceFile: string;
2532
+ methods: string[];
2533
+ }[];
2534
+ openApiPaths: string[];
2535
+ routerMountContext: string[];
2536
+ diffFilePath?: string | undefined;
2537
+ }>>;
2443
2538
  branchDiffContext: z.ZodOptional<z.ZodObject<{
2444
2539
  currentBranch: z.ZodString;
2445
2540
  baseBranch: z.ZodString;
@@ -2768,6 +2863,17 @@ export declare const repositoryAnalysisSchema: z.ZodObject<{
2768
2863
  estimatedCoverage?: number | undefined;
2769
2864
  relevantExternalTestPaths?: string[] | undefined;
2770
2865
  };
2866
+ routeDiscovery?: {
2867
+ candidateFiles: string[];
2868
+ staticHints: {
2869
+ path: string;
2870
+ sourceFile: string;
2871
+ methods: string[];
2872
+ }[];
2873
+ openApiPaths: string[];
2874
+ routerMountContext: string[];
2875
+ diffFilePath?: string | undefined;
2876
+ } | undefined;
2771
2877
  branchDiffContext?: {
2772
2878
  currentBranch: string;
2773
2879
  baseBranch: string;
@@ -2970,6 +3076,17 @@ export declare const repositoryAnalysisSchema: z.ZodObject<{
2970
3076
  estimatedCoverage?: number | undefined;
2971
3077
  relevantExternalTestPaths?: string[] | undefined;
2972
3078
  };
3079
+ routeDiscovery?: {
3080
+ candidateFiles: string[];
3081
+ staticHints: {
3082
+ path: string;
3083
+ sourceFile: string;
3084
+ methods: string[];
3085
+ }[];
3086
+ openApiPaths: string[];
3087
+ routerMountContext: string[];
3088
+ diffFilePath?: string | undefined;
3089
+ } | undefined;
2973
3090
  branchDiffContext?: {
2974
3091
  currentBranch: string;
2975
3092
  baseBranch: string;
@@ -128,17 +128,30 @@ export const branchDiffContextSchema = z.object({
128
128
  changeType: z.enum(["added", "modified", "removed"]),
129
129
  })),
130
130
  })),
131
- removedEndpoints: z.array(z.object({
131
+ removedEndpoints: z
132
+ .array(z.object({
132
133
  path: z.string(),
133
134
  methods: z.array(z.object({
134
135
  method: z.string(),
135
136
  sourceFile: z.string(),
136
137
  changeType: z.literal("removed"),
137
138
  })),
138
- })).optional(),
139
+ }))
140
+ .optional(),
139
141
  affectedServices: z.array(z.string()),
140
142
  summary: z.string().optional(),
141
143
  });
144
+ export const routeDiscoveryInfoSchema = z.object({
145
+ candidateFiles: z.array(z.string()),
146
+ staticHints: z.array(z.object({
147
+ path: z.string(),
148
+ methods: z.array(z.string()),
149
+ sourceFile: z.string(),
150
+ })),
151
+ openApiPaths: z.array(z.string()),
152
+ routerMountContext: z.array(z.string()),
153
+ diffFilePath: z.string().optional(),
154
+ });
142
155
  export const analysisMetadataSchema = z.object({
143
156
  repositoryName: z.string(),
144
157
  analysisDate: z.string(),
@@ -239,5 +252,6 @@ export const repositoryAnalysisSchema = z.object({
239
252
  estimatedCoverage: z.number().optional(),
240
253
  relevantExternalTestPaths: z.array(z.string()).optional(),
241
254
  }),
255
+ routeDiscovery: routeDiscoveryInfoSchema.optional(),
242
256
  branchDiffContext: branchDiffContextSchema.optional(),
243
257
  });
@@ -42,6 +42,7 @@ export interface TestExecutionOptions {
42
42
  token?: string;
43
43
  timeout?: number;
44
44
  playwrightSaveStoragePath?: string;
45
+ dockerNetwork?: string;
45
46
  useHostNetwork?: boolean;
46
47
  }
47
48
  /**
@@ -15,10 +15,19 @@ export declare enum TestType {
15
15
  INTEGRATION = "integration",
16
16
  E2E = "e2e",
17
17
  UI = "ui",
18
- MOCK = "mock",
19
18
  UNIT = "unit",
20
19
  UNKNOWN = "unknown"
21
20
  }
21
+ /**
22
+ * Mock is not a test type — mock files are deployed via apply_mock(),
23
+ * not executed as tests. Kept as a standalone constant for mock generation.
24
+ */
25
+ export declare const MOCK_TYPE: "mock";
26
+ export declare enum MockProtocol {
27
+ Rest = "rest",
28
+ Grpc = "grpc",
29
+ Kafka = "kafka"
30
+ }
22
31
  export declare enum HttpMethod {
23
32
  GET = "GET",
24
33
  POST = "POST",
@@ -31,7 +40,16 @@ export declare enum HttpMethod {
31
40
  export declare enum RuntimeEnvironment {
32
41
  LOCAL = "local",
33
42
  DOCKER = "docker",
34
- KUBERNETES = "kubernetes"
43
+ KUBERNETES = "k8s"
44
+ }
45
+ export declare const runtimeEnvironmentSchema: z.ZodNativeEnum<typeof RuntimeEnvironment>;
46
+ export declare const runtimeEnvironmentInputSchema: z.ZodEffects<z.ZodNativeEnum<typeof RuntimeEnvironment>, RuntimeEnvironment, unknown>;
47
+ export declare function normalizeRuntimeEnvironment(value: RuntimeEnvironment | "kubernetes"): RuntimeEnvironment;
48
+ export interface TestConfig {
49
+ testType: TestType;
50
+ toolName: string;
51
+ description: string;
52
+ additionalFields?: Record<string, z.ZodSchema>;
35
53
  }
36
54
  export declare const languageSchema: z.ZodObject<{
37
55
  language: z.ZodNativeEnum<typeof ProgrammingLanguage>;
@@ -45,7 +63,7 @@ export declare const languageSchema: z.ZodObject<{
45
63
  }>;
46
64
  export declare const baseSchema: z.ZodObject<{
47
65
  deployDashboard: z.ZodDefault<z.ZodBoolean>;
48
- runtime: z.ZodDefault<z.ZodNativeEnum<typeof RuntimeEnvironment>>;
66
+ runtime: z.ZodDefault<z.ZodEffects<z.ZodNativeEnum<typeof RuntimeEnvironment>, RuntimeEnvironment, unknown>>;
49
67
  dockerNetwork: z.ZodDefault<z.ZodString>;
50
68
  dockerWorkerPort: z.ZodDefault<z.ZodNumber>;
51
69
  k8sNamespace: z.ZodDefault<z.ZodString>;
@@ -87,7 +105,7 @@ export declare const baseSchema: z.ZodObject<{
87
105
  outputDir: string;
88
106
  prompt: string;
89
107
  deployDashboard?: boolean | undefined;
90
- runtime?: RuntimeEnvironment | undefined;
108
+ runtime?: unknown;
91
109
  dockerNetwork?: string | undefined;
92
110
  dockerWorkerPort?: number | undefined;
93
111
  k8sNamespace?: string | undefined;
@@ -125,7 +143,7 @@ export declare const basePlaywrightSchema: z.ZodObject<{
125
143
  }>;
126
144
  export declare const baseTraceSchema: z.ZodObject<{
127
145
  deployDashboard: z.ZodDefault<z.ZodBoolean>;
128
- runtime: z.ZodDefault<z.ZodNativeEnum<typeof RuntimeEnvironment>>;
146
+ runtime: z.ZodDefault<z.ZodEffects<z.ZodNativeEnum<typeof RuntimeEnvironment>, RuntimeEnvironment, unknown>>;
129
147
  dockerNetwork: z.ZodDefault<z.ZodString>;
130
148
  dockerWorkerPort: z.ZodDefault<z.ZodNumber>;
131
149
  k8sNamespace: z.ZodDefault<z.ZodString>;
@@ -174,7 +192,7 @@ export declare const baseTraceSchema: z.ZodObject<{
174
192
  prompt: string;
175
193
  trace: string;
176
194
  deployDashboard?: boolean | undefined;
177
- runtime?: RuntimeEnvironment | undefined;
195
+ runtime?: unknown;
178
196
  dockerNetwork?: string | undefined;
179
197
  dockerWorkerPort?: number | undefined;
180
198
  k8sNamespace?: string | undefined;
@@ -192,7 +210,7 @@ export declare const baseTraceSchema: z.ZodObject<{
192
210
  }>;
193
211
  export declare const baseTestSchema: {
194
212
  deployDashboard: z.ZodDefault<z.ZodBoolean>;
195
- runtime: z.ZodDefault<z.ZodNativeEnum<typeof RuntimeEnvironment>>;
213
+ runtime: z.ZodDefault<z.ZodEffects<z.ZodNativeEnum<typeof RuntimeEnvironment>, RuntimeEnvironment, unknown>>;
196
214
  dockerNetwork: z.ZodDefault<z.ZodString>;
197
215
  dockerWorkerPort: z.ZodDefault<z.ZodNumber>;
198
216
  k8sNamespace: z.ZodDefault<z.ZodString>;
@@ -17,10 +17,20 @@ export var TestType;
17
17
  TestType["INTEGRATION"] = "integration";
18
18
  TestType["E2E"] = "e2e";
19
19
  TestType["UI"] = "ui";
20
- TestType["MOCK"] = "mock";
21
20
  TestType["UNIT"] = "unit";
22
21
  TestType["UNKNOWN"] = "unknown";
23
22
  })(TestType || (TestType = {}));
23
+ /**
24
+ * Mock is not a test type — mock files are deployed via apply_mock(),
25
+ * not executed as tests. Kept as a standalone constant for mock generation.
26
+ */
27
+ export const MOCK_TYPE = "mock";
28
+ export var MockProtocol;
29
+ (function (MockProtocol) {
30
+ MockProtocol["Rest"] = "rest";
31
+ MockProtocol["Grpc"] = "grpc";
32
+ MockProtocol["Kafka"] = "kafka";
33
+ })(MockProtocol || (MockProtocol = {}));
24
34
  export var HttpMethod;
25
35
  (function (HttpMethod) {
26
36
  HttpMethod["GET"] = "GET";
@@ -35,8 +45,13 @@ export var RuntimeEnvironment;
35
45
  (function (RuntimeEnvironment) {
36
46
  RuntimeEnvironment["LOCAL"] = "local";
37
47
  RuntimeEnvironment["DOCKER"] = "docker";
38
- RuntimeEnvironment["KUBERNETES"] = "kubernetes";
48
+ RuntimeEnvironment["KUBERNETES"] = "k8s";
39
49
  })(RuntimeEnvironment || (RuntimeEnvironment = {}));
50
+ export const runtimeEnvironmentSchema = z.nativeEnum(RuntimeEnvironment);
51
+ export const runtimeEnvironmentInputSchema = z.preprocess((value) => (value === "kubernetes" ? RuntimeEnvironment.KUBERNETES : value), runtimeEnvironmentSchema);
52
+ export function normalizeRuntimeEnvironment(value) {
53
+ return value === "kubernetes" ? RuntimeEnvironment.KUBERNETES : value;
54
+ }
40
55
  export const languageSchema = z.object({
41
56
  language: z
42
57
  .nativeEnum(ProgrammingLanguage)
@@ -51,10 +66,9 @@ export const baseSchema = z.object({
51
66
  .boolean()
52
67
  .default(false)
53
68
  .describe("Whether to deploy the Skyramp dashboard for test monitoring"),
54
- runtime: z
55
- .nativeEnum(RuntimeEnvironment)
69
+ runtime: runtimeEnvironmentInputSchema
56
70
  .default(RuntimeEnvironment.LOCAL)
57
- .describe("Runtime environment (docker, kubernetes, local)"),
71
+ .describe("Runtime environment (local, docker, or k8s; accepts kubernetes as an alias)"),
58
72
  dockerNetwork: z
59
73
  .string()
60
74
  .default("")
@@ -1,5 +1,10 @@
1
1
  import { SkyrampClient } from "@skyramp/skyramp";
2
2
  import { IsValidKeyValueList } from "./utils.js";
3
+ import { logger } from "./logger.js";
4
+ function isOpenApiRuntimeFailure(error) {
5
+ const message = error instanceof Error ? error.message : String(error);
6
+ return /(?:ECONNREFUSED|ECONNRESET|ENOTFOUND|EAI_AGAIN|ETIMEDOUT|socket hang up|connection refused|unavailable|timed? out)/i.test(message);
7
+ }
3
8
  // Analyze OpenAPI to check for required path params
4
9
  export async function analyzeOpenAPIWithGivenEndpoint(apiSchemaInput, uriInput, pathParamsInput) {
5
10
  const client = new SkyrampClient();
@@ -7,7 +12,19 @@ export async function analyzeOpenAPIWithGivenEndpoint(apiSchemaInput, uriInput,
7
12
  apiSchema: apiSchemaInput,
8
13
  uri: uriInput,
9
14
  };
10
- const pathParamRequired = await client.analyzeOpenapi(analyzeOpenapiOptions);
15
+ let pathParamRequired;
16
+ try {
17
+ pathParamRequired = await client.analyzeOpenapi(analyzeOpenapiOptions);
18
+ }
19
+ catch (e) {
20
+ if (isOpenApiRuntimeFailure(e)) {
21
+ logger.warning("Skipping OpenAPI path-param validation because schema analysis is unavailable", {
22
+ error: e instanceof Error ? e.message : String(e),
23
+ });
24
+ return null;
25
+ }
26
+ throw e;
27
+ }
11
28
  if (IsValidKeyValueList(pathParamRequired)) {
12
29
  const requiredPathParams = pathParamRequired
13
30
  ? pathParamRequired.split(",").map((p) => p.trim())
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,19 @@
1
+ import { jest, describe, it, expect, beforeEach } from "@jest/globals";
2
+ const analyzeOpenapi = jest.fn();
3
+ jest.unstable_mockModule("@skyramp/skyramp", () => ({
4
+ SkyrampClient: jest.fn(() => ({ analyzeOpenapi })),
5
+ }));
6
+ const { analyzeOpenAPIWithGivenEndpoint } = await import("./analyze-openapi.js");
7
+ describe("analyzeOpenAPIWithGivenEndpoint", () => {
8
+ beforeEach(() => {
9
+ analyzeOpenapi.mockReset();
10
+ });
11
+ it("skips path-param validation for runtime connectivity failures", async () => {
12
+ analyzeOpenapi.mockRejectedValue(new Error("gRPC unavailable: connection refused"));
13
+ await expect(analyzeOpenAPIWithGivenEndpoint("schema.yaml", "/users/{id}", "")).resolves.toBeNull();
14
+ });
15
+ it("rethrows schema errors instead of silently disabling validation", async () => {
16
+ analyzeOpenapi.mockRejectedValue(new Error("invalid apiSchema path: missing.yaml"));
17
+ await expect(analyzeOpenAPIWithGivenEndpoint("missing.yaml", "/users/{id}", "")).rejects.toThrow("invalid apiSchema path");
18
+ });
19
+ });
@@ -15,4 +15,20 @@ export interface BranchDiffData {
15
15
  * Always uses the `b/` form so renames return the new path.
16
16
  */
17
17
  export declare function parseChangedFilesFromDiff(rawDiff: string): string[];
18
- export declare function computeBranchDiff(repositoryPath: string, providedBaseBranch?: string): Promise<BranchDiffData>;
18
+ /**
19
+ * Normalize and validate a caller-provided base branch/ref before passing it
20
+ * to git. Reject option-like or ambiguous values so the ref is always treated
21
+ * as a revision name, not as a command-line flag.
22
+ */
23
+ export declare function normalizeProvidedBaseRef(providedBaseBranch?: string): string | undefined;
24
+ /**
25
+ * Compute the diff between a base reference and the current state.
26
+ *
27
+ * @param repositoryPath Absolute path to the git repository.
28
+ * @param providedBaseBranch A branch name, tag, or commit SHA to diff against.
29
+ * When omitted, the remote default branch is auto-detected.
30
+ * @param includeUncommitted When true, diffs against the working tree so
31
+ * uncommitted/unstaged changes are included. Defaults to false (CI mode
32
+ * that only considers committed changes via the three-dot form).
33
+ */
34
+ export declare function computeBranchDiff(repositoryPath: string, providedBaseBranch?: string, includeUncommitted?: boolean): Promise<BranchDiffData>;