@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,220 @@
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.TelemetryCollector = void 0;
6
+ /**
7
+ * Telemetry Collection System (Phase A2)
8
+ *
9
+ * Tracks costs, performance, and success metrics for test generation operations.
10
+ * Provides visibility into:
11
+ * - Cost per operation (input/output tokens * model rate)
12
+ * - Model usage breakdown (Haiku, Sonnet, Opus)
13
+ * - Success rate by operation
14
+ * - Performance metrics (duration, tokens used)
15
+ *
16
+ * Data stored in: `.e2e-ai-agents/metrics/YYYY-MM-DD.json`
17
+ */
18
+ const fs_1 = require("fs");
19
+ const path_1 = require("path");
20
+ const crypto_1 = require("crypto");
21
+ const MODEL_RATES = {
22
+ 'claude-haiku-4-0-20250430': 0.25 / 1000000, // per input token
23
+ 'claude-sonnet-4-5-20250929': 3 / 1000000,
24
+ 'claude-opus-4-6-20250820': 15 / 1000000,
25
+ };
26
+ class TelemetryCollector {
27
+ constructor(metricsDir = '.e2e-ai-agents/metrics') {
28
+ this.metrics = new Map();
29
+ this.metricsDir = metricsDir;
30
+ this.ensureMetricsDir();
31
+ this.loadTodayMetrics();
32
+ }
33
+ /**
34
+ * Ensure metrics directory exists
35
+ */
36
+ ensureMetricsDir() {
37
+ if (!(0, fs_1.existsSync)(this.metricsDir)) {
38
+ (0, fs_1.mkdirSync)(this.metricsDir, { recursive: true });
39
+ }
40
+ }
41
+ /**
42
+ * Get today's metrics file path
43
+ */
44
+ getTodayPath() {
45
+ const now = new Date();
46
+ const date = now.toISOString().split('T')[0]; // YYYY-MM-DD
47
+ return (0, path_1.join)(this.metricsDir, `${date}.json`);
48
+ }
49
+ /**
50
+ * Load metrics from disk
51
+ */
52
+ loadTodayMetrics() {
53
+ const path = this.getTodayPath();
54
+ if ((0, fs_1.existsSync)(path)) {
55
+ try {
56
+ const data = JSON.parse((0, fs_1.readFileSync)(path, 'utf-8'));
57
+ this.metrics.set(path, data);
58
+ }
59
+ catch (error) {
60
+ console.error(`Failed to load metrics from ${path}:`, error);
61
+ }
62
+ }
63
+ }
64
+ /**
65
+ * Track a metric
66
+ */
67
+ track(metric) {
68
+ const fullMetric = {
69
+ id: (0, crypto_1.randomUUID)().substring(0, 8),
70
+ ...metric,
71
+ };
72
+ const path = this.getTodayPath();
73
+ const metrics = this.metrics.get(path) || [];
74
+ metrics.push(fullMetric);
75
+ this.metrics.set(path, metrics);
76
+ // Persist to disk
77
+ this.saveMetrics();
78
+ }
79
+ /**
80
+ * Save metrics to disk
81
+ */
82
+ saveMetrics() {
83
+ this.metrics.forEach((metrics, path) => {
84
+ (0, fs_1.writeFileSync)(path, JSON.stringify(metrics, null, 2), 'utf-8');
85
+ });
86
+ }
87
+ /**
88
+ * Calculate cost for a metric
89
+ */
90
+ static calculateCost(model, tokensInput, tokensOutput) {
91
+ const inputRate = MODEL_RATES[model] || 0.003 / 1000000; // Default estimate
92
+ const outputRate = inputRate * 3; // Output usually 3x input cost
93
+ return tokensInput * inputRate + tokensOutput * outputRate;
94
+ }
95
+ /**
96
+ * Generate report for a date range
97
+ */
98
+ generateReport(since, until) {
99
+ const start = since || new Date(new Date().setDate(new Date().getDate() - 7)); // Default: last 7 days
100
+ const end = until || new Date();
101
+ // Collect all metrics in date range
102
+ const allMetrics = [];
103
+ this.metrics.forEach((metrics) => {
104
+ metrics.forEach((m) => {
105
+ const metricDate = new Date(m.timestamp);
106
+ if (metricDate >= start && metricDate <= end) {
107
+ allMetrics.push(m);
108
+ }
109
+ });
110
+ });
111
+ // Calculate summary
112
+ const successCount = allMetrics.filter((m) => m.success).length;
113
+ const failureCount = allMetrics.length - successCount;
114
+ const totalCost = allMetrics.reduce((sum, m) => sum + m.costUsd, 0);
115
+ const avgCost = allMetrics.length > 0 ? totalCost / allMetrics.length : 0;
116
+ const totalTokens = allMetrics.reduce((sum, m) => sum + (m.tokensInput + m.tokensOutput), 0);
117
+ const avgDuration = allMetrics.length > 0 ? allMetrics.reduce((sum, m) => sum + m.durationMs, 0) / allMetrics.length / 1000 : 0;
118
+ // By model
119
+ const byModel = {};
120
+ allMetrics.forEach((m) => {
121
+ if (!byModel[m.model]) {
122
+ byModel[m.model] = { count: 0, totalCost: 0, successCount: 0 };
123
+ }
124
+ byModel[m.model].count += 1;
125
+ byModel[m.model].totalCost += m.costUsd;
126
+ if (m.success)
127
+ byModel[m.model].successCount += 1;
128
+ });
129
+ Object.keys(byModel).forEach((model) => {
130
+ const data = byModel[model];
131
+ byModel[model] = {
132
+ count: data.count,
133
+ totalCost: data.totalCost,
134
+ avgCost: data.totalCost / data.count,
135
+ successRate: (data.successCount / data.count) * 100,
136
+ };
137
+ });
138
+ // By operation
139
+ const byOperation = {};
140
+ allMetrics.forEach((m) => {
141
+ if (!byOperation[m.operation]) {
142
+ byOperation[m.operation] = { count: 0, totalCost: 0, totalDuration: 0, successCount: 0 };
143
+ }
144
+ byOperation[m.operation].count += 1;
145
+ byOperation[m.operation].totalCost += m.costUsd;
146
+ byOperation[m.operation].totalDuration += m.durationMs;
147
+ if (m.success)
148
+ byOperation[m.operation].successCount += 1;
149
+ });
150
+ Object.keys(byOperation).forEach((op) => {
151
+ const data = byOperation[op];
152
+ byOperation[op] = {
153
+ count: data.count,
154
+ totalCost: data.totalCost,
155
+ avgDuration: data.totalDuration / data.count / 1000,
156
+ successRate: (data.successCount / data.count) * 100,
157
+ };
158
+ });
159
+ return {
160
+ period: {
161
+ start: start.toISOString().split('T')[0],
162
+ end: end.toISOString().split('T')[0],
163
+ },
164
+ summary: {
165
+ totalOperations: allMetrics.length,
166
+ successCount,
167
+ failureCount,
168
+ successRate: allMetrics.length > 0 ? (successCount / allMetrics.length) * 100 : 0,
169
+ totalCost,
170
+ avgCost,
171
+ totalTokens,
172
+ avgDuration,
173
+ },
174
+ byModel,
175
+ byOperation,
176
+ };
177
+ }
178
+ /**
179
+ * Format report for console output
180
+ */
181
+ static formatReport(report) {
182
+ const lines = [
183
+ '',
184
+ '📊 Test Generation Metrics',
185
+ `Period: ${report.period.start} to ${report.period.end}`,
186
+ '═'.repeat(50),
187
+ '',
188
+ `Total Operations: ${report.summary.totalOperations}`,
189
+ `Success Rate: ${report.summary.successRate.toFixed(1)}% (${report.summary.successCount}/${report.summary.totalOperations})`,
190
+ `Total Cost: $${report.summary.totalCost.toFixed(2)}`,
191
+ `Avg Cost/Op: $${report.summary.avgCost.toFixed(4)}`,
192
+ `Avg Duration: ${report.summary.avgDuration.toFixed(1)}s`,
193
+ `Total Tokens: ${report.summary.totalTokens.toLocaleString()}`,
194
+ '',
195
+ 'Model Usage:',
196
+ ];
197
+ Object.entries(report.byModel).forEach(([model, data]) => {
198
+ const shortName = model.includes('haiku') ? 'Haiku' : model.includes('sonnet') ? 'Sonnet' : 'Opus';
199
+ lines.push(` ${shortName}: ${data.count} ops - $${data.totalCost.toFixed(2)} (avg $${data.avgCost.toFixed(4)}, ${data.successRate.toFixed(0)}% success)`);
200
+ });
201
+ lines.push('', 'By Operation:');
202
+ Object.entries(report.byOperation).forEach(([op, data]) => {
203
+ lines.push(` ${op}: ${data.count} ops - $${data.totalCost.toFixed(2)} (${data.avgDuration.toFixed(1)}s avg, ${data.successRate.toFixed(0)}% success)`);
204
+ });
205
+ lines.push('');
206
+ return lines.join('\n');
207
+ }
208
+ /**
209
+ * Export metrics as JSON
210
+ */
211
+ exportJson(filepath) {
212
+ const allMetrics = [];
213
+ this.metrics.forEach((metrics) => {
214
+ allMetrics.push(...metrics);
215
+ });
216
+ (0, fs_1.writeFileSync)(filepath, JSON.stringify(allMetrics, null, 2), 'utf-8');
217
+ console.log(` ✓ Exported ${allMetrics.length} metrics to ${filepath}`);
218
+ }
219
+ }
220
+ exports.TelemetryCollector = TelemetryCollector;
@@ -0,0 +1,2 @@
1
+ export declare function inferSubsystemFromTestPath(test: string): string;
2
+ //# sourceMappingURL=test_path.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test_path.d.ts","sourceRoot":"","sources":["../../src/agent/test_path.ts"],"names":[],"mappings":"AAOA,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAiB/D"}
@@ -0,0 +1,23 @@
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.inferSubsystemFromTestPath = inferSubsystemFromTestPath;
6
+ function normalizeTestName(test) {
7
+ return test.replace(/ \(flags:.*\)$/, '').trim();
8
+ }
9
+ function inferSubsystemFromTestPath(test) {
10
+ const normalized = normalizeTestName(test).replace(/^\/+/, '');
11
+ const parts = normalized.split('/').filter(Boolean);
12
+ if (parts.length === 0) {
13
+ return 'unknown';
14
+ }
15
+ const specsIdx = parts.findIndex((part) => part === 'specs' || part === 'tests');
16
+ if (specsIdx >= 0 && specsIdx + 1 < parts.length) {
17
+ return parts[specsIdx + 1] || 'unknown';
18
+ }
19
+ if (parts.length >= 2 && (parts[0] === 'e2e-tests' || parts[0] === 'playwright')) {
20
+ return parts[1] || 'unknown';
21
+ }
22
+ return parts[0] || 'unknown';
23
+ }
@@ -0,0 +1,18 @@
1
+ import type { FlowImpact } from './analysis.js';
2
+ export interface TestFile {
3
+ path: string;
4
+ content: string | null;
5
+ }
6
+ export interface FlowCoverage {
7
+ flowId: string;
8
+ flowName: string;
9
+ priority: string;
10
+ coveredBy: string[];
11
+ score: number;
12
+ expectedTests?: string[];
13
+ source?: 'catalog' | 'traceability' | 'heuristic';
14
+ }
15
+ export declare function discoverTests(appRoot: string, patterns: string[]): TestFile[];
16
+ export declare function mapTestsToFlows(flows: FlowImpact[], tests: TestFile[]): FlowCoverage[];
17
+ export declare function mapCatalogTestsToFlows(flows: FlowImpact[], testsRoot: string, testsByFlow: Map<string, string[]>): FlowCoverage[];
18
+ //# sourceMappingURL=tests.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tests.d.ts","sourceRoot":"","sources":["../../src/agent/tests.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,eAAe,CAAC;AAG9C,MAAM,WAAW,QAAQ;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED,MAAM,WAAW,YAAY;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,MAAM,CAAC,EAAE,SAAS,GAAG,cAAc,GAAG,WAAW,CAAC;CACrD;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,QAAQ,EAAE,CAkB7E;AAUD,wBAAgB,eAAe,CAAC,KAAK,EAAE,UAAU,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,YAAY,EAAE,CAgCtF;AAaD,wBAAgB,sBAAsB,CAClC,KAAK,EAAE,UAAU,EAAE,EACnB,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,GACnC,YAAY,EAAE,CA8BhB"}
@@ -0,0 +1,106 @@
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.discoverTests = discoverTests;
6
+ exports.mapTestsToFlows = mapTestsToFlows;
7
+ exports.mapCatalogTestsToFlows = mapCatalogTestsToFlows;
8
+ const fs_1 = require("fs");
9
+ const glob_1 = require("glob");
10
+ const path_1 = require("path");
11
+ const utils_js_1 = require("./utils.js");
12
+ function discoverTests(appRoot, patterns) {
13
+ const files = new Set();
14
+ for (const pattern of patterns) {
15
+ const matches = (0, glob_1.globSync)(pattern, {
16
+ cwd: appRoot,
17
+ ignore: ['**/node_modules/**', '**/.git/**'],
18
+ nodir: true,
19
+ });
20
+ for (const match of matches) {
21
+ files.add((0, utils_js_1.normalizePath)(match));
22
+ }
23
+ }
24
+ return Array.from(files).map((relativePath) => {
25
+ const fullPath = (0, path_1.join)(appRoot, relativePath);
26
+ const content = (0, utils_js_1.safeReadTextFile)(fullPath);
27
+ return { path: relativePath, content };
28
+ });
29
+ }
30
+ function buildFlowKeywords(flow) {
31
+ const tokens = [];
32
+ tokens.push(...(0, utils_js_1.tokenize)(flow.id));
33
+ tokens.push(...(0, utils_js_1.tokenize)(flow.name));
34
+ tokens.push(...flow.keywords);
35
+ return (0, utils_js_1.uniqueTokens)(tokens);
36
+ }
37
+ function mapTestsToFlows(flows, tests) {
38
+ const coverage = [];
39
+ for (const flow of flows) {
40
+ const keywords = buildFlowKeywords(flow);
41
+ const matched = [];
42
+ let score = 0;
43
+ for (const test of tests) {
44
+ const haystack = `${test.path} ${test.content || ''}`.toLowerCase();
45
+ let localScore = 0;
46
+ for (const keyword of keywords) {
47
+ if (keyword && haystack.includes(keyword.toLowerCase())) {
48
+ localScore += 1;
49
+ }
50
+ }
51
+ if (localScore > 0) {
52
+ matched.push(test.path);
53
+ score += localScore;
54
+ }
55
+ }
56
+ coverage.push({
57
+ flowId: flow.id,
58
+ flowName: flow.name,
59
+ priority: flow.priority,
60
+ coveredBy: matched,
61
+ score,
62
+ source: 'heuristic',
63
+ });
64
+ }
65
+ return coverage;
66
+ }
67
+ function resolveExpectedTests(testsRoot, expectedTests) {
68
+ const resolved = [];
69
+ for (const entry of expectedTests) {
70
+ if (!entry) {
71
+ continue;
72
+ }
73
+ resolved.push((0, utils_js_1.normalizePath)(entry));
74
+ }
75
+ return resolved;
76
+ }
77
+ function mapCatalogTestsToFlows(flows, testsRoot, testsByFlow) {
78
+ return flows.map((flow) => {
79
+ const expectedTests = resolveExpectedTests(testsRoot, testsByFlow.get(flow.id) || []);
80
+ const coveredBy = [];
81
+ for (const expected of expectedTests) {
82
+ const isAbsolute = expected.startsWith('/');
83
+ const globTarget = isAbsolute ? expected : expected;
84
+ if (expected.includes('*') || expected.includes('?') || expected.includes('{')) {
85
+ const matches = (0, glob_1.globSync)(globTarget, { cwd: isAbsolute ? undefined : testsRoot, nodir: true });
86
+ if (matches.length > 0) {
87
+ coveredBy.push(...matches.map((match) => (0, utils_js_1.normalizePath)(match)));
88
+ }
89
+ continue;
90
+ }
91
+ const fullPath = isAbsolute ? expected : (0, path_1.join)(testsRoot, expected);
92
+ if ((0, fs_1.existsSync)(fullPath)) {
93
+ coveredBy.push(isAbsolute ? (0, utils_js_1.normalizePath)(expected) : expected);
94
+ }
95
+ }
96
+ return {
97
+ flowId: flow.id,
98
+ flowName: flow.name,
99
+ priority: flow.priority,
100
+ coveredBy: Array.from(new Set(coveredBy)),
101
+ score: coveredBy.length,
102
+ expectedTests,
103
+ source: 'catalog',
104
+ };
105
+ });
106
+ }
@@ -0,0 +1,22 @@
1
+ import type { FlowImpact } from './analysis.js';
2
+ import type { TraceabilityImpactConfig } from './config.js';
3
+ import type { FlowCoverage } from './tests.js';
4
+ export interface TraceabilityStats {
5
+ source: 'manifest';
6
+ enabled: boolean;
7
+ manifestPath: string;
8
+ manifestFound: boolean;
9
+ manifestTests: number;
10
+ manifestEdges: number;
11
+ matchedFlows: number;
12
+ totalFlows: number;
13
+ matchedTests: number;
14
+ coverageRatio: number;
15
+ }
16
+ export interface TraceabilityMappingResult {
17
+ coverage: FlowCoverage[];
18
+ stats: TraceabilityStats;
19
+ warnings: string[];
20
+ }
21
+ export declare function mapTraceabilityToFlows(appRoot: string, config: TraceabilityImpactConfig, flows: FlowImpact[]): TraceabilityMappingResult;
22
+ //# sourceMappingURL=traceability.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"traceability.d.ts","sourceRoot":"","sources":["../../src/agent/traceability.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,eAAe,CAAC;AAC9C,OAAO,KAAK,EAAC,wBAAwB,EAAC,MAAM,aAAa,CAAC;AAC1D,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,YAAY,CAAC;AAoB7C,MAAM,WAAW,iBAAiB;IAC9B,MAAM,EAAE,UAAU,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,OAAO,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,yBAAyB;IACtC,QAAQ,EAAE,YAAY,EAAE,CAAC;IACzB,KAAK,EAAE,iBAAiB,CAAC;IACzB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACtB;AAsFD,wBAAgB,sBAAsB,CAClC,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,wBAAwB,EAChC,KAAK,EAAE,UAAU,EAAE,GACpB,yBAAyB,CAiH3B"}
@@ -0,0 +1,183 @@
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.mapTraceabilityToFlows = mapTraceabilityToFlows;
6
+ const fs_1 = require("fs");
7
+ const path_1 = require("path");
8
+ const utils_js_1 = require("./utils.js");
9
+ function ratio(numerator, denominator) {
10
+ if (denominator <= 0) {
11
+ return 0;
12
+ }
13
+ return Number((numerator / denominator).toFixed(4));
14
+ }
15
+ function resolveManifestPath(root, configuredPath) {
16
+ if ((0, path_1.isAbsolute)(configuredPath)) {
17
+ return configuredPath;
18
+ }
19
+ return (0, path_1.join)(root, configuredPath);
20
+ }
21
+ function safeReadManifest(path) {
22
+ if (!(0, fs_1.existsSync)(path)) {
23
+ return null;
24
+ }
25
+ try {
26
+ return JSON.parse((0, fs_1.readFileSync)(path, 'utf-8'));
27
+ }
28
+ catch {
29
+ return null;
30
+ }
31
+ }
32
+ function normalizeFiles(files) {
33
+ if (!files) {
34
+ return [];
35
+ }
36
+ return files.map((file) => (0, utils_js_1.normalizePath)(file)).filter(Boolean);
37
+ }
38
+ function normalizeTests(tests) {
39
+ if (!tests) {
40
+ return [];
41
+ }
42
+ return tests.map((test) => (0, utils_js_1.normalizePath)(test)).filter(Boolean);
43
+ }
44
+ function buildFileToTestsMap(manifest) {
45
+ const fileToTests = new Map();
46
+ const setMapping = (file, test) => {
47
+ if (!file || !test) {
48
+ return;
49
+ }
50
+ const key = (0, utils_js_1.normalizePath)(file);
51
+ const value = (0, utils_js_1.normalizePath)(test);
52
+ if (!fileToTests.has(key)) {
53
+ fileToTests.set(key, new Set());
54
+ }
55
+ fileToTests.get(key)?.add(value);
56
+ };
57
+ if (manifest.tests) {
58
+ for (const entry of manifest.tests) {
59
+ const files = normalizeFiles(entry.touchedFiles);
60
+ for (const file of files) {
61
+ setMapping(file, entry.test);
62
+ }
63
+ }
64
+ }
65
+ if (manifest.fileToTests) {
66
+ for (const [file, tests] of Object.entries(manifest.fileToTests)) {
67
+ const normalizedTests = normalizeTests(tests);
68
+ for (const test of normalizedTests) {
69
+ setMapping(file, test);
70
+ }
71
+ }
72
+ }
73
+ if (manifest.mappings) {
74
+ for (const entry of manifest.mappings) {
75
+ const normalizedTests = normalizeTests(entry.tests);
76
+ for (const test of normalizedTests) {
77
+ setMapping(entry.file, test);
78
+ }
79
+ }
80
+ }
81
+ return fileToTests;
82
+ }
83
+ function mapTraceabilityToFlows(appRoot, config, flows) {
84
+ const manifestPath = resolveManifestPath(appRoot, config.manifestPath);
85
+ const warnings = [];
86
+ const fallbackStats = {
87
+ source: 'manifest',
88
+ enabled: config.enabled,
89
+ manifestPath,
90
+ manifestFound: false,
91
+ manifestTests: 0,
92
+ manifestEdges: 0,
93
+ matchedFlows: 0,
94
+ totalFlows: flows.length,
95
+ matchedTests: 0,
96
+ coverageRatio: 0,
97
+ };
98
+ if (!config.enabled) {
99
+ return {
100
+ coverage: [],
101
+ warnings,
102
+ stats: fallbackStats,
103
+ };
104
+ }
105
+ const manifest = safeReadManifest(manifestPath);
106
+ if (!manifest) {
107
+ warnings.push(`Traceability manifest not found or invalid: ${manifestPath}`);
108
+ return {
109
+ coverage: [],
110
+ warnings,
111
+ stats: fallbackStats,
112
+ };
113
+ }
114
+ const fileToTests = buildFileToTestsMap(manifest);
115
+ let manifestEdges = 0;
116
+ for (const tests of fileToTests.values()) {
117
+ manifestEdges += tests.size;
118
+ }
119
+ const manifestTests = new Set();
120
+ const coverage = [];
121
+ const matchedTests = new Set();
122
+ let matchedFlows = 0;
123
+ for (const tests of fileToTests.values()) {
124
+ for (const test of tests) {
125
+ manifestTests.add(test);
126
+ }
127
+ }
128
+ for (const flow of flows) {
129
+ const signalCounts = new Map();
130
+ const files = flow.files.map((file) => (0, utils_js_1.normalizePath)(file));
131
+ for (const file of files) {
132
+ const tests = fileToTests.get(file);
133
+ if (!tests) {
134
+ continue;
135
+ }
136
+ for (const test of tests) {
137
+ signalCounts.set(test, (signalCounts.get(test) || 0) + 1);
138
+ }
139
+ }
140
+ const coveredBy = Array.from(signalCounts.entries())
141
+ .filter(([, count]) => count >= Math.max(1, config.minSignalsPerTest))
142
+ .map(([test]) => test)
143
+ .sort();
144
+ const score = Array.from(signalCounts.values()).reduce((acc, value) => acc + value, 0);
145
+ if (coveredBy.length > 0) {
146
+ matchedFlows += 1;
147
+ for (const test of coveredBy) {
148
+ matchedTests.add(test);
149
+ }
150
+ }
151
+ coverage.push({
152
+ flowId: flow.id,
153
+ flowName: flow.name,
154
+ priority: flow.priority,
155
+ coveredBy,
156
+ score,
157
+ source: 'traceability',
158
+ });
159
+ }
160
+ const stats = {
161
+ source: 'manifest',
162
+ enabled: config.enabled,
163
+ manifestPath,
164
+ manifestFound: true,
165
+ manifestTests: manifestTests.size,
166
+ manifestEdges,
167
+ matchedFlows,
168
+ totalFlows: flows.length,
169
+ matchedTests: matchedTests.size,
170
+ coverageRatio: ratio(matchedFlows, flows.length),
171
+ };
172
+ if (manifestEdges === 0) {
173
+ warnings.push(`Traceability manifest has no file-to-test mappings: ${manifestPath}`);
174
+ }
175
+ else if (stats.coverageRatio < 0.4) {
176
+ warnings.push(`Traceability coverage is low (${stats.matchedFlows}/${stats.totalFlows} flows mapped). Recommendations may require heuristic fallback.`);
177
+ }
178
+ return {
179
+ coverage,
180
+ warnings,
181
+ stats,
182
+ };
183
+ }
@@ -0,0 +1,18 @@
1
+ export interface TraceabilityCaptureOptions {
2
+ appPath: string;
3
+ testsRoot: string;
4
+ reportPath: string;
5
+ sinceRef: string;
6
+ outputPath?: string;
7
+ coverageMapPath?: string;
8
+ changedFilesPath?: string;
9
+ }
10
+ export interface TraceabilityCaptureResult {
11
+ outputPath: string;
12
+ testsSeen: number;
13
+ runsGenerated: number;
14
+ changedFilesUsed: number;
15
+ warnings: string[];
16
+ }
17
+ export declare function captureTraceabilityInput(options: TraceabilityCaptureOptions): TraceabilityCaptureResult;
18
+ //# sourceMappingURL=traceability_capture.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"traceability_capture.d.ts","sourceRoot":"","sources":["../../src/agent/traceability_capture.ts"],"names":[],"mappings":"AAmCA,MAAM,WAAW,0BAA0B;IACvC,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,yBAAyB;IACtC,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,gBAAgB,EAAE,MAAM,CAAC;IACzB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACtB;AAgRD,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,0BAA0B,GAAG,yBAAyB,CAgFvG"}