@skyramp/mcp 0.3.5 → 0.3.6-rc.2.ac20

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 (164) hide show
  1. package/build/adapters/jestAdapter.js +3 -0
  2. package/build/adapters/mochaAdapter.js +2 -0
  3. package/build/adapters/playwrightAdapter.js +3 -0
  4. package/build/adapters/pytestAdapter.js +12 -0
  5. package/build/playwright/registerPlaywrightTools.js +92 -30
  6. package/build/playwright/traceRecordingPrompt.d.ts +6 -0
  7. package/build/playwright/traceRecordingPrompt.js +6 -2
  8. package/build/prompts/code-reuse.d.ts +1 -2
  9. package/build/prompts/code-reuse.js +182 -77
  10. package/build/prompts/modularization/integration-test-modularization.d.ts +2 -0
  11. package/build/prompts/modularization/integration-test-modularization.js +83 -41
  12. package/build/prompts/modularization/render.d.ts +18 -0
  13. package/build/prompts/modularization/render.js +12 -0
  14. package/build/prompts/modularization/ui-test-modularization.d.ts +3 -1
  15. package/build/prompts/modularization/ui-test-modularization.js +89 -47
  16. package/build/prompts/pom-aware-code-reuse.js +3 -1
  17. package/build/prompts/shared-helper-policy.d.ts +57 -0
  18. package/build/prompts/shared-helper-policy.js +135 -0
  19. package/build/prompts/test-recommendation/diffExecutionPlan.js +62 -56
  20. package/build/prompts/test-recommendation/fullRepoCatalog.js +19 -8
  21. package/build/prompts/test-recommendation/recommendationShared.d.ts +28 -6
  22. package/build/prompts/test-recommendation/recommendationShared.js +90 -16
  23. package/build/prompts/test-recommendation/registerRecommendTestsPrompt.js +22 -0
  24. package/build/prompts/test-recommendation/test-recommendation-prompt.d.ts +2 -2
  25. package/build/prompts/test-recommendation/test-recommendation-prompt.js +3 -3
  26. package/build/prompts/testbot/testbot-prompts.js +81 -35
  27. package/build/recommendation/budgeters/shared.js +105 -27
  28. package/build/recommendation/discriminators.js +13 -2
  29. package/build/recommendation/planRanker.d.ts +6 -6
  30. package/build/recommendation/planRanker.js +6 -61
  31. package/build/services/AnalyticsService.d.ts +7 -0
  32. package/build/services/AnalyticsService.js +7 -1
  33. package/build/services/ModularizationService.js +1 -3
  34. package/build/services/TestDiscoveryService.d.ts +0 -2
  35. package/build/services/TestDiscoveryService.js +2 -37
  36. package/build/services/TestGenerationService.d.ts +16 -0
  37. package/build/services/TestGenerationService.js +86 -10
  38. package/build/services/containerEnv.js +13 -12
  39. package/build/tools/code-refactor/codeReuseTool.js +279 -93
  40. package/build/tools/code-refactor/enhance-state.d.ts +49 -0
  41. package/build/tools/code-refactor/enhance-state.js +109 -0
  42. package/build/tools/code-refactor/enhanceAssertionsTool.js +34 -1
  43. package/build/tools/code-refactor/modularizationTool.js +9 -2
  44. package/build/tools/code-refactor/reuse-outcome.d.ts +23 -1
  45. package/build/tools/code-refactor/reuse-outcome.js +14 -4
  46. package/build/tools/code-refactor/reuse-state.d.ts +127 -5
  47. package/build/tools/code-refactor/reuse-state.js +628 -16
  48. package/build/tools/code-refactor/utils-verify-gates.d.ts +26 -0
  49. package/build/tools/code-refactor/utils-verify-gates.js +100 -0
  50. package/build/tools/code-refactor/verify-gates.d.ts +2 -1
  51. package/build/tools/code-refactor/verify-gates.js +90 -25
  52. package/build/tools/executeSkyrampTestTool.d.ts +19 -0
  53. package/build/tools/executeSkyrampTestTool.js +158 -8
  54. package/build/tools/generate-tests/generateBatchScenarioRestTool.js +2 -2
  55. package/build/tools/generate-tests/generateE2ERestTool.js +16 -0
  56. package/build/tools/generate-tests/generateUIRestTool.d.ts +1 -0
  57. package/build/tools/generate-tests/generateUIRestTool.js +22 -0
  58. package/build/tools/generate-tests/scenarioLint.d.ts +2 -0
  59. package/build/tools/generate-tests/scenarioLint.js +127 -19
  60. package/build/tools/generate-tests/trace-reuse-guard.d.ts +20 -0
  61. package/build/tools/generate-tests/trace-reuse-guard.js +93 -0
  62. package/build/tools/runExistingTestsTool.d.ts +34 -2
  63. package/build/tools/runExistingTestsTool.js +104 -4
  64. package/build/tools/submitReportTool.d.ts +38 -38
  65. package/build/tools/submitReportTool.js +537 -120
  66. package/build/tools/test-management/analyzeChangesTool.d.ts +24 -1
  67. package/build/tools/test-management/analyzeChangesTool.js +71 -10
  68. package/build/tools/test-management/analyzeTestHealthTool.js +7 -7
  69. package/build/tools/test-management/registerTestPlanTool.d.ts +203 -0
  70. package/build/tools/test-management/registerTestPlanTool.js +70 -12
  71. package/build/types/ExternalTestExecution.d.ts +67 -1
  72. package/build/types/Recommendation.d.ts +34 -5
  73. package/build/types/RepositoryAnalysis.d.ts +133 -114
  74. package/build/types/RepositoryAnalysis.js +1 -1
  75. package/build/types/ReuseOutcome.d.ts +102 -6
  76. package/build/types/ReuseOutcome.js +16 -2
  77. package/build/types/TestRecommendation.js +21 -3
  78. package/build/types/TestTypes.js +14 -8
  79. package/build/types/TestbotReport.d.ts +10 -1
  80. package/build/types/index.d.ts +2 -2
  81. package/build/types/index.js +1 -1
  82. package/build/utils/AnalysisStateManager.d.ts +57 -1
  83. package/build/utils/AnalysisStateManager.js +54 -5
  84. package/build/utils/branchDiff.d.ts +10 -0
  85. package/build/utils/branchDiff.js +28 -0
  86. package/build/utils/changedRoutes.d.ts +29 -0
  87. package/build/utils/changedRoutes.js +87 -0
  88. package/build/utils/featureFlags.d.ts +21 -0
  89. package/build/utils/featureFlags.js +23 -0
  90. package/build/utils/frontendIntegration.js +34 -4
  91. package/build/utils/importerHop.d.ts +2 -8
  92. package/build/utils/importerHop.js +15 -53
  93. package/build/utils/pathMatching.d.ts +38 -0
  94. package/build/utils/pathMatching.js +71 -0
  95. package/build/utils/pathSignatures.d.ts +22 -0
  96. package/build/utils/pathSignatures.js +57 -0
  97. package/build/utils/planMatchKeys.d.ts +16 -3
  98. package/build/utils/planMatchKeys.js +26 -10
  99. package/build/utils/pluralization.d.ts +10 -0
  100. package/build/utils/pluralization.js +18 -0
  101. package/build/utils/pom-catalog-parse.d.ts +52 -0
  102. package/build/utils/pom-catalog-parse.js +141 -0
  103. package/build/utils/pom-scope/selector-extractor.d.ts +12 -0
  104. package/build/utils/pom-scope/selector-extractor.js +34 -8
  105. package/build/utils/pom-verify/verify.d.ts +6 -5
  106. package/build/utils/pom-verify/verify.js +8 -6
  107. package/build/utils/reportVerification.d.ts +64 -4
  108. package/build/utils/reportVerification.js +228 -3
  109. package/build/utils/reuseRouting.d.ts +3 -0
  110. package/build/utils/reuseRouting.js +50 -0
  111. package/build/utils/routeParsers.d.ts +2 -0
  112. package/build/utils/routeParsers.js +65 -8
  113. package/build/utils/scenarioDrafting.d.ts +1 -1
  114. package/build/utils/scenarioDrafting.js +57 -45
  115. package/build/utils/subjectEndpoints.d.ts +19 -0
  116. package/build/utils/subjectEndpoints.js +98 -0
  117. package/build/utils/testFileClassification.d.ts +11 -0
  118. package/build/utils/testFileClassification.js +47 -0
  119. package/build/utils/uiPageEnumerator.d.ts +45 -19
  120. package/build/utils/uiPageEnumerator.js +95 -51
  121. package/build/utils/utils-verify/allow.d.ts +16 -0
  122. package/build/utils/utils-verify/allow.js +68 -0
  123. package/build/utils/utils-verify/call-sites.d.ts +34 -0
  124. package/build/utils/utils-verify/call-sites.js +154 -0
  125. package/build/utils/utils-verify/index.d.ts +7 -0
  126. package/build/utils/utils-verify/index.js +7 -0
  127. package/build/utils/utils-verify/language-spec.d.ts +91 -0
  128. package/build/utils/utils-verify/language-spec.js +210 -0
  129. package/build/utils/utils-verify/locate.d.ts +39 -0
  130. package/build/utils/utils-verify/locate.js +199 -0
  131. package/build/utils/utils-verify/parse.d.ts +34 -0
  132. package/build/utils/utils-verify/parse.js +177 -0
  133. package/build/utils/utils-verify/stage.d.ts +24 -0
  134. package/build/utils/utils-verify/stage.js +107 -0
  135. package/build/utils/utils-verify/verify.d.ts +63 -0
  136. package/build/utils/utils-verify/verify.js +168 -0
  137. package/build/utils/utils.d.ts +3 -1
  138. package/build/utils/utils.js +3 -1
  139. package/build/workspace/workspace.d.ts +32 -32
  140. package/node_modules/playwright/lib/mcp/skyramp/assertTool.js +9 -5
  141. package/node_modules/playwright/lib/mcp/skyramp/loadTraceTool.js +16 -0
  142. package/node_modules/playwright/lib/mcp/skyramp/skyRampImport.js +2 -0
  143. package/node_modules/playwright/lib/mcp/skyramp/traceRecordingBackend.js +115 -14
  144. package/node_modules/playwright/lib/mcp/test/skyRampExport.js +13 -1
  145. package/node_modules/playwright/node_modules/playwright-core/.DS_Store +0 -0
  146. package/node_modules/playwright/node_modules/playwright-core/lib/server/codegen/skyramp/jsonlReader.js +2 -0
  147. package/node_modules/playwright/node_modules/playwright-core/lib/vite/htmlReport/index.html +27 -253
  148. package/node_modules/playwright/node_modules/playwright-core/lib/vite/recorder/assets/{codeMirrorModule-DtudTj_v.js → codeMirrorModule-DJMC4zNo.js} +1 -1
  149. package/node_modules/playwright/node_modules/playwright-core/lib/vite/recorder/assets/index-BW82eAUI.js +196 -0
  150. package/node_modules/playwright/node_modules/playwright-core/lib/vite/recorder/index.html +1 -1
  151. package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/assets/{codeMirrorModule-FNMuBzX1.js → codeMirrorModule-CZfp96qZ.js} +1 -1
  152. package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/assets/defaultSettingsView-gpLo02E0.js +809 -0
  153. package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/index.Bq1r1URj.js +2 -0
  154. package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/index.html +2 -2
  155. package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/uiMode.VEfqi1qN.js +5 -0
  156. package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/uiMode.html +2 -2
  157. package/node_modules/playwright/node_modules/playwright-core/package.json +1 -1
  158. package/node_modules/playwright/node_modules/playwright-core/src/server/codegen/skyramp/jsonlReader.ts +1 -1
  159. package/node_modules/playwright/package.json +1 -1
  160. package/package.json +2 -2
  161. package/node_modules/playwright/node_modules/playwright-core/lib/vite/recorder/assets/index-BpDwp16L.js +0 -422
  162. package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/assets/defaultSettingsView-Co9upU5h.js +0 -1035
  163. package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/index.DXNIQ_dx.js +0 -2
  164. package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/uiMode.CIKB3XSv.js +0 -5
