@yasserkhanorg/e2e-agents 1.8.5 → 1.9.5

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 (256) hide show
  1. package/README.md +95 -8
  2. package/dist/adapters/cypress.d.ts +10 -0
  3. package/dist/adapters/cypress.d.ts.map +1 -0
  4. package/dist/adapters/cypress.js +86 -0
  5. package/dist/adapters/framework_adapter.d.ts +41 -0
  6. package/dist/adapters/framework_adapter.d.ts.map +1 -0
  7. package/dist/adapters/framework_adapter.js +152 -0
  8. package/dist/adapters/playwright.d.ts +10 -0
  9. package/dist/adapters/playwright.d.ts.map +1 -0
  10. package/dist/adapters/playwright.js +86 -0
  11. package/dist/adapters/pytest.d.ts +10 -0
  12. package/dist/adapters/pytest.d.ts.map +1 -0
  13. package/dist/adapters/pytest.js +96 -0
  14. package/dist/adapters/supertest.d.ts +12 -0
  15. package/dist/adapters/supertest.d.ts.map +1 -0
  16. package/dist/adapters/supertest.js +85 -0
  17. package/dist/agent/config.d.ts +1 -1
  18. package/dist/agent/config.d.ts.map +1 -1
  19. package/dist/agent/git.d.ts +1 -0
  20. package/dist/agent/git.d.ts.map +1 -1
  21. package/dist/agent/git.js +3 -0
  22. package/dist/agentic/fix_loop.d.ts.map +1 -1
  23. package/dist/agentic/fix_loop.js +5 -4
  24. package/dist/agentic/runner.d.ts +2 -0
  25. package/dist/agentic/runner.d.ts.map +1 -1
  26. package/dist/agentic/runner.js +15 -12
  27. package/dist/agents/cross-impact.d.ts.map +1 -1
  28. package/dist/agents/cross-impact.js +6 -1
  29. package/dist/agents/executor.d.ts.map +1 -1
  30. package/dist/agents/executor.js +6 -1
  31. package/dist/agents/strategist.d.ts.map +1 -1
  32. package/dist/agents/strategist.js +6 -1
  33. package/dist/agents/test-designer.d.ts.map +1 -1
  34. package/dist/agents/test-designer.js +6 -1
  35. package/dist/anthropic_provider.d.ts.map +1 -1
  36. package/dist/anthropic_provider.js +1 -0
  37. package/dist/base_provider.d.ts +56 -0
  38. package/dist/base_provider.d.ts.map +1 -1
  39. package/dist/base_provider.js +123 -1
  40. package/dist/budget_ledger.d.ts +28 -0
  41. package/dist/budget_ledger.d.ts.map +1 -0
  42. package/dist/budget_ledger.js +62 -0
  43. package/dist/cache/cached_provider.d.ts +45 -0
  44. package/dist/cache/cached_provider.d.ts.map +1 -0
  45. package/dist/cache/cached_provider.js +88 -0
  46. package/dist/cache/response_cache.d.ts +79 -0
  47. package/dist/cache/response_cache.d.ts.map +1 -0
  48. package/dist/cache/response_cache.js +177 -0
  49. package/dist/cli/commands/bootstrap.d.ts +3 -0
  50. package/dist/cli/commands/bootstrap.d.ts.map +1 -0
  51. package/dist/cli/commands/bootstrap.js +109 -0
  52. package/dist/cli/commands/cost_report.d.ts +3 -0
  53. package/dist/cli/commands/cost_report.d.ts.map +1 -0
  54. package/dist/cli/commands/cost_report.js +115 -0
  55. package/dist/cli/commands/crew.d.ts.map +1 -1
  56. package/dist/cli/commands/crew.js +118 -1
  57. package/dist/cli/commands/gate.d.ts +3 -0
  58. package/dist/cli/commands/gate.d.ts.map +1 -0
  59. package/dist/cli/commands/gate.js +86 -0
  60. package/dist/cli/commands/init.d.ts.map +1 -1
  61. package/dist/cli/commands/init.js +7 -62
  62. package/dist/cli/commands/train.d.ts.map +1 -1
  63. package/dist/cli/commands/train.js +16 -21
  64. package/dist/cli/defaults.d.ts +35 -0
  65. package/dist/cli/defaults.d.ts.map +1 -0
  66. package/dist/cli/defaults.js +125 -0
  67. package/dist/cli/errors.d.ts +27 -0
  68. package/dist/cli/errors.d.ts.map +1 -0
  69. package/dist/cli/errors.js +57 -0
  70. package/dist/cli/parse_args.d.ts.map +1 -1
  71. package/dist/cli/parse_args.js +24 -2
  72. package/dist/cli/types.d.ts +7 -1
  73. package/dist/cli/types.d.ts.map +1 -1
  74. package/dist/cli.js +47 -2
  75. package/dist/crew/context.d.ts +15 -0
  76. package/dist/crew/context.d.ts.map +1 -1
  77. package/dist/crew/orchestrator.d.ts +14 -0
  78. package/dist/crew/orchestrator.d.ts.map +1 -1
  79. package/dist/crew/orchestrator.js +162 -4
  80. package/dist/crew/protocol.d.ts +13 -0
  81. package/dist/crew/protocol.d.ts.map +1 -1
  82. package/dist/crew/provider.d.ts +15 -1
  83. package/dist/crew/provider.d.ts.map +1 -1
  84. package/dist/crew/provider.js +24 -4
  85. package/dist/custom_provider.d.ts.map +1 -1
  86. package/dist/custom_provider.js +1 -0
  87. package/dist/engine/diff_loader.d.ts.map +1 -1
  88. package/dist/engine/diff_loader.js +3 -14
  89. package/dist/engine/impact_engine.d.ts.map +1 -1
  90. package/dist/engine/impact_engine.js +9 -23
  91. package/dist/esm/adapters/cypress.js +49 -0
  92. package/dist/esm/adapters/framework_adapter.js +114 -0
  93. package/dist/esm/adapters/playwright.js +49 -0
  94. package/dist/esm/adapters/pytest.js +59 -0
  95. package/dist/esm/adapters/supertest.js +48 -0
  96. package/dist/esm/agent/git.js +3 -1
  97. package/dist/esm/agentic/fix_loop.js +5 -4
  98. package/dist/esm/agentic/runner.js +15 -12
  99. package/dist/esm/agents/cross-impact.js +6 -1
  100. package/dist/esm/agents/executor.js +6 -1
  101. package/dist/esm/agents/strategist.js +6 -1
  102. package/dist/esm/agents/test-designer.js +6 -1
  103. package/dist/esm/anthropic_provider.js +1 -0
  104. package/dist/esm/base_provider.js +121 -0
  105. package/dist/esm/budget_ledger.js +58 -0
  106. package/dist/esm/cache/cached_provider.js +82 -0
  107. package/dist/esm/cache/response_cache.js +140 -0
  108. package/dist/esm/cli/commands/bootstrap.js +106 -0
  109. package/dist/esm/cli/commands/cost_report.js +112 -0
  110. package/dist/esm/cli/commands/crew.js +118 -1
  111. package/dist/esm/cli/commands/gate.js +83 -0
  112. package/dist/esm/cli/commands/init.js +3 -58
  113. package/dist/esm/cli/commands/train.js +16 -21
  114. package/dist/esm/cli/defaults.js +118 -0
  115. package/dist/esm/cli/errors.js +52 -0
  116. package/dist/esm/cli/parse_args.js +24 -2
  117. package/dist/esm/cli.js +47 -2
  118. package/dist/esm/crew/orchestrator.js +162 -4
  119. package/dist/esm/crew/provider.js +24 -4
  120. package/dist/esm/custom_provider.js +1 -0
  121. package/dist/esm/engine/diff_loader.js +1 -12
  122. package/dist/esm/engine/impact_engine.js +9 -23
  123. package/dist/esm/index.js +21 -0
  124. package/dist/esm/knowledge/cluster_utils.js +60 -0
  125. package/dist/esm/knowledge/kg_bridge.js +381 -0
  126. package/dist/esm/knowledge/kg_types.js +3 -0
  127. package/dist/esm/knowledge/route_families.js +89 -0
  128. package/dist/esm/mcp-server.js +2 -4
  129. package/dist/esm/metrics/prometheus.js +149 -0
  130. package/dist/esm/model_router.js +59 -0
  131. package/dist/esm/ollama_provider.js +1 -0
  132. package/dist/esm/openai_provider.js +1 -0
  133. package/dist/esm/pipeline/orchestrator.js +6 -12
  134. package/dist/esm/pipeline/stage0_preprocess.js +12 -19
  135. package/dist/esm/pipeline/stage2_coverage.js +1 -0
  136. package/dist/esm/pipeline/stage3_generation.js +1 -0
  137. package/dist/esm/progress.js +112 -0
  138. package/dist/esm/prompts/coverage.js +7 -24
  139. package/dist/esm/prompts/cross-impact.js +3 -21
  140. package/dist/esm/prompts/generation.js +158 -36
  141. package/dist/esm/prompts/generation_profile.js +147 -0
  142. package/dist/esm/prompts/heal.js +33 -15
  143. package/dist/esm/prompts/impact.js +3 -22
  144. package/dist/esm/prompts/json_extract.js +36 -0
  145. package/dist/esm/prompts/strategist.js +2 -20
  146. package/dist/esm/prompts/test-designer.js +6 -21
  147. package/dist/esm/provider_factory.js +6 -4
  148. package/dist/esm/reporters/junit.js +86 -0
  149. package/dist/esm/reporters/reporter.js +3 -0
  150. package/dist/esm/reporters/sarif.js +131 -0
  151. package/dist/esm/resilience/circuit_breaker.js +78 -0
  152. package/dist/esm/resilience/retry.js +56 -0
  153. package/dist/esm/sanitize.js +66 -0
  154. package/dist/esm/training/kg_scanner.js +115 -0
  155. package/dist/esm/training/scanner.js +27 -34
  156. package/dist/esm/version.js +33 -0
  157. package/dist/index.d.ts +21 -1
  158. package/dist/index.d.ts.map +1 -1
  159. package/dist/index.js +45 -1
  160. package/dist/knowledge/cluster_utils.d.ts +28 -0
  161. package/dist/knowledge/cluster_utils.d.ts.map +1 -0
  162. package/dist/knowledge/cluster_utils.js +67 -0
  163. package/dist/knowledge/kg_bridge.d.ts +31 -0
  164. package/dist/knowledge/kg_bridge.d.ts.map +1 -0
  165. package/dist/knowledge/kg_bridge.js +388 -0
  166. package/dist/knowledge/kg_types.d.ts +75 -0
  167. package/dist/knowledge/kg_types.d.ts.map +1 -0
  168. package/dist/knowledge/kg_types.js +4 -0
  169. package/dist/knowledge/route_families.d.ts +18 -0
  170. package/dist/knowledge/route_families.d.ts.map +1 -1
  171. package/dist/knowledge/route_families.js +91 -0
  172. package/dist/mcp-server.d.ts.map +1 -1
  173. package/dist/mcp-server.js +2 -4
  174. package/dist/metrics/prometheus.d.ts +37 -0
  175. package/dist/metrics/prometheus.d.ts.map +1 -0
  176. package/dist/metrics/prometheus.js +153 -0
  177. package/dist/model_router.d.ts +28 -0
  178. package/dist/model_router.d.ts.map +1 -0
  179. package/dist/model_router.js +63 -0
  180. package/dist/ollama_provider.d.ts.map +1 -1
  181. package/dist/ollama_provider.js +1 -0
  182. package/dist/openai_provider.d.ts.map +1 -1
  183. package/dist/openai_provider.js +1 -0
  184. package/dist/pipeline/orchestrator.d.ts +2 -0
  185. package/dist/pipeline/orchestrator.d.ts.map +1 -1
  186. package/dist/pipeline/orchestrator.js +6 -12
  187. package/dist/pipeline/stage0_preprocess.d.ts.map +1 -1
  188. package/dist/pipeline/stage0_preprocess.js +11 -18
  189. package/dist/pipeline/stage2_coverage.d.ts +2 -0
  190. package/dist/pipeline/stage2_coverage.d.ts.map +1 -1
  191. package/dist/pipeline/stage2_coverage.js +1 -0
  192. package/dist/pipeline/stage3_generation.d.ts +2 -0
  193. package/dist/pipeline/stage3_generation.d.ts.map +1 -1
  194. package/dist/pipeline/stage3_generation.js +1 -0
  195. package/dist/pipeline/stage4_heal.d.ts +2 -0
  196. package/dist/pipeline/stage4_heal.d.ts.map +1 -1
  197. package/dist/progress.d.ts +22 -0
  198. package/dist/progress.d.ts.map +1 -0
  199. package/dist/progress.js +116 -0
  200. package/dist/prompts/coverage.d.ts +2 -0
  201. package/dist/prompts/coverage.d.ts.map +1 -1
  202. package/dist/prompts/coverage.js +7 -24
  203. package/dist/prompts/cross-impact.d.ts +1 -0
  204. package/dist/prompts/cross-impact.d.ts.map +1 -1
  205. package/dist/prompts/cross-impact.js +3 -21
  206. package/dist/prompts/generation.d.ts +3 -1
  207. package/dist/prompts/generation.d.ts.map +1 -1
  208. package/dist/prompts/generation.js +158 -36
  209. package/dist/prompts/generation_profile.d.ts +29 -0
  210. package/dist/prompts/generation_profile.d.ts.map +1 -0
  211. package/dist/prompts/generation_profile.js +151 -0
  212. package/dist/prompts/heal.d.ts +3 -1
  213. package/dist/prompts/heal.d.ts.map +1 -1
  214. package/dist/prompts/heal.js +33 -15
  215. package/dist/prompts/impact.d.ts +1 -0
  216. package/dist/prompts/impact.d.ts.map +1 -1
  217. package/dist/prompts/impact.js +3 -22
  218. package/dist/prompts/json_extract.d.ts +14 -0
  219. package/dist/prompts/json_extract.d.ts.map +1 -0
  220. package/dist/prompts/json_extract.js +39 -0
  221. package/dist/prompts/strategist.d.ts.map +1 -1
  222. package/dist/prompts/strategist.js +2 -20
  223. package/dist/prompts/test-designer.d.ts +2 -0
  224. package/dist/prompts/test-designer.d.ts.map +1 -1
  225. package/dist/prompts/test-designer.js +6 -21
  226. package/dist/provider_factory.d.ts.map +1 -1
  227. package/dist/provider_factory.js +6 -4
  228. package/dist/reporters/junit.d.ts +6 -0
  229. package/dist/reporters/junit.d.ts.map +1 -0
  230. package/dist/reporters/junit.js +89 -0
  231. package/dist/reporters/reporter.d.ts +42 -0
  232. package/dist/reporters/reporter.d.ts.map +1 -0
  233. package/dist/reporters/reporter.js +4 -0
  234. package/dist/reporters/sarif.d.ts +7 -0
  235. package/dist/reporters/sarif.d.ts.map +1 -0
  236. package/dist/reporters/sarif.js +134 -0
  237. package/dist/resilience/circuit_breaker.d.ts +36 -0
  238. package/dist/resilience/circuit_breaker.d.ts.map +1 -0
  239. package/dist/resilience/circuit_breaker.js +82 -0
  240. package/dist/resilience/retry.d.ts +11 -0
  241. package/dist/resilience/retry.d.ts.map +1 -0
  242. package/dist/resilience/retry.js +59 -0
  243. package/dist/sanitize.d.ts +15 -0
  244. package/dist/sanitize.d.ts.map +1 -0
  245. package/dist/sanitize.js +71 -0
  246. package/dist/training/kg_scanner.d.ts +13 -0
  247. package/dist/training/kg_scanner.d.ts.map +1 -0
  248. package/dist/training/kg_scanner.js +118 -0
  249. package/dist/training/scanner.d.ts +7 -2
  250. package/dist/training/scanner.d.ts.map +1 -1
  251. package/dist/training/scanner.js +27 -34
  252. package/dist/version.d.ts +6 -0
  253. package/dist/version.d.ts.map +1 -0
  254. package/dist/version.js +36 -0
  255. package/package.json +7 -2
  256. package/schemas/route-families.schema.json +31 -1
