@yasserkhanorg/e2e-agents 0.3.2

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 (221) hide show
  1. package/LICENSE +168 -0
  2. package/README.md +620 -0
  3. package/dist/agent/analysis.d.ts +62 -0
  4. package/dist/agent/analysis.d.ts.map +1 -0
  5. package/dist/agent/analysis.js +292 -0
  6. package/dist/agent/blast_radius.d.ts +4 -0
  7. package/dist/agent/blast_radius.d.ts.map +1 -0
  8. package/dist/agent/blast_radius.js +37 -0
  9. package/dist/agent/cache_utils.d.ts +38 -0
  10. package/dist/agent/cache_utils.d.ts.map +1 -0
  11. package/dist/agent/cache_utils.js +67 -0
  12. package/dist/agent/config.d.ts +148 -0
  13. package/dist/agent/config.d.ts.map +1 -0
  14. package/dist/agent/config.js +640 -0
  15. package/dist/agent/dependency_graph.d.ts +14 -0
  16. package/dist/agent/dependency_graph.d.ts.map +1 -0
  17. package/dist/agent/dependency_graph.js +227 -0
  18. package/dist/agent/feedback.d.ts +55 -0
  19. package/dist/agent/feedback.d.ts.map +1 -0
  20. package/dist/agent/feedback.js +257 -0
  21. package/dist/agent/flags.d.ts +23 -0
  22. package/dist/agent/flags.d.ts.map +1 -0
  23. package/dist/agent/flags.js +171 -0
  24. package/dist/agent/flow_catalog.d.ts +25 -0
  25. package/dist/agent/flow_catalog.d.ts.map +1 -0
  26. package/dist/agent/flow_catalog.js +106 -0
  27. package/dist/agent/flow_mapping.d.ts +10 -0
  28. package/dist/agent/flow_mapping.d.ts.map +1 -0
  29. package/dist/agent/flow_mapping.js +84 -0
  30. package/dist/agent/framework.d.ts +13 -0
  31. package/dist/agent/framework.d.ts.map +1 -0
  32. package/dist/agent/framework.js +149 -0
  33. package/dist/agent/gap_suggestions.d.ts +14 -0
  34. package/dist/agent/gap_suggestions.d.ts.map +1 -0
  35. package/dist/agent/gap_suggestions.js +101 -0
  36. package/dist/agent/generator.d.ts +10 -0
  37. package/dist/agent/generator.d.ts.map +1 -0
  38. package/dist/agent/generator.js +115 -0
  39. package/dist/agent/git.d.ts +11 -0
  40. package/dist/agent/git.d.ts.map +1 -0
  41. package/dist/agent/git.js +90 -0
  42. package/dist/agent/handoff.d.ts +22 -0
  43. package/dist/agent/handoff.d.ts.map +1 -0
  44. package/dist/agent/handoff.js +180 -0
  45. package/dist/agent/impact-analyzer.d.ts +114 -0
  46. package/dist/agent/impact-analyzer.d.ts.map +1 -0
  47. package/dist/agent/impact-analyzer.js +557 -0
  48. package/dist/agent/index.d.ts +21 -0
  49. package/dist/agent/index.d.ts.map +1 -0
  50. package/dist/agent/index.js +38 -0
  51. package/dist/agent/model-router.d.ts +57 -0
  52. package/dist/agent/model-router.d.ts.map +1 -0
  53. package/dist/agent/model-router.js +154 -0
  54. package/dist/agent/operational_insights.d.ts +41 -0
  55. package/dist/agent/operational_insights.d.ts.map +1 -0
  56. package/dist/agent/operational_insights.js +126 -0
  57. package/dist/agent/pipeline.d.ts +23 -0
  58. package/dist/agent/pipeline.d.ts.map +1 -0
  59. package/dist/agent/pipeline.js +609 -0
  60. package/dist/agent/plan.d.ts +91 -0
  61. package/dist/agent/plan.d.ts.map +1 -0
  62. package/dist/agent/plan.js +331 -0
  63. package/dist/agent/playwright_report.d.ts +8 -0
  64. package/dist/agent/playwright_report.d.ts.map +1 -0
  65. package/dist/agent/playwright_report.js +126 -0
  66. package/dist/agent/report-generator.d.ts +24 -0
  67. package/dist/agent/report-generator.d.ts.map +1 -0
  68. package/dist/agent/report-generator.js +250 -0
  69. package/dist/agent/report.d.ts +81 -0
  70. package/dist/agent/report.d.ts.map +1 -0
  71. package/dist/agent/report.js +147 -0
  72. package/dist/agent/runner.d.ts +7 -0
  73. package/dist/agent/runner.d.ts.map +1 -0
  74. package/dist/agent/runner.js +576 -0
  75. package/dist/agent/selectors.d.ts +10 -0
  76. package/dist/agent/selectors.d.ts.map +1 -0
  77. package/dist/agent/selectors.js +75 -0
  78. package/dist/agent/spec-bridge.d.ts +101 -0
  79. package/dist/agent/spec-bridge.d.ts.map +1 -0
  80. package/dist/agent/spec-bridge.js +273 -0
  81. package/dist/agent/spec-builder.d.ts +102 -0
  82. package/dist/agent/spec-builder.d.ts.map +1 -0
  83. package/dist/agent/spec-builder.js +273 -0
  84. package/dist/agent/subsystem_risk.d.ts +23 -0
  85. package/dist/agent/subsystem_risk.d.ts.map +1 -0
  86. package/dist/agent/subsystem_risk.js +207 -0
  87. package/dist/agent/telemetry.d.ts +84 -0
  88. package/dist/agent/telemetry.d.ts.map +1 -0
  89. package/dist/agent/telemetry.js +220 -0
  90. package/dist/agent/test_path.d.ts +2 -0
  91. package/dist/agent/test_path.d.ts.map +1 -0
  92. package/dist/agent/test_path.js +23 -0
  93. package/dist/agent/tests.d.ts +18 -0
  94. package/dist/agent/tests.d.ts.map +1 -0
  95. package/dist/agent/tests.js +106 -0
  96. package/dist/agent/traceability.d.ts +22 -0
  97. package/dist/agent/traceability.d.ts.map +1 -0
  98. package/dist/agent/traceability.js +183 -0
  99. package/dist/agent/traceability_capture.d.ts +18 -0
  100. package/dist/agent/traceability_capture.d.ts.map +1 -0
  101. package/dist/agent/traceability_capture.js +313 -0
  102. package/dist/agent/traceability_ingest.d.ts +21 -0
  103. package/dist/agent/traceability_ingest.d.ts.map +1 -0
  104. package/dist/agent/traceability_ingest.js +237 -0
  105. package/dist/agent/utils.d.ts +13 -0
  106. package/dist/agent/utils.d.ts.map +1 -0
  107. package/dist/agent/utils.js +152 -0
  108. package/dist/agent/validators/selector-validator.d.ts +74 -0
  109. package/dist/agent/validators/selector-validator.d.ts.map +1 -0
  110. package/dist/agent/validators/selector-validator.js +165 -0
  111. package/dist/anthropic_provider.d.ts +65 -0
  112. package/dist/anthropic_provider.d.ts.map +1 -0
  113. package/dist/anthropic_provider.js +332 -0
  114. package/dist/api.d.ts +48 -0
  115. package/dist/api.d.ts.map +1 -0
  116. package/dist/api.js +113 -0
  117. package/dist/base_provider.d.ts +53 -0
  118. package/dist/base_provider.d.ts.map +1 -0
  119. package/dist/base_provider.js +81 -0
  120. package/dist/cli.d.ts +3 -0
  121. package/dist/cli.d.ts.map +1 -0
  122. package/dist/cli.js +843 -0
  123. package/dist/custom_provider.d.ts +20 -0
  124. package/dist/custom_provider.d.ts.map +1 -0
  125. package/dist/custom_provider.js +276 -0
  126. package/dist/e2e-test-gen/index.d.ts +51 -0
  127. package/dist/e2e-test-gen/index.d.ts.map +1 -0
  128. package/dist/e2e-test-gen/index.js +57 -0
  129. package/dist/e2e-test-gen/spec_parser.d.ts +142 -0
  130. package/dist/e2e-test-gen/spec_parser.d.ts.map +1 -0
  131. package/dist/e2e-test-gen/spec_parser.js +786 -0
  132. package/dist/e2e-test-gen/types.d.ts +185 -0
  133. package/dist/e2e-test-gen/types.d.ts.map +1 -0
  134. package/dist/e2e-test-gen/types.js +4 -0
  135. package/dist/esm/agent/analysis.js +287 -0
  136. package/dist/esm/agent/blast_radius.js +34 -0
  137. package/dist/esm/agent/cache_utils.js +63 -0
  138. package/dist/esm/agent/config.js +637 -0
  139. package/dist/esm/agent/dependency_graph.js +224 -0
  140. package/dist/esm/agent/feedback.js +253 -0
  141. package/dist/esm/agent/flags.js +160 -0
  142. package/dist/esm/agent/flow_catalog.js +103 -0
  143. package/dist/esm/agent/flow_mapping.js +81 -0
  144. package/dist/esm/agent/framework.js +145 -0
  145. package/dist/esm/agent/gap_suggestions.js +98 -0
  146. package/dist/esm/agent/generator.js +112 -0
  147. package/dist/esm/agent/git.js +87 -0
  148. package/dist/esm/agent/handoff.js +177 -0
  149. package/dist/esm/agent/impact-analyzer.js +548 -0
  150. package/dist/esm/agent/index.js +22 -0
  151. package/dist/esm/agent/model-router.js +150 -0
  152. package/dist/esm/agent/operational_insights.js +123 -0
  153. package/dist/esm/agent/pipeline.js +605 -0
  154. package/dist/esm/agent/plan.js +324 -0
  155. package/dist/esm/agent/playwright_report.js +123 -0
  156. package/dist/esm/agent/report-generator.js +247 -0
  157. package/dist/esm/agent/report.js +144 -0
  158. package/dist/esm/agent/runner.js +572 -0
  159. package/dist/esm/agent/selectors.js +71 -0
  160. package/dist/esm/agent/spec-bridge.js +267 -0
  161. package/dist/esm/agent/spec-builder.js +267 -0
  162. package/dist/esm/agent/subsystem_risk.js +204 -0
  163. package/dist/esm/agent/telemetry.js +216 -0
  164. package/dist/esm/agent/test_path.js +20 -0
  165. package/dist/esm/agent/tests.js +101 -0
  166. package/dist/esm/agent/traceability.js +180 -0
  167. package/dist/esm/agent/traceability_capture.js +310 -0
  168. package/dist/esm/agent/traceability_ingest.js +234 -0
  169. package/dist/esm/agent/utils.js +138 -0
  170. package/dist/esm/agent/validators/selector-validator.js +160 -0
  171. package/dist/esm/anthropic_provider.js +324 -0
  172. package/dist/esm/api.js +105 -0
  173. package/dist/esm/base_provider.js +77 -0
  174. package/dist/esm/cli.js +841 -0
  175. package/dist/esm/custom_provider.js +272 -0
  176. package/dist/esm/e2e-test-gen/index.js +50 -0
  177. package/dist/esm/e2e-test-gen/spec_parser.js +782 -0
  178. package/dist/esm/e2e-test-gen/types.js +3 -0
  179. package/dist/esm/index.js +16 -0
  180. package/dist/esm/logger.js +89 -0
  181. package/dist/esm/mcp-server.js +465 -0
  182. package/dist/esm/ollama_provider.js +300 -0
  183. package/dist/esm/openai_provider.js +242 -0
  184. package/dist/esm/package.json +3 -0
  185. package/dist/esm/plan-and-test-constants.js +126 -0
  186. package/dist/esm/provider_factory.js +336 -0
  187. package/dist/esm/provider_interface.js +23 -0
  188. package/dist/esm/provider_utils.js +96 -0
  189. package/dist/index.d.ts +31 -0
  190. package/dist/index.d.ts.map +1 -0
  191. package/dist/index.js +41 -0
  192. package/dist/logger.d.ts +23 -0
  193. package/dist/logger.d.ts.map +1 -0
  194. package/dist/logger.js +93 -0
  195. package/dist/mcp-server.d.ts +35 -0
  196. package/dist/mcp-server.d.ts.map +1 -0
  197. package/dist/mcp-server.js +469 -0
  198. package/dist/ollama_provider.d.ts +65 -0
  199. package/dist/ollama_provider.d.ts.map +1 -0
  200. package/dist/ollama_provider.js +308 -0
  201. package/dist/openai_provider.d.ts +23 -0
  202. package/dist/openai_provider.d.ts.map +1 -0
  203. package/dist/openai_provider.js +250 -0
  204. package/dist/plan-and-test-constants.d.ts +110 -0
  205. package/dist/plan-and-test-constants.d.ts.map +1 -0
  206. package/dist/plan-and-test-constants.js +132 -0
  207. package/dist/provider_factory.d.ts +99 -0
  208. package/dist/provider_factory.d.ts.map +1 -0
  209. package/dist/provider_factory.js +341 -0
  210. package/dist/provider_interface.d.ts +358 -0
  211. package/dist/provider_interface.d.ts.map +1 -0
  212. package/dist/provider_interface.js +28 -0
  213. package/dist/provider_utils.d.ts +39 -0
  214. package/dist/provider_utils.d.ts.map +1 -0
  215. package/dist/provider_utils.js +103 -0
  216. package/package.json +101 -0
  217. package/schemas/gap.schema.json +18 -0
  218. package/schemas/impact.schema.json +418 -0
  219. package/schemas/plan.schema.json +285 -0
  220. package/schemas/subsystem-risk-map.schema.json +62 -0
  221. package/schemas/traceability-input.schema.json +122 -0