@@ -1,3 +1,4 @@
1
+ import * as path from "path";
1
2
  /**
2
3
  * Shared jest + vitest adapter for skyramp_run_existing_tests. vitest's json
3
4
  * reporter is jest-compatible, so both use ONE parser; only the reporter flag
@@ -40,6 +41,7 @@ export function parseJestJson(report, opts) {
40
41
  results.push({
41
42
  testId: `${name} › (file failed to run)`,
42
43
  file: name,
44
+ ...(path.isAbsolute(name) ? { absoluteFile: name } : {}),
43
45
  status: "error",
44
46
  message: stripVTControlCharacters(raw).trim() || undefined,
45
47
  durationMs: 0,
@@ -55,6 +57,7 @@ export function parseJestJson(report, opts) {
55
57
  results.push({
56
58
  testId: `${name} › ${titlePath}`,
57
59
  file: name,
60
+ ...(path.isAbsolute(name) ? { absoluteFile: name } : {}),
58
61
  status,
59
62
  message,
60
63
  durationMs: Math.round(a.duration ?? 0),
@@ -1,3 +1,4 @@
1
+ import * as path from "path";
1
2
  /**
2
3
  * Mocha adapter for skyramp_run_existing_tests. Normalizes `mocha --reporter
3
4
  * json` (emitted to stdout) into the neutral shape. Mocha does not tag
@@ -29,6 +30,7 @@ function toResult(t, status) {
29
30
  return {
30
31
  testId: `${file} › ${full}`,
31
32
  file,
33
+ ...(path.isAbsolute(file) ? { absoluteFile: file } : {}),
32
34
  status,
33
35
  message: status === "fail" || status === "error" ? messageOf(t) : undefined,
34
36
  durationMs: Math.round(t.duration ?? 0),
@@ -12,6 +12,7 @@
12
12
  * builds the flags appended to the suite's `testRunCommand`. The IO shell that
13
13
  * actually spawns the run lives in `runExistingTestsTool`.
14
14
  */