@@ -0,0 +1,151 @@
1
+ "use strict";
2
+ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
3
+ // See LICENSE.txt for license information.
4
+ Object.defineProperty(exports, "__esModule", { value: true });
5
+ exports.resolveGenerationProfile = resolveGenerationProfile;
6
+ exports.isMattermostProfile = isMattermostProfile;
7
+ const framework_adapter_js_1 = require("../adapters/framework_adapter.js");
8
+ const MATTERMOST_PROFILE = {
9
+ projectName: 'Mattermost',
10
+ testFramework: 'Playwright',
11
+ importStatement: '@mattermost/playwright-lib',
12
+ conventions: [
13
+ 'Import ONLY from "@mattermost/playwright-lib" — no other test framework imports.',
14
+ 'Every test must call `await pw.initSetup()` first.',
15
+ 'Use `await pw.testBrowser.login(user)` to log in — never hardcode credentials.',
16
+ 'Use `expect` from "@mattermost/playwright-lib" — do NOT import from "@playwright/test".',
17
+ 'Include the copyright header for new files.',
18
+ ],
19
+ copyrightHeader: [
20
+ '// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.',
21
+ '// See LICENSE.txt for license information.',
22
+ ].join('\n'),
23
+ testMode: 'ui',
24
+ };
25
+ const DEFAULT_PLAYWRIGHT_PROFILE = {
26
+ projectName: 'Project',
27
+ testFramework: 'Playwright',
28
+ importStatement: '@playwright/test',
29
+ conventions: [
30
+ 'Import from "@playwright/test" for test and expect.',
31
+ 'Use page fixtures provided by Playwright.',
32
+ 'Prefer ARIA roles and data-testid attributes for selectors.',
33
+ 'Write one test per scenario with a descriptive name.',
34
+ ],
35
+ testMode: 'ui',
36
+ };
37
+ const DEFAULT_API_PROFILE = {
38
+ projectName: 'Project',
39
+ testFramework: 'vitest + supertest',
40
+ importStatement: 'vitest',
41
+ conventions: [
42
+ 'Import from "vitest" for test and expect.',
43
+ 'Use supertest for HTTP request assertions.',
44
+ 'Validate response status codes, headers, and body structure.',
45
+ 'Test both success and error paths for each endpoint.',
46
+ ],
47
+ testMode: 'api',
48
+ };
49
+ /**
50
+ * Resolves the generation profile from config and optional KG metadata.
51
+ * - If profile='mattermost' or Mattermost is detected, returns Mattermost profile.
52
+ * - If KG is present, derives project-specific profile from it.
53
+ * - Otherwise, returns generic Playwright profile.
54
+ */
55
+ function resolveGenerationProfile(config, kg) {
56
+ // Explicit Mattermost profile
57
+ if (config?.profile === 'mattermost') {
58
+ return { ...MATTERMOST_PROFILE };
59
+ }
60
+ // KG-based profile derivation
61
+ if (kg) {
62
+ const frameworks = kg.project.frameworks.map((f) => f.toLowerCase());
63
+ const isMattermost = kg.project.name.toLowerCase().includes('mattermost') ||
64
+ frameworks.includes('@mattermost/playwright-lib');
65
+ if (isMattermost) {
66
+ return { ...MATTERMOST_PROFILE };
67
+ }
68
+ const testMode = config?.testMode || deriveTestMode(frameworks);
69
+ const testFramework = deriveTestFramework(frameworks, testMode);
70
+ const importStatement = deriveImportStatement(frameworks, testMode);
71
+ return {
72
+ projectName: kg.project.name || 'Project',
73
+ testFramework,
74
+ importStatement,
75
+ conventions: buildConventions(testFramework, importStatement, testMode),
76
+ testMode,
77
+ };
78
+ }
79
+ // Default profiles based on test mode
80
+ if (config?.testMode === 'api') {
81
+ return { ...DEFAULT_API_PROFILE };
82
+ }
83
+ return { ...DEFAULT_PLAYWRIGHT_PROFILE };
84
+ }
85
+ /**
86
+ * Checks if a profile is the Mattermost profile (for backward compatibility checks).
87
+ */
88
+ function isMattermostProfile(profile) {
89
+ return profile.importStatement === '@mattermost/playwright-lib';
90
+ }
91
+ // ---------------------------------------------------------------------------
92
+ // Internal helpers
93
+ // ---------------------------------------------------------------------------
94
+ function deriveTestMode(frameworks) {
95
+ const uiSet = new Set(framework_adapter_js_1.UI_FRAMEWORKS);
96
+ const apiSet = new Set(framework_adapter_js_1.API_FRAMEWORKS);
97
+ const hasUiFramework = frameworks.some((f) => uiSet.has(f));
98
+ const hasApiFramework = frameworks.some((f) => apiSet.has(f));
99
+ if (hasUiFramework && hasApiFramework)
100
+ return 'both';
101
+ if (hasApiFramework && !hasUiFramework)
102
+ return 'api';
103
+ return 'ui';
104
+ }
105
+ function deriveTestFramework(frameworks, testMode) {
106
+ if (testMode === 'api') {
107
+ if (frameworks.includes('pytest'))
108
+ return 'pytest';
109
+ if (frameworks.includes('jest'))
110
+ return 'jest + supertest';
111
+ return 'vitest + supertest';
112
+ }
113
+ if (frameworks.includes('cypress'))
114
+ return 'Cypress';
115
+ if (frameworks.includes('selenium'))
116
+ return 'Selenium';
117
+ return 'Playwright';
118
+ }
119
+ function deriveImportStatement(frameworks, testMode) {
120
+ if (testMode === 'api') {
121
+ if (frameworks.includes('pytest'))
122
+ return 'pytest';
123
+ if (frameworks.includes('jest'))
124
+ return 'jest';
125
+ return 'vitest';
126
+ }
127
+ if (frameworks.includes('cypress'))
128
+ return 'cypress';
129
+ return '@playwright/test';
130
+ }
131
+ function buildConventions(testFramework, importStatement, testMode) {
132
+ const conventions = [];
133
+ if (testMode === 'api' || testMode === 'both') {
134
+ conventions.push(`Import from "${importStatement}" for test and expect.`);
135
+ conventions.push('Validate response status codes, headers, and body structure.');
136
+ conventions.push('Test both success and error paths for each endpoint.');
137
+ }
138
+ if (testMode === 'ui' || testMode === 'both') {
139
+ if (testFramework.includes('Playwright')) {
140
+ conventions.push('Import from "@playwright/test" for test and expect.');
141
+ conventions.push('Use page fixtures provided by Playwright.');
142
+ }
143
+ else if (testFramework.includes('Cypress')) {
144
+ conventions.push('Use cy.* commands for browser interaction.');
145
+ }
146
+ conventions.push('Prefer ARIA roles and data-testid attributes for selectors.');
147
+ }
148
+ conventions.push('Write one test per scenario with a descriptive name of what the user does and what is verified.');
149
+ conventions.push('NEVER fabricate test IDs. Use descriptive names only.');
150
+ return conventions;
151
+ }
@@ -1,4 +1,5 @@
1
1
  import type { FlowDecision } from '../validation/output_schema.js';