@@ -0,0 +1,247 @@
1
+ /**
2
+ * Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
3
+ * See LICENSE.txt for license information.
4
+ *
5
+ * Report Generation Engine
6
+ *
7
+ * Generates console, markdown, and JSON reports from impact analysis results.
8
+ */
9
+ import { writeFileSync, mkdirSync } from 'fs';
10
+ import { join } from 'path';
11
+ /**
12
+ * Generate all requested report formats
13
+ */
14
+ export async function generateReports(analysis, options = {}) {
15
+ const { console: consoleReport = true, markdown = true, json = true, outputDir = './.e2e-ai-agents/reports', } = options;
16
+ if (consoleReport) {
17
+ printConsoleReport(analysis);
18
+ }
19
+ if (markdown || json) {
20
+ mkdirSync(outputDir, { recursive: true });
21
+ }
22
+ if (markdown) {
23
+ const mdPath = join(outputDir, `impact-${Date.now()}.md`);
24
+ const markdownReport = generateMarkdownReport(analysis);
25
+ writeFileSync(mdPath, markdownReport);
26
+ console.log(`\n📄 Markdown report: ${mdPath}`);
27
+ }
28
+ if (json) {
29
+ const jsonPath = join(outputDir, `impact-${Date.now()}.json`);
30
+ writeFileSync(jsonPath, JSON.stringify(analysis, null, 2));
31
+ console.log(`📊 JSON report: ${jsonPath}`);
32
+ }
33
+ }
34
+ // =============================================================================
35
+ // CONSOLE REPORT
36
+ // =============================================================================
37
+ function printConsoleReport(analysis) {
38
+ console.log('\n═══════════════════════════════════════════════════════════════════');
39
+ console.log('📊 CODE IMPACT ANALYSIS REPORT');
40
+ console.log('═══════════════════════════════════════════════════════════════════\n');
41
+ // Summary
42
+ console.log('📈 SUMMARY');
43
+ console.log(` Changed files: ${analysis.totalChanges}`);
44
+ console.log(` Affected flows: ${analysis.affectedFlows.length}`);
45
+ console.log(` Priority breakdown: P0=${analysis.priorityBreakdown.p0}, P1=${analysis.priorityBreakdown.p1}, P2=${analysis.priorityBreakdown.p2}`);
46
+ console.log(` Test coverage: ${analysis.testCoverage.covered}/${analysis.testCoverage.total} flows have tests (${analysis.testCoverage.gaps} gaps)\n`);
47
+ // P0 Flows (Critical)
48
+ const p0Flows = analysis.affectedFlows.filter((f) => f.flow.priority === 'P0');
49
+ if (p0Flows.length > 0) {
50
+ console.log('🔴 CRITICAL (P0) FLOWS AFFECTED:');
51
+ p0Flows.forEach((impact) => {
52
+ console.log(` • ${impact.flow.name} (${impact.flow.id})`);
53
+ console.log(` Confidence: ${impact.confidence}% | Match: ${impact.matchType}`);
54
+ console.log(` Existing tests: ${impact.existingTests.length} ${impact.existingTests.length > 0 ? '✓' : '✗'}`);
55
+ if (impact.testGaps.length > 0) {
56
+ console.log(` ⚠️ Test gaps: ${impact.testGaps.slice(0, 2).join(', ')}${impact.testGaps.length > 2 ? '...' : ''}`);
57
+ }
58
+ console.log(` Affected files: ${impact.affectedFiles.length}`);
59
+ });
60
+ console.log('');
61
+ }
62
+ // P1 Flows (High Priority)
63
+ const p1Flows = analysis.affectedFlows.filter((f) => f.flow.priority === 'P1');
64
+ if (p1Flows.length > 0) {
65
+ console.log('🟡 HIGH PRIORITY (P1) FLOWS AFFECTED:');
66
+ p1Flows.slice(0, 5).forEach((impact) => {
67
+ console.log(` • ${impact.flow.name} (${impact.flow.id})`);
68
+ console.log(` Tests: ${impact.existingTests.length}/${impact.affectedFiles.length} files, Gaps: ${impact.testGaps.length}`);
69
+ });
70
+ if (p1Flows.length > 5) {
71
+ console.log(` ... and ${p1Flows.length - 5} more P1 flows`);
72
+ }
73
+ console.log('');
74
+ }
75
+ // Recommendations
76
+ if (analysis.recommendations.length > 0) {
77
+ console.log('💡 RECOMMENDATIONS:');
78
+ analysis.recommendations.forEach((rec, i) => {
79
+ console.log(` ${i + 1}. ${rec}`);
80
+ });
81
+ console.log('');
82
+ }
83
+ // Action Items
84
+ console.log('🎯 SUGGESTED ACTIONS:');
85
+ if (analysis.hasP0Impact) {
86
+ console.log(' 1. Run P0 flow tests immediately:');
87
+ const p0Tests = p0Flows.flatMap((f) => f.existingTests).filter((t) => t);
88
+ if (p0Tests.length > 0) {
89
+ console.log(` npx playwright test ${p0Tests.slice(0, 3).join(' ')}`);
90
+ if (p0Tests.length > 3) {
91
+ console.log(` ... and ${p0Tests.length - 3} more P0 tests`);
92
+ }
93
+ }
94
+ else {
95
+ console.log(' ⚠️ No existing P0 tests found - generate tests first:');
96
+ console.log(' npx e2e-ai-agents approve-and-generate --path <app-root> --tests-root <tests-root> --pipeline --pipeline-scenarios 3');
97
+ }
98
+ }
99
+ if (analysis.testCoverage.gaps > 0) {
100
+ console.log(` 2. Address ${analysis.testCoverage.gaps} test coverage gap(s):`);
101
+ const gapFlows = analysis.affectedFlows.filter((f) => f.testGaps.length > 0).slice(0, 2);
102
+ gapFlows.forEach(() => {
103
+ console.log(' npx e2e-ai-agents approve-and-generate --path <app-root> --tests-root <tests-root> --pipeline --pipeline-scenarios 3');
104
+ });
105
+ if (analysis.testCoverage.gaps > 2) {
106
+ console.log(` # ... and ${analysis.testCoverage.gaps - 2} more flows with gaps`);
107
+ }
108
+ }
109
+ console.log('\n═══════════════════════════════════════════════════════════════════\n');
110
+ }
111
+ // =============================================================================
112
+ // MARKDOWN REPORT
113
+ // =============================================================================
114
+ function generateMarkdownReport(analysis) {
115
+ const lines = [];
116
+ lines.push('# Code Impact Analysis Report');
117
+ lines.push('');
118
+ lines.push(`**Generated**: ${new Date(analysis.timestamp).toLocaleString()}`);
119
+ lines.push(`**Git Reference**: ${analysis.gitRef}`);
120
+ lines.push('');
121
+ // Risk Level Badge
122
+ const riskLevel = analysis.hasP0Impact
123
+ ? '🔴 **HIGH** (P0 flows affected)'
124
+ : analysis.priorityBreakdown.p1 > 0
125
+ ? '🟡 **MEDIUM** (P1 flows affected)'
126
+ : '🟢 **LOW** (Only P2 flows affected)';
127
+ // Executive Summary
128
+ lines.push('## Executive Summary');
129
+ lines.push('');
130
+ lines.push(`- **Changed files**: ${analysis.totalChanges}`);
131
+ lines.push(`- **Affected flows**: ${analysis.affectedFlows.length}`);
132
+ lines.push(`- **Priority**: P0=${analysis.priorityBreakdown.p0}, P1=${analysis.priorityBreakdown.p1}, P2=${analysis.priorityBreakdown.p2}`);
133
+ lines.push(`- **Test coverage**: ${analysis.testCoverage.covered}/${analysis.testCoverage.total} flows have tests (${analysis.testCoverage.gaps} gaps)`);
134
+ lines.push(`- **Risk level**: ${riskLevel}`);
135
+ lines.push('');
136
+ // Critical Flows
137
+ const p0Flows = analysis.affectedFlows.filter((f) => f.flow.priority === 'P0');
138
+ if (p0Flows.length > 0) {
139
+ lines.push('## 🔴 Critical (P0) Flows Affected');
140
+ lines.push('');
141
+ p0Flows.forEach((impact) => {
142
+ lines.push(`### ${impact.flow.name} (\`${impact.flow.id}\`)`);
143
+ lines.push('');
144
+ lines.push(`**Confidence**: ${impact.confidence}%`);
145
+ lines.push(`**Match type**: ${impact.matchType}`);
146
+ lines.push(`**Affected files**: ${impact.affectedFiles.length} file${impact.affectedFiles.length !== 1 ? 's' : ''}`);
147
+ lines.push('');
148
+ lines.push(`**Existing tests**: ${impact.existingTests.length > 0 ? '✓ ' + impact.existingTests.length : '✗ None'}`);
149
+ if (impact.existingTests.length > 0) {
150
+ lines.push('```');
151
+ impact.existingTests.slice(0, 3).forEach((test) => {
152
+ lines.push(test);
153
+ });
154
+ if (impact.existingTests.length > 3) {
155
+ lines.push(`... and ${impact.existingTests.length - 3} more`);
156
+ }
157
+ lines.push('```');
158
+ }
159
+ if (impact.testGaps.length > 0) {
160
+ lines.push('');
161
+ lines.push(`**Test gaps**:`);
162
+ impact.testGaps.slice(0, 3).forEach((gap) => {
163
+ lines.push(`- ${gap}`);
164
+ });
165
+ if (impact.testGaps.length > 3) {
166
+ lines.push(`- ... and ${impact.testGaps.length - 3} more`);
167
+ }
168
+ }
169
+ lines.push('');
170
+ });
171
+ }
172
+ // High Priority Flows
173
+ const p1Flows = analysis.affectedFlows.filter((f) => f.flow.priority === 'P1');
174
+ if (p1Flows.length > 0) {
175
+ lines.push('## 🟡 High Priority (P1) Flows Affected');
176
+ lines.push('');
177
+ lines.push('| Flow | ID | Files | Tests | Gaps |');
178
+ lines.push('|------|----| ------|-------|------|');
179
+ p1Flows.slice(0, 10).forEach((impact) => {
180
+ lines.push(`| ${impact.flow.name} | \`${impact.flow.id}\` | ${impact.affectedFiles.length} | ${impact.existingTests.length} | ${impact.testGaps.length} |`);
181
+ });
182
+ if (p1Flows.length > 10) {
183
+ lines.push(`| ... ${p1Flows.length - 10} more | ... | ... | ... | ... |`);
184
+ }
185
+ lines.push('');
186
+ }
187
+ // Recommendations
188
+ if (analysis.recommendations.length > 0) {
189
+ lines.push('## 💡 Recommendations');
190
+ lines.push('');
191
+ analysis.recommendations.forEach((rec, i) => {
192
+ lines.push(`${i + 1}. ${rec}`);
193
+ });
194
+ lines.push('');
195
+ }
196
+ // Action Items
197
+ lines.push('## 🎯 Action Items');
198
+ lines.push('');
199
+ if (analysis.hasP0Impact) {
200
+ lines.push('### Immediate: Run P0 Tests');
201
+ lines.push('');
202
+ const p0Tests = p0Flows.flatMap((f) => f.existingTests).filter((t) => t);
203
+ if (p0Tests.length > 0) {
204
+ lines.push('```bash');
205
+ lines.push(`npx playwright test ${p0Tests.slice(0, 3).join(' ')}`);
206
+ if (p0Tests.length > 3) {
207
+ lines.push(`# ... and ${p0Tests.length - 3} more P0 tests`);
208
+ }
209
+ lines.push('```');
210
+ }
211
+ else {
212
+ lines.push('No existing P0 tests found. Generate tests:');
213
+ lines.push('');
214
+ lines.push('```bash');
215
+ lines.push('npx e2e-ai-agents approve-and-generate --path <app-root> --tests-root <tests-root> --pipeline --pipeline-scenarios 3');
216
+ lines.push('```');
217
+ }
218
+ lines.push('');
219
+ }
220
+ if (analysis.testCoverage.gaps > 0) {
221
+ lines.push('### Generate Missing Tests');
222
+ lines.push('');
223
+ const gapFlows = analysis.affectedFlows.filter((f) => f.testGaps.length > 0).slice(0, 3);
224
+ if (gapFlows.length > 0) {
225
+ lines.push('```bash');
226
+ gapFlows.forEach(() => {
227
+ lines.push('npx e2e-ai-agents approve-and-generate --path <app-root> --tests-root <tests-root> --pipeline --pipeline-scenarios 3');
228
+ });
229
+ if (analysis.testCoverage.gaps > 3) {
230
+ lines.push(`# ... and ${analysis.testCoverage.gaps - 3} more gaps to address`);
231
+ }
232
+ lines.push('```');
233
+ }
234
+ }
235
+ lines.push('');
236
+ lines.push('---');
237
+ lines.push('');
238
+ lines.push('_This report was generated by the E2E Impact Analysis Agent. To regenerate, run: `npx e2e-ai-agents impact --path <app-root> --tests-root <tests-root>`_');
239
+ return lines.join('\n');
240
+ }
241
+ // =============================================================================
242
+ // JSON REPORT
243
+ // =============================================================================
244
+ /**
245
+ * JSON report is generated directly in generateReports()
246
+ * No additional formatting needed
247
+ */
@@ -0,0 +1,144 @@
1
+ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
2
+ // See LICENSE.txt for license information.
3
+ import { mkdirSync, writeFileSync } from 'fs';
4
+ import { join } from 'path';
5
+ import { formatFlags } from './flags.js';
6
+ function formatFlow(flow) {
7
+ const reasonText = flow.reasons.length > 0 ? flow.reasons.join('; ') : 'No specific reasons';
8
+ const audienceText = flow.audience && flow.audience.length > 0 ? `\n Audience: ${flow.audience.join(', ')}` : '';
9
+ const flagsText = flow.flags && flow.flags.length > 0 ? `\n Flags: ${formatFlags(flow.flags)}` : '';
10
+ const blastText = flow.blastRadius ? `\n Blast radius: ${flow.blastRadius.summary}` : '';
11
+ return `- [${flow.priority}] ${flow.name} (${flow.id})\n Score: ${flow.score}\n Reasons: ${reasonText}\n Files: ${flow.files.join(', ')}${audienceText}${flagsText}${blastText}`;
12
+ }
13
+ function formatGap(flow) {
14
+ return `- [${flow.priority}] ${flow.name} (${flow.id})`;
15
+ }
16
+ function formatSuggestion(suggestion) {
17
+ return `- ${suggestion.file}:${suggestion.line} -> ${suggestion.testId}\n ${suggestion.snippet}`;
18
+ }
19
+ function formatTestSuggestion(suggestion) {
20
+ const source = suggestion.sourceFiles.length > 0 ? suggestion.sourceFiles.join(', ') : 'N/A';
21
+ return `- [${suggestion.priority}] ${suggestion.flowName} (${suggestion.flowId})\n Path: ${suggestion.suggestedTestPath}\n Source files: ${source}\n Why: ${suggestion.rationale}`;
22
+ }
23
+ function flowCounts(flows) {
24
+ return flows.reduce((acc, flow) => {
25
+ if (flow.priority === 'P0')
26
+ acc.p0 += 1;
27
+ else if (flow.priority === 'P1')
28
+ acc.p1 += 1;
29
+ else
30
+ acc.p2 += 1;
31
+ return acc;
32
+ }, { p0: 0, p1: 0, p2: 0 });
33
+ }
34
+ export function writeReport(appRoot, config, data) {
35
+ const specsDir = join(appRoot, config.artifacts.specsDir);
36
+ const baseDir = join(appRoot, '.e2e-ai-agents');
37
+ if (config.artifacts.mode !== 'none') {
38
+ mkdirSync(specsDir, { recursive: true });
39
+ }
40
+ mkdirSync(baseDir, { recursive: true });
41
+ const counts = flowCounts(data.flows);
42
+ const markdownLines = [];
43
+ markdownLines.push(`# ${data.mode === 'impact' ? 'Impact Analysis' : 'Gap Analysis'} Report`);
44
+ markdownLines.push('');
45
+ markdownLines.push(`Framework: ${data.framework}`);
46
+ markdownLines.push(`Test Patterns: ${data.testPatterns.join(', ') || 'None'}`);
47
+ if (data.flowCatalog) {
48
+ markdownLines.push(`Flow Catalog: ${data.flowCatalog}`);
49
+ }
50
+ if (data.impactModel) {
51
+ markdownLines.push(`Impact Model: flow=${data.impactModel.flowMapping} test=${data.impactModel.testMapping} confidence=${data.impactModel.confidenceClass}`);
52
+ if (data.impactModel.traceability) {
53
+ const traceability = data.impactModel.traceability;
54
+ markdownLines.push(`Traceability: enabled=${traceability.enabled} manifestFound=${traceability.manifestFound} matchedFlows=${traceability.matchedFlows}/${traceability.totalFlows} matchedTests=${traceability.matchedTests} coverageRatio=${traceability.coverageRatio}`);
55
+ }
56
+ if (data.impactModel.dependencyGraph) {
57
+ const graph = data.impactModel.dependencyGraph;
58
+ markdownLines.push(`Dependency Graph: enabled=${graph.enabled} seeds=${graph.seedFiles} expanded=${graph.expandedFiles} files=${graph.analyzedFiles} edges=${graph.analyzedEdges} depth=${graph.maxDepth}${graph.truncated ? ' (truncated)' : ''}`);
59
+ }
60
+ if (data.impactModel.subsystemRisk) {
61
+ const subsystemRisk = data.impactModel.subsystemRisk;
62
+ markdownLines.push(`Subsystem Risk: enabled=${subsystemRisk.enabled} mapFound=${subsystemRisk.mapFound} rules=${subsystemRisk.rulesLoaded} filesMatched=${subsystemRisk.filesMatched} ruleMatches=${subsystemRisk.ruleMatches} boostedFlows=${subsystemRisk.boostedFlows}`);
63
+ }
64
+ }
65
+ markdownLines.push(`Changed Files: ${data.changedFiles.length}`);
66
+ markdownLines.push(`Flows: P0=${counts.p0} P1=${counts.p1} P2=${counts.p2}`);
67
+ if (data.specPDF) {
68
+ markdownLines.push(`Spec PDF: ${data.specPDF}`);
69
+ }
70
+ if (data.warnings.length > 0) {
71
+ markdownLines.push('');
72
+ markdownLines.push('Warnings:');
73
+ markdownLines.push(...data.warnings.map((warning) => `- ${warning}`));
74
+ }
75
+ if (data.flows.length > 0) {
76
+ markdownLines.push('');
77
+ markdownLines.push('Impacted Flows:');
78
+ markdownLines.push(...data.flows.map(formatFlow));
79
+ }
80
+ if (data.gaps.length > 0) {
81
+ markdownLines.push('');
82
+ markdownLines.push('Coverage Gaps (P0/P1 without tests):');
83
+ markdownLines.push(...data.gaps.map(formatGap));
84
+ }
85
+ if (data.recommendedTests && data.recommendedTests.length > 0) {
86
+ markdownLines.push('');
87
+ markdownLines.push('Recommended Tests to Run:');
88
+ markdownLines.push(...data.recommendedTests.map((test) => `- ${test}`));
89
+ }
90
+ if (data.pipeline) {
91
+ markdownLines.push('');
92
+ markdownLines.push('Pipeline Results:');
93
+ markdownLines.push(`- Runner: ${data.pipeline.runner}`);
94
+ for (const result of data.pipeline.results) {
95
+ const status = result.healStatus ? `${result.generateStatus}/${result.healStatus}` : result.generateStatus;
96
+ markdownLines.push(`- ${result.flowId} (${result.flowName}): ${status} -> ${result.generatedDir}`);
97
+ if (result.error) {
98
+ markdownLines.push(` Error: ${result.error}`);
99
+ }
100
+ }
101
+ if (data.pipeline.warnings.length > 0) {
102
+ markdownLines.push('Pipeline warnings:');
103
+ markdownLines.push(...data.pipeline.warnings.map((warning) => `- ${warning}`));
104
+ }
105
+ }
106
+ if (data.dataTestIds.length > 0) {
107
+ markdownLines.push('');
108
+ markdownLines.push('data-testid Suggestions:');
109
+ markdownLines.push(...data.dataTestIds.map(formatSuggestion));
110
+ }
111
+ if (data.testSuggestions && data.testSuggestions.length > 0) {
112
+ markdownLines.push('');
113
+ markdownLines.push('Suggested New Tests (Actionable):');
114
+ markdownLines.push(...data.testSuggestions.map(formatTestSuggestion));
115
+ }
116
+ if (data.applied) {
117
+ markdownLines.push('');
118
+ markdownLines.push('Applied Changes:');
119
+ if (data.applied.patchedFiles.length > 0) {
120
+ markdownLines.push(`- Patched files: ${data.applied.patchedFiles.join(', ')}`);
121
+ }
122
+ if (data.applied.generatedTests.length > 0) {
123
+ markdownLines.push(`- Generated tests: ${data.applied.generatedTests.join(', ')}`);
124
+ }
125
+ if (data.applied.skippedTests.length > 0) {
126
+ markdownLines.push(`- Skipped test files: ${data.applied.skippedTests.join(', ')}`);
127
+ }
128
+ }
129
+ const markdownContent = markdownLines.join('\n');
130
+ const reportName = data.mode === 'impact' ? 'impact-plan.md' : 'gap-report.md';
131
+ const markdownPath = join(specsDir, reportName);
132
+ if (config.artifacts.mode !== 'none') {
133
+ writeFileSync(markdownPath, markdownContent, 'utf-8');
134
+ }
135
+ const jsonPath = join(baseDir, data.mode === 'impact' ? 'impact.json' : 'gap.json');
136
+ const jsonData = data.mode === 'gap'
137
+ ? {
138
+ ...data,
139
+ suggestedNewTests: data.suggestedNewTests || data.testSuggestions || [],
140
+ }
141
+ : data;
142
+ writeFileSync(jsonPath, JSON.stringify(jsonData, null, 2), 'utf-8');
143
+ return { markdownPath, jsonPath };
144
+ }