15
+ import * as path from "path";
15
16
  import { stripVTControlCharacters } from "util";
16
17
  /**
17
18
  * A Playwright project (or spec file) is "infra" when it exists to bring up /
@@ -67,6 +68,7 @@ function messageOf(test) {
67
68
  }
68
69
  export function parsePlaywrightJson(report, opts) {
69
70
  const rep = (report ?? {});
71
+ const rootDir = rep.config?.rootDir;
70
72
  const collected = [];
71
73
  for (const fileSuite of rep.suites ?? []) {
72
74
  collectSpecs(fileSuite, fileSuite.file ?? "", [], collected);
@@ -118,6 +120,7 @@ export function parsePlaywrightJson(report, opts) {
118
120
  results.push({
119
121
  testId,
120
122
  file,
123
+ ...(rootDir ? { absoluteFile: path.resolve(rootDir, file) } : {}),
121
124
  status,
122
125
  message: status === "fail" || status === "error" ? messageOf(test) : undefined,
123
126
  durationMs,
@@ -1,3 +1,4 @@
1
+ import * as path from "path";
1
2
  /**
2
3
  * pytest adapter for skyramp_run_existing_tests. Normalizes the
3
4
  * `pytest-json-report` (`--json-report`) output into the neutral result shape.
@@ -39,6 +40,8 @@ function durationMsOf(test) {
39
40
  export function parsePytestJson(report, opts) {
40
41
  const rep = (report ?? {});
41
42
  const tests = rep.tests ?? [];
43
+ const root = rep.root;
44
+ const abs = (f) => (root ? { absoluteFile: path.resolve(root, f) } : {});
42
45
  // Real test outcomes.
43
46
  const testResults = tests.map((t) => {
44
47
  const status = mapOutcome(t.outcome);
@@ -46,6 +49,7 @@ export function parsePytestJson(report, opts) {
46
49
  return {
47
50
  testId: nodeid,
48
51
  file: fileOf(nodeid),
52
+ ...abs(fileOf(nodeid)),
49
53
  status,
50
54
  message: status === "fail" || status === "error" ? messageOf(t) : undefined,
51
55
  durationMs: durationMsOf(t),
@@ -61,6 +65,7 @@ export function parsePytestJson(report, opts) {
61
65
  return {
62
66
  testId: `${nodeid} › (collection error)`,
63
67
  file: fileOf(nodeid),
68
+ ...abs(fileOf(nodeid)),
64
69
  status: "error",
65
70
  message: lastLine(c.longrepr),
66
71
  durationMs: 0,
@@ -72,6 +77,13 @@ export function parsePytestJson(report, opts) {
72
77
  // When collection failed AND no real test ran, the suite as a whole could not be
73
78
  // collected — environmental (a wall of red), not PR signal. When some real tests
74
79
  // DID run, the collection errors above stand as ordinary PR-signal error results.
80
+ // NOTE: a collection error names the file that failed to import, but says nothing
81
+ // about WHY. A missing settings module, a database that is down or any shared
82
+ // dependency failure surfaces per-module, landing on exactly the files the run
83
+ // selected — indistinguishable here from a file the PR itself broke. So a run that
84
+ // collected nothing stays unhealthy even when scoped, and the file keeps its
85
+ // Unknown baseline. Treating it as PR signal would let an environment outage be
86
+ // reported as a test the change broke, and then repaired.
75
87
  if (collectionErrors.length > 0 && testResults.length === 0) {
76
88
  environmentHealthy = false;
77
89
  const first = collectionErrors[0];
@@ -4,36 +4,111 @@
4
4
  */