2
+ import type { GenerationProfile } from './generation_profile.js';
2
3
  export interface HealPromptContext {
3
4
  specPath: string;
4
5
  status: 'failed' | 'flaky';
@@ -6,6 +7,7 @@ export interface HealPromptContext {
6
7
  failureDetail?: string;
7
8
  /** Last 3 console errors from the test run */
8
9
  consoleErrors?: string[];
10
+ profile?: GenerationProfile;
9
11
  }
10
12
  /**
11
13
  * Builds a route-family-aware heal prompt for the playwright-test-healer agent.
@@ -17,5 +19,5 @@ export declare function buildHealPrompt(ctx: HealPromptContext): string;
17
19
  * Builds a minimal quality-fix prompt for spec files that fail content validation
18
20
  * (e.g. contain test.describe, test.only, wrong imports).
19
21
  */
20
- export declare function buildQualityFixPrompt(specPath: string, qualityIssues: string[]): string;
22
+ export declare function buildQualityFixPrompt(specPath: string, qualityIssues: string[], profile?: GenerationProfile): string;
21
23
  //# sourceMappingURL=heal.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"heal.d.ts","sourceRoot":"","sources":["../../src/prompts/heal.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,gCAAgC,CAAC;AAEjE,MAAM,WAAW,iBAAiB;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,QAAQ,GAAG,OAAO,CAAC;IAC3B,QAAQ,CAAC,EAAE,YAAY,CAAC;IACxB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,8CAA8C;IAC9C,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;CAC5B;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,iBAAiB,GAAG,MAAM,CAgD9D;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,GAAG,MAAM,CAgBvF"}
1
+ {"version":3,"file":"heal.d.ts","sourceRoot":"","sources":["../../src/prompts/heal.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,gCAAgC,CAAC;AAEjE,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,yBAAyB,CAAC;AAG/D,MAAM,WAAW,iBAAiB;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,QAAQ,GAAG,OAAO,CAAC;IAC3B,QAAQ,CAAC,EAAE,YAAY,CAAC;IACxB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,8CAA8C;IAC9C,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,OAAO,CAAC,EAAE,iBAAiB,CAAC;CAC/B;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,iBAAiB,GAAG,MAAM,CAgE9D;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,EAAE,OAAO,CAAC,EAAE,iBAAiB,GAAG,MAAM,CAmBpH"}
@@ -4,6 +4,8 @@
4
4
  Object.defineProperty(exports, "__esModule", { value: true });
5
5
  exports.buildHealPrompt = buildHealPrompt;
6
6
  exports.buildQualityFixPrompt = buildQualityFixPrompt;
7
+ const sanitize_js_1 = require("../crew/sanitize.js");
8
+ const generation_profile_js_1 = require("./generation_profile.js");
7
9
  /**
8
10
  * Builds a route-family-aware heal prompt for the playwright-test-healer agent.
9
11
  * Enriches the base healer constraints with flow context so the agent understands
@@ -14,24 +16,43 @@ function buildHealPrompt(ctx) {
14
16
  ? [
15
17
  '',
16
18
  'FLOW CONTEXT (use to understand test intent — do not change test objectives):',
17
- ` Flow: ${ctx.decision.flowName}`,
19
+ ` Flow: ${(0, sanitize_js_1.sanitizeForPrompt)(ctx.decision.flowName)}`,
18
20
  ` Route Family: ${ctx.decision.routeFamily}${ctx.decision.featureId ? ` / ${ctx.decision.featureId}` : ''}`,
19
21
  ` Route: ${ctx.decision.specificRoute || '(family-level)'}`,
20
- ` User Actions: ${ctx.decision.userActions.join('; ') || 'not specified'}`,
21
- ` Evidence: ${ctx.decision.evidence}`,
22
+ ` User Actions: ${(0, sanitize_js_1.sanitizeForPrompt)(ctx.decision.userActions.join('; ')) || 'not specified'}`,
23
+ ` Evidence: ${(0, sanitize_js_1.sanitizeForPrompt)(ctx.decision.evidence)}`,
22
24
  ].join('\n')
23
25
  : '';
24
26
  const statusNote = ctx.status === 'flaky'
25
27
  ? 'This test is FLAKY (passes sometimes, fails other times). Look for race conditions, missing waits, or order-dependent state.'
26
28
  : 'This test is FAILING consistently. The selector, URL, or API call is likely broken.';
27
29
  const failureBlock = ctx.failureDetail
28
- ? `\nFailure detail:\n${ctx.failureDetail}`
30
+ ? `\nFailure detail:\n${(0, sanitize_js_1.sanitizeForPrompt)(ctx.failureDetail)}`
29
31
  : '';
30
32
  const consoleBlock = ctx.consoleErrors && ctx.consoleErrors.length > 0
31
- ? `\nRecent console errors from test run:\n${ctx.consoleErrors.slice(-3).map((e) => ` - ${e}`).join('\n')}`
33
+ ? `\nRecent console errors from test run:\n${ctx.consoleErrors.slice(-3).map((e) => ` - ${(0, sanitize_js_1.sanitizeForPrompt)(e)}`).join('\n')}`
32
34
  : '';
35
+ const importLib = ctx.profile?.importStatement || '@mattermost/playwright-lib';
36
+ const isMM = ctx.profile ? (0, generation_profile_js_1.isMattermostProfile)(ctx.profile) : true;
37
+ const projectLabel = ctx.profile?.projectName || 'Mattermost';
38
+ const frameworkLabel = ctx.profile?.testFramework || 'Playwright';
39
+ const constraints = isMM
40
+ ? [
41
+ `- Import ONLY from "${importLib}". Do not use "@playwright/test" directly.`,
42
+ '- Do not use test.describe or test.only.',
43
+ '- Keep a single tag string matching the route family (e.g. "@channels", "@scheduled_posts").',
44
+ `- Use only existing ${projectLabel} ${frameworkLabel} fixture and page-object APIs.`,
45
+ '- Do NOT invent new pw.* clients or page object methods that do not exist.',
46
+ '- Avoid brittle class selectors (.backstage-navbar, .admin-console__wrapper, .left-panel, .panel-card).',
47
+ ]
48
+ : [
49
+ `- Import from "${importLib}".`,
50
+ '- Keep a single tag string matching the route family.',
51
+ `- Use only existing ${projectLabel} ${frameworkLabel} page-object APIs.`,
52
+ '- Do NOT invent page object methods that do not exist.',
53
+ ];
33
54
  return [
34
- 'Heal this specific Playwright test file and keep edits minimal.',
55
+ `Heal this specific ${frameworkLabel} test file and keep edits minimal.`,
35
56
  '',
36
57
  `Target test file: ${ctx.specPath}`,
37
58
  `Status: ${ctx.status.toUpperCase()} — ${statusNote}`,
@@ -40,12 +61,7 @@ function buildHealPrompt(ctx) {
40
61
  flowBlock,
41
62
  '',
42
63
  'Healing constraints (must follow):',
43
- '- Import ONLY from "@mattermost/playwright-lib". Do not use "@playwright/test" directly.',
44
- '- Do not use test.describe or test.only.',
45
- '- Keep a single tag string matching the route family (e.g. "@channels", "@scheduled_posts").',
46
- '- Use only existing Mattermost Playwright fixture and page-object APIs.',
47
- '- Do NOT invent new pw.* clients or page object methods that do not exist.',
48
- '- Avoid brittle class selectors (.backstage-navbar, .admin-console__wrapper, .left-panel, .panel-card).',
64
+ ...constraints,
49
65
  '- Prefer stable assertions using URL patterns, data-testid attributes, ARIA roles, and page-object methods.',
50
66
  '- For flaky tests: add explicit waits (waitFor, expect().toBeVisible()) before interactions.',
51
67
  '- Keep the test intent and scenario unchanged — only fix what is broken.',
@@ -58,9 +74,11 @@ function buildHealPrompt(ctx) {
58
74
  * Builds a minimal quality-fix prompt for spec files that fail content validation
59
75
  * (e.g. contain test.describe, test.only, wrong imports).
60
76
  */
61
- function buildQualityFixPrompt(specPath, qualityIssues) {
77
+ function buildQualityFixPrompt(specPath, qualityIssues, profile) {
78
+ const importLib = profile?.importStatement || '@mattermost/playwright-lib';
79
+ const frameworkLabel = profile?.testFramework || 'Playwright';
62
80
  return [
63
- 'Fix quality issues in this Playwright spec file. Make minimal edits only.',
81
+ `Fix quality issues in this ${frameworkLabel} spec file. Make minimal edits only.`,
64
82
  '',
65
83
  `Target file: ${specPath}`,
66
84
  '',
@@ -68,7 +86,7 @@ function buildQualityFixPrompt(specPath, qualityIssues) {
68
86
  ...qualityIssues.map((issue) => ` - ${issue}`),
69
87
  '',
70
88
  'Rules:',
71
- '- Import only from "@mattermost/playwright-lib".',
89
+ `- Import only from "${importLib}".`,
72
90
  '- Remove test.describe wrappers (flatten to top-level test() calls).',
73
91
  '- Remove test.only calls.',
74
92
  '- Ensure each test has exactly one tag string.',
@@ -11,6 +11,7 @@ export interface ImpactPromptContext {
11
11
  existingSpecs: SpecEntry[];
12
12
  apiSurface: ApiSurfaceCatalog;
13
13
  contextBlock: string;
14
+ projectName?: string;
14
15
  }
15
16
  export declare function buildImpactPrompt(ctx: ImpactPromptContext): string;
16
17
  export interface ImpactAgentResponse {
@@ -1 +1 @@
1
- {"version":3,"file":"impact.d.ts","sourceRoot":"","sources":["../../src/prompts/impact.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,gCAAgC,CAAC;AAChE,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,4BAA4B,CAAC;AAE1D,OAAO,EAA4B,KAAK,iBAAiB,EAAC,MAAM,6BAA6B,CAAC;AAE9F,MAAM,WAAW,mBAAmB;IAChC,MAAM,EAAE,WAAW,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,KAAK,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAC,CAAC,CAAC;IACtD,aAAa,EAAE,SAAS,EAAE,CAAC;IAC3B,UAAU,EAAE,iBAAiB,CAAC;IAC9B,YAAY,EAAE,MAAM,CAAC;CACxB;AAED,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,mBAAmB,GAAG,MAAM,CA0DlE;AAED,MAAM,WAAW,mBAAmB;IAChC,KAAK,EAAE,KAAK,CAAC;QACT,EAAE,EAAE,MAAM,CAAC;QACX,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;QACvB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;QACvB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;KAC3B,CAAC,CAAC;CACN;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,mBAAmB,GAAG,IAAI,CAsB5E"}
1
+ {"version":3,"file":"impact.d.ts","sourceRoot":"","sources":["../../src/prompts/impact.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,gCAAgC,CAAC;AAChE,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,4BAA4B,CAAC;AAG1D,OAAO,EAA4B,KAAK,iBAAiB,EAAC,MAAM,6BAA6B,CAAC;AAE9F,MAAM,WAAW,mBAAmB;IAChC,MAAM,EAAE,WAAW,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,KAAK,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAC,CAAC,CAAC;IACtD,aAAa,EAAE,SAAS,EAAE,CAAC;IAC3B,UAAU,EAAE,iBAAiB,CAAC;IAC9B,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,mBAAmB,GAAG,MAAM,CA0DlE;AAED,MAAM,WAAW,mBAAmB;IAChC,KAAK,EAAE,KAAK,CAAC;QACT,EAAE,EAAE,MAAM,CAAC;QACX,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;QACvB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;QACvB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;KAC3B,CAAC,CAAC;CACN;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,mBAAmB,GAAG,IAAI,CAM5E"}
@@ -4,6 +4,7 @@
4
4
  Object.defineProperty(exports, "__esModule", { value: true });
5
5
  exports.buildImpactPrompt = buildImpactPrompt;
6
6
  exports.parseImpactResponse = parseImpactResponse;
7
+ const json_extract_js_1 = require("./json_extract.js");
7
8
  const spec_index_js_1 = require("../knowledge/spec_index.js");
8
9
  const api_surface_js_1 = require("../knowledge/api_surface.js");
9
10
  function buildImpactPrompt(ctx) {
@@ -27,7 +28,7 @@ function buildImpactPrompt(ctx) {
27
28
  })
28
29
  .join('\n\n');
29
30
  return [
30
- 'You are analyzing code changes in Mattermost to identify impacted user-facing flows.',
31
+ `You are analyzing code changes in ${ctx.projectName || 'Mattermost'} to identify impacted user-facing flows.`,
31
32
  '',
32
33
  `ROUTE FAMILY: ${ctx.family.id}`,
33
34
  `ROUTES: ${familyRoutes}`,
@@ -62,25 +63,5 @@ function buildImpactPrompt(ctx) {
62
63
  ].filter(Boolean).join('\n');
63
64
  }
64
65
  function parseImpactResponse(text) {
65
- // Try to extract JSON from the response
66
- const fenced = text.match(/```(?:json)?\s*([\s\S]*?)```/i);
67
- const candidates = fenced ? [fenced[1], text] : [text];
68
- for (const candidate of candidates) {
69
- const start = candidate.indexOf('{');
70
- const end = candidate.lastIndexOf('}');
71
- if (start < 0 || end <= start) {
72
- continue;
73
- }
74
- const raw = candidate.slice(start, end + 1);
75
- try {
76
- const parsed = JSON.parse(raw);
77
- if (parsed && Array.isArray(parsed.flows)) {
78
- return parsed;
79
- }
80
- }
81
- catch {
82
- continue;
83
- }
84
- }
85
- return null;
66
+ return (0, json_extract_js_1.extractJsonFromResponse)(text, (obj) => obj != null && typeof obj === 'object' && Array.isArray(obj.flows));
86
67
  }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Shared JSON extraction from LLM text responses.
3
+ * Handles fenced code blocks, bare JSON, and partial text.
4
+ */
5
+ /**
6
+ * Extract and parse JSON from LLM response text.
7
+ * Tries fenced code blocks first, then raw text.
8
+ * Returns null if no valid JSON found.
9
+ *
10
+ * @param text - Raw LLM response text
11
+ * @param validate - Predicate to check if parsed object has the expected shape
12
+ */
13
+ export declare function extractJsonFromResponse<T>(text: string, validate: (obj: unknown) => obj is T): T | null;
14
+ //# sourceMappingURL=json_extract.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"json_extract.d.ts","sourceRoot":"","sources":["../../src/prompts/json_extract.ts"],"names":[],"mappings":"AAGA;;;GAGG;AAEH;;;;;;;GAOG;AACH,wBAAgB,uBAAuB,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,IAAI,CAqBvG"}
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
3
+ // See LICENSE.txt for license information.
4
+ Object.defineProperty(exports, "__esModule", { value: true });
5
+ exports.extractJsonFromResponse = extractJsonFromResponse;
6
+ /**
7
+ * Shared JSON extraction from LLM text responses.
8
+ * Handles fenced code blocks, bare JSON, and partial text.
9
+ */
10
+ /**
11
+ * Extract and parse JSON from LLM response text.
12
+ * Tries fenced code blocks first, then raw text.
13
+ * Returns null if no valid JSON found.
14
+ *
15
+ * @param text - Raw LLM response text
16
+ * @param validate - Predicate to check if parsed object has the expected shape
17
+ */
18
+ function extractJsonFromResponse(text, validate) {
19
+ const fenced = text.match(/```(?:json)?\s*([\s\S]*?)```/i);
20
+ const candidates = fenced ? [fenced[1], text] : [text];
21
+ for (const candidate of candidates) {
22
+ const start = candidate.indexOf('{');
23
+ const end = candidate.lastIndexOf('}');
24
+ if (start < 0 || end <= start) {
25
+ continue;
26
+ }
27
+ const raw = candidate.slice(start, end + 1);
28
+ try {
29
+ const parsed = JSON.parse(raw);
30
+ if (validate(parsed)) {
31
+ return parsed;
32
+ }
33
+ }
34
+ catch {
35
+ continue;
36
+ }
37
+ }
38
+ return null;
39
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"strategist.d.ts","sourceRoot":"","sources":["../../src/prompts/strategist.ts"],"names":[],"mappings":"AAGA;;;GAGG;AAEH,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,gCAAgC,CAAC;AACjE,OAAO,KAAK,EAAC,WAAW,EAAE,cAAc,EAAgB,MAAM,kBAAkB,CAAC;AAGjF,MAAM,WAAW,uBAAuB;IACpC,aAAa,EAAE,YAAY,EAAE,CAAC;IAC9B,YAAY,EAAE,WAAW,EAAE,CAAC;IAC5B,eAAe,EAAE,cAAc,EAAE,CAAC;CACrC;AAED,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,uBAAuB,GAAG,MAAM,CA4D1E;AAED,MAAM,WAAW,uBAAuB;IACpC,QAAQ,EAAE,KAAK,CAAC;QACZ,MAAM,EAAE,MAAM,CAAC;QACf,QAAQ,EAAE,MAAM,CAAC;QACjB,QAAQ,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,MAAM,CAAC;QACtC,QAAQ,EAAE,WAAW,GAAG,YAAY,GAAG,MAAM,GAAG,eAAe,GAAG,MAAM,CAAC;QACzE,SAAS,EAAE,MAAM,CAAC;QAClB,cAAc,EAAE,MAAM,EAAE,CAAC;QACzB,eAAe,EAAE,MAAM,GAAG,QAAQ,GAAG,KAAK,GAAG,MAAM,GAAG,MAAM,CAAC;KAChE,CAAC,CAAC;CACN;AAED,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,uBAAuB,GAAG,IAAI,CAqBpF"}
1
+ {"version":3,"file":"strategist.d.ts","sourceRoot":"","sources":["../../src/prompts/strategist.ts"],"names":[],"mappings":"AAGA;;;GAGG;AAEH,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,gCAAgC,CAAC;AACjE,OAAO,KAAK,EAAC,WAAW,EAAE,cAAc,EAAgB,MAAM,kBAAkB,CAAC;AAIjF,MAAM,WAAW,uBAAuB;IACpC,aAAa,EAAE,YAAY,EAAE,CAAC;IAC9B,YAAY,EAAE,WAAW,EAAE,CAAC;IAC5B,eAAe,EAAE,cAAc,EAAE,CAAC;CACrC;AAED,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,uBAAuB,GAAG,MAAM,CA4D1E;AAED,MAAM,WAAW,uBAAuB;IACpC,QAAQ,EAAE,KAAK,CAAC;QACZ,MAAM,EAAE,MAAM,CAAC;QACf,QAAQ,EAAE,MAAM,CAAC;QACjB,QAAQ,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,MAAM,CAAC;QACtC,QAAQ,EAAE,WAAW,GAAG,YAAY,GAAG,MAAM,GAAG,eAAe,GAAG,MAAM,CAAC;QACzE,SAAS,EAAE,MAAM,CAAC;QAClB,cAAc,EAAE,MAAM,EAAE,CAAC;QACzB,eAAe,EAAE,MAAM,GAAG,QAAQ,GAAG,KAAK,GAAG,MAAM,GAAG,MAAM,CAAC;KAChE,CAAC,CAAC;CACN;AAED,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,uBAAuB,GAAG,IAAI,CAMpF"}
@@ -5,6 +5,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
5
5
  exports.buildStrategistPrompt = buildStrategistPrompt;
6
6
  exports.parseStrategistResponse = parseStrategistResponse;
7
7
  const sanitize_js_1 = require("../crew/sanitize.js");
8
+ const json_extract_js_1 = require("./json_extract.js");
8
9
  function buildStrategistPrompt(ctx) {
9
10
  const flowsBlock = ctx.impactedFlows
10
11
  .map((f) => {
@@ -60,24 +61,5 @@ function buildStrategistPrompt(ctx) {
60
61
  ].join('\n');
61
62
  }
62
63
  function parseStrategistResponse(text) {
63
- const fenced = text.match(/```(?:json)?\s*([\s\S]*?)```/i);
64
- const candidates = fenced ? [fenced[1], text] : [text];
65
- for (const candidate of candidates) {
66
- const start = candidate.indexOf('{');
67
- const end = candidate.lastIndexOf('}');
68
- if (start < 0 || end <= start) {
69
- continue;
70
- }
71
- const raw = candidate.slice(start, end + 1);
72
- try {
73
- const parsed = JSON.parse(raw);
74
- if (parsed && Array.isArray(parsed.strategy)) {
75
- return parsed;
76
- }
77
- }
78
- catch {
79
- continue;
80
- }
81
- }
82
- return null;
64
+ return (0, json_extract_js_1.extractJsonFromResponse)(text, (obj) => obj != null && typeof obj === 'object' && Array.isArray(obj.strategy));
83
65
  }
@@ -6,12 +6,14 @@ import type { FlowDecision } from '../validation/output_schema.js';
6
6
  import { type ApiSurfaceCatalog } from '../knowledge/api_surface.js';
7
7
  import type { SpecEntry } from '../knowledge/spec_index.js';
8
8
  import type { StrategyEntry, CrossImpact } from '../crew/types.js';
9
+ import type { GenerationProfile } from './generation_profile.js';
9
10
  export interface TestDesignerPromptContext {
10
11
  flow: FlowDecision;
11
12
  strategy: StrategyEntry;
12
13
  apiSurface: ApiSurfaceCatalog;
13
14
  existingSpecs: SpecEntry[];
14
15
  crossImpacts: CrossImpact[];
16
+ profile?: GenerationProfile;
15
17
  }
16
18
  export declare function buildTestDesignerPrompt(ctx: TestDesignerPromptContext): string;
17
19
  export interface TestDesignerAgentResponse {
@@ -1 +1 @@
1
- {"version":3,"file":"test-designer.d.ts","sourceRoot":"","sources":["../../src/prompts/test-designer.ts"],"names":[],"mappings":"AAGA;;;GAGG;AAEH,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,gCAAgC,CAAC;AACjE,OAAO,EAA4B,KAAK,iBAAiB,EAAC,MAAM,6BAA6B,CAAC;AAC9F,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,4BAA4B,CAAC;AAC1D,OAAO,KAAK,EAAC,aAAa,EAAE,WAAW,EAAa,MAAM,kBAAkB,CAAC;AAG7E,MAAM,WAAW,yBAAyB;IACtC,IAAI,EAAE,YAAY,CAAC;IACnB,QAAQ,EAAE,aAAa,CAAC;IACxB,UAAU,EAAE,iBAAiB,CAAC;IAC9B,aAAa,EAAE,SAAS,EAAE,CAAC;IAC3B,YAAY,EAAE,WAAW,EAAE,CAAC;CAC/B;AAED,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,yBAAyB,GAAG,MAAM,CAuF9E;AAED,MAAM,WAAW,yBAAyB;IACtC,UAAU,EAAE;QACR,MAAM,EAAE,MAAM,CAAC;QACf,QAAQ,EAAE,MAAM,CAAC;QACjB,SAAS,EAAE,KAAK,CAAC;YACb,IAAI,EAAE,MAAM,CAAC;YACb,IAAI,EAAE,YAAY,GAAG,WAAW,GAAG,UAAU,GAAG,UAAU,GAAG,kBAAkB,GAAG,gBAAgB,GAAG,YAAY,GAAG,eAAe,GAAG,aAAa,GAAG,MAAM,CAAC;YAC7J,aAAa,EAAE,MAAM,EAAE,CAAC;YACxB,KAAK,EAAE,MAAM,EAAE,CAAC;YAChB,eAAe,EAAE,MAAM,CAAC;YACxB,QAAQ,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,MAAM,CAAC;YACtC,SAAS,EAAE,MAAM,CAAC;SACrB,CAAC,CAAC;KACN,CAAC;CACL;AAED,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,MAAM,GAAG,yBAAyB,GAAG,IAAI,CAqBxF"}
1
+ {"version":3,"file":"test-designer.d.ts","sourceRoot":"","sources":["../../src/prompts/test-designer.ts"],"names":[],"mappings":"AAGA;;;GAGG;AAEH,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,gCAAgC,CAAC;AAEjE,OAAO,EAA4B,KAAK,iBAAiB,EAAC,MAAM,6BAA6B,CAAC;AAC9F,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,4BAA4B,CAAC;AAC1D,OAAO,KAAK,EAAC,aAAa,EAAE,WAAW,EAAa,MAAM,kBAAkB,CAAC;AAE7E,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,yBAAyB,CAAC;AAE/D,MAAM,WAAW,yBAAyB;IACtC,IAAI,EAAE,YAAY,CAAC;IACnB,QAAQ,EAAE,aAAa,CAAC;IACxB,UAAU,EAAE,iBAAiB,CAAC;IAC9B,aAAa,EAAE,SAAS,EAAE,CAAC;IAC3B,YAAY,EAAE,WAAW,EAAE,CAAC;IAC5B,OAAO,CAAC,EAAE,iBAAiB,CAAC;CAC/B;AAED,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,yBAAyB,GAAG,MAAM,CAuF9E;AAED,MAAM,WAAW,yBAAyB;IACtC,UAAU,EAAE;QACR,MAAM,EAAE,MAAM,CAAC;QACf,QAAQ,EAAE,MAAM,CAAC;QACjB,SAAS,EAAE,KAAK,CAAC;YACb,IAAI,EAAE,MAAM,CAAC;YACb,IAAI,EAAE,YAAY,GAAG,WAAW,GAAG,UAAU,GAAG,UAAU,GAAG,kBAAkB,GAAG,gBAAgB,GAAG,YAAY,GAAG,eAAe,GAAG,aAAa,GAAG,MAAM,CAAC;YAC7J,aAAa,EAAE,MAAM,EAAE,CAAC;YACxB,KAAK,EAAE,MAAM,EAAE,CAAC;YAChB,eAAe,EAAE,MAAM,CAAC;YACxB,QAAQ,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,MAAM,CAAC;YACtC,SAAS,EAAE,MAAM,CAAC;SACrB,CAAC,CAAC;KACN,CAAC;CACL;AAED,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,MAAM,GAAG,yBAAyB,GAAG,IAAI,CAQxF"}
@@ -4,6 +4,7 @@
4
4
  Object.defineProperty(exports, "__esModule", { value: true });
5
5
  exports.buildTestDesignerPrompt = buildTestDesignerPrompt;
6
6
  exports.parseTestDesignerResponse = parseTestDesignerResponse;
7
+ const json_extract_js_1 = require("./json_extract.js");
7
8
  const api_surface_js_1 = require("../knowledge/api_surface.js");
8
9
  const sanitize_js_1 = require("../crew/sanitize.js");
9
10
  function buildTestDesignerPrompt(ctx) {
@@ -28,7 +29,7 @@ function buildTestDesignerPrompt(ctx) {
28
29
  : 'None detected.';
29
30
  const categories = ctx.strategy.testCategories.join(', ');
30
31
  return [
31
- 'You are a senior QA engineer designing comprehensive test cases for a Mattermost user flow.',
32
+ `You are a senior QA engineer designing comprehensive test cases for a ${ctx.profile?.projectName || 'Mattermost'} user flow.`,
32
33
  '',
33
34
  `FLOW: ${ctx.flow.flowName}`,
34
35
  `Flow ID: ${ctx.flow.flowId}`,
@@ -88,24 +89,8 @@ function buildTestDesignerPrompt(ctx) {
88
89
  ].join('\n');
89
90
  }
90
91
  function parseTestDesignerResponse(text) {
91
- const fenced = text.match(/```(?:json)?\s*([\s\S]*?)```/i);
92
- const candidates = fenced ? [fenced[1], text] : [text];
93
- for (const candidate of candidates) {
94
- const start = candidate.indexOf('{');
95
- const end = candidate.lastIndexOf('}');
96
- if (start < 0 || end <= start) {
97
- continue;
98
- }
99
- const raw = candidate.slice(start, end + 1);
100
- try {
101
- const parsed = JSON.parse(raw);
102
- if (parsed?.testDesign?.testCases && Array.isArray(parsed.testDesign.testCases)) {
103
- return parsed;
104
- }
105
- }
106
- catch {
107
- continue;
108
- }
109
- }
110
- return null;
92
+ return (0, json_extract_js_1.extractJsonFromResponse)(text, (obj) => {
93
+ const r = obj;
94
+ return r?.testDesign?.testCases != null && Array.isArray(r.testDesign.testCases);
95
+ });
111
96
  }
@@ -1 +1 @@
1
- {"version":3,"file":"provider_factory.d.ts","sourceRoot":"","sources":["../src/provider_factory.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAKR,WAAW,EAIX,cAAc,EAEjB,MAAM,yBAAyB,CAAC;AAGjC;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,qBAAa,kBAAkB;IAC3B;;OAEG;IACH,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,cAAc,GAAG,WAAW;IAmBlD;;;;;;;;;;;OAWG;IACH,MAAM,CAAC,YAAY,CAAC,MAAM,EAAE,YAAY,GAAG,WAAW;IAWtD;;;;;;;;;OASG;WACU,aAAa,IAAI,OAAO,CAAC,WAAW,CAAC;IAyElD;;;OAGG;WACU,oBAAoB,CAAC,kBAAkB,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IASpF;;;;;;;;;;OAUG;IACH,MAAM,CAAC,gBAAgB,CAAC,cAAc,EAAE,MAAM,GAAG,WAAW;CAkC/D;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IACzB;;;OAGG;IACH,OAAO,EAAE,cAAc,CAAC;IAExB;;;OAGG;IACH,QAAQ,EAAE,cAAc,CAAC;IAEzB;;;OAGG;IACH,cAAc,CAAC,EAAE,KAAK,CAAC,QAAQ,GAAG,mBAAmB,GAAG,wBAAwB,CAAC,CAAC;CACrF;AA+ID;;GAEG;AACH,wBAAsB,qBAAqB,CAAC,QAAQ,EAAE,WAAW,GAAG,OAAO,CAAC;IACxE,KAAK,EAAE,OAAO,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,EAAE,CAAC;CAC1B,CAAC,CAsCD"}
1
+ {"version":3,"file":"provider_factory.d.ts","sourceRoot":"","sources":["../src/provider_factory.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAKR,WAAW,EAIX,cAAc,EAEjB,MAAM,yBAAyB,CAAC;AAGjC;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,qBAAa,kBAAkB;IAC3B;;OAEG;IACH,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,cAAc,GAAG,WAAW;IAmBlD;;;;;;;;;;;OAWG;IACH,MAAM,CAAC,YAAY,CAAC,MAAM,EAAE,YAAY,GAAG,WAAW;IAWtD;;;;;;;;;OASG;WACU,aAAa,IAAI,OAAO,CAAC,WAAW,CAAC;IAyElD;;;OAGG;WACU,oBAAoB,CAAC,kBAAkB,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IASpF;;;;;;;;;;OAUG;IACH,MAAM,CAAC,gBAAgB,CAAC,cAAc,EAAE,MAAM,GAAG,WAAW;CAkC/D;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IACzB;;;OAGG;IACH,OAAO,EAAE,cAAc,CAAC;IAExB;;;OAGG;IACH,QAAQ,EAAE,cAAc,CAAC;IAEzB;;;OAGG;IACH,cAAc,CAAC,EAAE,KAAK,CAAC,QAAQ,GAAG,mBAAmB,GAAG,wBAAwB,CAAC,CAAC;CACrF;AAgJD;;GAEG;AACH,wBAAsB,qBAAqB,CAAC,QAAQ,EAAE,WAAW,GAAG,OAAO,CAAC;IACxE,KAAK,EAAE,OAAO,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,EAAE,CAAC;CAC1B,CAAC,CAsCD"}
@@ -268,15 +268,17 @@ class HybridProvider {
268
268
  const primaryStats = this.primary.getUsageStats();
269
269
  const fallbackStats = this.fallback.getUsageStats();
270
270
  // Combine stats
271
+ const totalRequests = primaryStats.requestCount + fallbackStats.requestCount;
271
272
  return {
272
- requestCount: primaryStats.requestCount + fallbackStats.requestCount,
273
+ requestCount: totalRequests,
273
274
  totalInputTokens: primaryStats.totalInputTokens + fallbackStats.totalInputTokens,
274
275
  totalOutputTokens: primaryStats.totalOutputTokens + fallbackStats.totalOutputTokens,
275
276
  totalTokens: primaryStats.totalTokens + fallbackStats.totalTokens,
276
277
  totalCost: primaryStats.totalCost + fallbackStats.totalCost,
277
- averageResponseTimeMs: (primaryStats.averageResponseTimeMs * primaryStats.requestCount +
278
- fallbackStats.averageResponseTimeMs * fallbackStats.requestCount) /
279
- (primaryStats.requestCount + fallbackStats.requestCount),
278
+ averageResponseTimeMs: totalRequests > 0
279
+ ? (primaryStats.averageResponseTimeMs * primaryStats.requestCount +
280
+ fallbackStats.averageResponseTimeMs * fallbackStats.requestCount) / totalRequests
281
+ : 0,
280
282
  failedRequests: primaryStats.failedRequests + fallbackStats.failedRequests,
281
283
  startTime: new Date(Math.min(primaryStats.startTime.getTime(), fallbackStats.startTime.getTime())),
282
284
  lastUpdated: new Date(Math.max(primaryStats.lastUpdated.getTime(), fallbackStats.lastUpdated.getTime())),
@@ -0,0 +1,6 @@
1
+ /**
2
+ * JUnit XML reporter — maps crew results to JUnit format for Jenkins/GitLab CI.
3
+ */
4
+ import type { Reporter } from './reporter.js';
5
+ export declare const junitReporter: Reporter;
6
+ //# sourceMappingURL=junit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"junit.d.ts","sourceRoot":"","sources":["../../src/reporters/junit.ts"],"names":[],"mappings":"AAGA;;GAEG;AAEH,OAAO,KAAK,EAAc,QAAQ,EAAC,MAAM,eAAe,CAAC;AA6BzD,eAAO,MAAM,aAAa,EAAE,QAyE3B,CAAC"}
@@ -0,0 +1,89 @@
1
+ "use strict";
2
+ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
3
+ // See LICENSE.txt for license information.
4
+ Object.defineProperty(exports, "__esModule", { value: true });
5
+ exports.junitReporter = void 0;
6
+ function escapeXml(str) {
7
+ return str
8
+ .replace(/&/g, '&amp;')
9
+ .replace(/</g, '&lt;')
10
+ .replace(/>/g, '&gt;')
11
+ .replace(/"/g, '&quot;')
12
+ .replace(/'/g, '&apos;');
13
+ }
14
+ function buildTestCase(tc, flowName) {
15
+ const className = escapeXml(flowName.replace(/\s+/g, '.'));
16
+ const testName = escapeXml(tc.name);
17
+ return ` <testcase classname="${className}" name="${testName}" status="${escapeXml(tc.priority)}">\n` +
18
+ ` <properties>\n` +
19
+ ` <property name="type" value="${escapeXml(tc.type)}" />\n` +
20
+ ` <property name="priority" value="${escapeXml(tc.priority)}" />\n` +
21
+ ` </properties>\n` +
22
+ ` </testcase>`;
23
+ }
24
+ function buildFailureCase(finding) {
25
+ const name = escapeXml(finding.title);
26
+ return ` <testcase classname="findings" name="${name}">\n` +
27
+ ` <failure message="${escapeXml(finding.title)}" type="${escapeXml(finding.severity)}">${escapeXml(finding.description)}</failure>\n` +
28
+ ` </testcase>`;
29
+ }
30
+ exports.junitReporter = {
31
+ name: 'junit',
32
+ extension: '.xml',
33
+ format(results) {
34
+ const suites = [];
35
+ // Build a lookup from flowName -> test cases
36
+ const designsByFlow = new Map();
37
+ for (const design of results.testDesigns) {
38
+ designsByFlow.set(design.flowName, design.testCases);
39
+ }
40
+ // High-severity findings as failure cases
41
+ const highFindings = results.findings.filter((f) => f.severity === 'high');
42
+ // Each strategy entry becomes a test suite
43
+ for (const entry of results.strategyEntries) {
44
+ const testCases = designsByFlow.get(entry.flowName) ?? [];
45
+ const failures = highFindings.filter((f) => f.title.includes(entry.flowName));
46
+ const totalTests = testCases.length + failures.length;
47
+ const casesXml = testCases.map((tc) => buildTestCase(tc, entry.flowName)).join('\n');
48
+ const failuresXml = failures.map((f) => buildFailureCase(f)).join('\n');
49
+ const allCases = [casesXml, failuresXml].filter(Boolean).join('\n');
50
+ // Warnings as system-out
51
+ const warningsText = results.warnings.length > 0
52
+ ? ` <system-out>${escapeXml(results.warnings.join('\n'))}</system-out>`
53
+ : '';
54
+ suites.push(` <testsuite name="${escapeXml(entry.flowName)}" tests="${totalTests}" failures="${failures.length}" ` +
55
+ `id="${escapeXml(entry.flowId)}">\n` +
56
+ ` <properties>\n` +
57
+ ` <property name="priority" value="${escapeXml(entry.priority)}" />\n` +
58
+ ` <property name="approach" value="${escapeXml(entry.approach)}" />\n` +
59
+ ` <property name="rationale" value="${escapeXml(entry.rationale)}" />\n` +
60
+ ` </properties>\n` +
61
+ (allCases ? allCases + '\n' : '') +
62
+ (warningsText ? warningsText + '\n' : '') +
63
+ ` </testsuite>`);
64
+ }
65
+ // Remaining high findings not tied to a strategy entry
66
+ const coveredFlowNames = new Set(results.strategyEntries.map((e) => e.flowName));
67
+ const uncoveredFindings = highFindings.filter((f) => !Array.from(coveredFlowNames).some((name) => f.title.includes(name)));
68
+ if (uncoveredFindings.length > 0) {
69
+ const failureCases = uncoveredFindings.map((f) => buildFailureCase(f)).join('\n');
70
+ suites.push(` <testsuite name="findings" tests="${uncoveredFindings.length}" failures="${uncoveredFindings.length}">\n` +
71
+ failureCases + '\n' +
72
+ ` </testsuite>`);
73
+ }
74
+ const totalTests = results.testDesigns.reduce((sum, d) => sum + d.testCases.length, 0) + highFindings.length;
75
+ const totalFailures = highFindings.length;
76
+ return `<?xml version="1.0" encoding="UTF-8"?>\n` +
77
+ `<testsuites name="e2e-agents: ${escapeXml(results.workflow)}" ` +
78
+ `tests="${totalTests}" failures="${totalFailures}" ` +
79
+ `time="0">\n` +
80
+ ` <properties>\n` +
81
+ ` <property name="changedFiles" value="${results.changedFiles}" />\n` +
82
+ ` <property name="impactedFlows" value="${results.impactedFlows}" />\n` +
83
+ ` <property name="cost" value="${results.cost}" />\n` +
84
+ ` <property name="tokens" value="${results.tokens}" />\n` +
85
+ ` </properties>\n` +
86
+ suites.join('\n') + '\n' +
87
+ `</testsuites>\n`;
88
+ },
89
+ };