5
5
  import { z } from "zod";
6
6
  import { logger } from "../utils/logger.js";
7
- import { dualChannelResult } from "../utils/utils.js";
8
7
  import { PlaywrightTraceService, } from "./PlaywrightTraceService.js";
9
8
  let _service = null;
10
9
  export function getPlaywrightTraceService() {
11
10
  return _service;
12
11
  }
13
12
  /**
14
- * The one browser tool whose result is re-shaped on the way out — see
15
- * toStructuredBlueprintResult.
13
+ * The one browser tool whose text payload is shrunk on the way out — see
14
+ * shrinkBlueprintResult.
16
15
  */
17
16
  const BLUEPRINT_TOOL = "browser_blueprint";
18
17
  /**
19
- * Re-shape a browser_blueprint result onto both MCP result channels.
18
+ * Shrink a browser_blueprint result on the text channel it already arrives on.
20
19
  *
21
- * SKYR-4194. The browser tools are registered here as passthroughs, and the fork
22
- * returns the captured DOM as one `content[]` text block with no
23
- * `structuredContent` the shape whose spill the agent's file reader refuses.
24
- * See `dualChannelResult` for why both channels carry the payload.
20
+ * SKYR-4267. SKYR-4194 (#752) also copied the capture into `structuredContent`
21
+ * as a JSON string. Claude Code measures that copy against its size cap and
22
+ * drops `content[]` when both channels are present, so the escape added to
23
+ * every quote and newline counted against the cap. On the largest real capture
24
+ * (`sb20-bucket-visibility`) the text is 26,542 bytes and the escaped string
25
+ * 29,411 — which BOTH channels carried, because `dualChannelResult` puts the
26
+ * JSON serialization of `structuredContent` into `content[]`, so 58,822 went on
27
+ * the wire.
25
28
  *
26
- * An error result is returned untouched: the SDK's outputSchema check returns
27
- * early on `isError`, so it needs no structuredContent.
29
+ * The escaping also collapsed the whole payload onto ONE line, so a spill file
30
+ * read at a line offset returned nothing at all
31
+ * (`repo14-U06-field-reorder-toast`). Dropping it does not give the capture its
32
+ * newlines back: the fork pretty-prints the JSON, but shrinkBlueprintText below
33
+ * re-serializes with no indent, so the body is still one line. What returns are
34
+ * the `### ` section headers around it, so an offset read now lands on a header
35
+ * instead of erroring — the failure is mitigated, not closed.
36
+ *
37
+ * The text channel is the one every agent CLI reads, and it is what the other
38
+ * browser tools already return, so the capture rides it alone.
39
+ *
40
+ * An error result is returned untouched.
28
41
  */
29
- function toStructuredBlueprintResult(result) {
42
+ function shrinkBlueprintResult(result) {
30
43
  if (result?.isError)
31
44
  return result;
32
- const blueprint = (result?.content ?? [])
33
- .filter((block) => block?.type === "text")
34
- .map((block) => block.text)
35
- .join("\n");
36
- return dualChannelResult({ blueprint });
45
+ return {
46
+ ...result,
47
+ content: (result?.content ?? []).map((block) => block?.type === "text"
48
+ ? { ...block, text: shrinkBlueprintText(block.text) }
49
+ : block),
50
+ };
51
+ }
52
+ /**
53
+ * Drop every null-valued key, at any depth.
54
+ *
55
+ * SKYR-4208. Array entries are left alone — a null there holds a position, and
56
+ * removing it would renumber everything after it.
57
+ */
58
+ function dropNulls(value) {
59
+ if (Array.isArray(value))
60
+ return value.map(dropNulls);
61
+ if (value === null || typeof value !== "object")
62
+ return value;
63
+ return Object.fromEntries(Object.entries(value)
64
+ .filter(([, v]) => v !== null)
65
+ .map(([k, v]) => [k, dropNulls(v)]));
66
+ }
67
+ /**
68
+ * Minify the JSON body of a browser_blueprint payload and drop its null keys.
69
+ *
70
+ * SKYR-4208. The fork pretty-prints the capture behind a `### Result` header.
71
+ * On eval run 32458076607 (962 payloads, 11,368 elements) the indentation is
72
+ * 35.4% of the payload's tokens, and the null keys a further 6.8% — `stableId`,
73
+ * `testId` and `fingerprint` are null on 72-94% of elements and cost their key
74
+ * name on every one.
75
+ *
76
+ * Both steps are lossless: the agent parses the same fields with the same
77
+ * values, and an absent key already says what a null key says. Keys that merely
78
+ * hold their DEFAULT (`framePath: []`, `shadowRoot: false`) are deliberately
79
+ * kept — dropping those makes the agent infer the default from an absence, and
80
+ * `framePath` stops being empty as soon as the page has an iframe.
81
+ *
82
+ * The header and anything else outside the JSON survive unchanged, and a body
83
+ * that does not parse is returned as it arrived.
84
+ */
85
+ function shrinkBlueprintText(text) {
86
+ const start = text.indexOf("{");
87
+ if (start === -1)
88
+ return text;
89
+ // The fork can append more sections after `### Result` — `### Page` whenever
90
+ // the title or URL changed since the last call, and `### Modal state`,
91
+ // `### Snapshot` or `### Events` when the page produced them. Only the Result
92
+ // section holds JSON, so the body has to stop at the next header; reading to
93
+ // the end of the string makes the parse throw and costs the whole saving.
94
+ // A `### ` at the start of a line cannot sit inside the JSON, because JSON
95
+ // escapes the newline that would have to precede it.
96
+ const nextSection = text.slice(start).search(/\n### /);
97
+ const end = nextSection === -1 ? text.length : start + nextSection;
98
+ // The blank line the fork puts after a section belongs to the payload, not to
99
+ // the JSON, so it is put back byte-for-byte rather than parsed away.
100
+ const body = text.slice(start, end);
101
+ const json = body.trimEnd();
102
+ try {
103
+ return (text.slice(0, start) +
104
+ JSON.stringify(dropNulls(JSON.parse(json))) +
105
+ body.slice(json.length) +
106
+ text.slice(end));
107
+ }
108
+ catch {
109
+ logger.warning("browser_blueprint payload did not parse as JSON — delivering it unshrunk (SKYR-4208)");
110
+ return text;
111
+ }
37
112
  }
38
113
  export async function registerPlaywrightTools(server, options) {
39
114
  _service = new PlaywrightTraceService(options);
@@ -98,19 +173,6 @@ export async function registerPlaywrightTools(server, options) {
98
173
  server.registerTool(tool.name, {
99
174
  description: tool.description || `Playwright tool: ${tool.name}`,
100
175
  inputSchema: zodSchema,
101
- // SKYR-4194: only browser_blueprint returns its payload via
102
- // structuredContent, which requires a declared outputSchema. The other
103
- // browser tools return small results, and some return images, which this
104
- // text-only re-shaping would drop.
105
- ...(isBlueprint
106
- ? {
107
- outputSchema: {
108
- blueprint: z
109
- .string()
110
- .describe("The captured PageBlueprint — sections, elements, and their logical names. Read and follow this field."),
111
- },
112
- }
113
- : {}),
114
176
  }, async (params) => {
115
177
  if (!_service?.isInitialized()) {
116
178
  return {
@@ -124,7 +186,7 @@ export async function registerPlaywrightTools(server, options) {
124
186
  };
125
187
  }
126
188
  const result = await _service.callTool(tool.name, params);
127
- return isBlueprint ? toStructuredBlueprintResult(result) : result;
189
+ return isBlueprint ? shrinkBlueprintResult(result) : result;
128
190
  });
129
191
  }
130
192
  logger.info(`Registered ${filteredTools.length} Playwright tools: ${filteredTools.map((t) => t.name).join(", ")}`);
@@ -8,6 +8,12 @@ export interface TraceRecordingPromptOpts {
8
8
  outputDir?: string;
9
9
  /** Whether to run skyramp_modularization after generation. Default: true. Set false in CI. */
10
10
  modularize?: boolean;
11
+ /**
12
+ * With modularize=false: the generation result itself may still instruct a
13
+ * modularization pass (codeReuse flows that seed a shared utils file). When
14
+ * true, the note defers to that result instead of banning the tool outright.
15
+ */
16
+ modularizeViaGenerationResult?: boolean;
11
17
  }
12
18
  export declare function getTraceRecordingPromptText(opts?: TraceRecordingPromptOpts): string;
13
19
  export declare function registerTraceRecordingPrompt(server: McpServer): void;
@@ -13,10 +13,14 @@ export function getTraceRecordingPromptText(opts) {
13
13
  : `Call \`skyramp_export_zip\` with \`outputPath\` set to the absolute zip path (same directory and base name as the test file, replacing \`.spec.ts\` with \`.zip\`).`;
14
14
  const generateInstruction = modularize
15
15
  ? `Call \`skyramp_ui_test_generation\` with \`playwrightInput\` set to the absolute zip path from the Export step.`
16
- : `Call \`skyramp_ui_test_generation\` with \`playwrightInput\` set to the absolute zip path from step 5 and \`modularizeCode: false\`.`;
16
+ : opts?.modularizeViaGenerationResult
17
+ ? `Call \`skyramp_ui_test_generation\` with \`playwrightInput\` set to the absolute zip path from step 5, \`modularizeCode: false\`, and \`codeReuse: true\` — its result lists the post-generation steps (assertion enhancement, modularization, reuse) with their arguments.`
18
+ : `Call \`skyramp_ui_test_generation\` with \`playwrightInput\` set to the absolute zip path from step 5 and \`modularizeCode: false\`.`;
17
19
  const modularizeNote = modularize
18
20
  ? `- **After generating the test**, run \`skyramp_modularization\` for code quality.`
19
- : `- Do NOT run \`skyramp_modularization\` — skip modularization in CI.`;
21
+ : opts?.modularizeViaGenerationResult
22
+ ? `- Run \`skyramp_modularization\` ONLY when the generation result's CRITICAL NEXT STEPS instruct it (with the arguments they give) — never on your own initiative.`
23
+ : `- Do NOT run \`skyramp_modularization\` — skip modularization in CI.`;
20
24
  return `## Skyramp UI Test Recording
21
25
 
22
26
  ${getPersonaPrefix()} For UI recording, every action must be grounded in what \`browser_snapshot\` returns. If an element is not visible in the snapshot, do not interact with it.
@@ -1,3 +1,2 @@
1
1
  import type { ScopedPomInput } from "../utils/pom-scope/index.js";
2
- export declare function isPomAwareTarget(language: string, framework?: string): boolean;
3
- export declare function getCodeReusePrompt(testFile: string, language: string, framework?: string, scopedPoms?: ScopedPomInput): string;
2
+ export declare function getCodeReusePrompt(testFile: string, language: string, framework?: string, scopedPoms?: ScopedPomInput, testType?: string): string;