@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,637 @@
1
+ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
2
+ // See LICENSE.txt for license information.
3
+ import { existsSync, readFileSync } from 'fs';
4
+ import { dirname, resolve } from 'path';
5
+ const DEFAULT_CONFIG = {
6
+ path: '.',
7
+ testsRoot: undefined,
8
+ flowCatalogPath: undefined,
9
+ mode: 'impact',
10
+ framework: 'auto',
11
+ timeLimitMinutes: 10,
12
+ budget: {
13
+ maxUSD: 2,
14
+ maxTokens: 20000,
15
+ },
16
+ artifacts: {
17
+ mode: 'commit',
18
+ specsDir: '.e2e-ai-agents/reports',
19
+ },
20
+ selectors: {
21
+ patchOnApply: true,
22
+ },
23
+ testDiscovery: {
24
+ patterns: [],
25
+ },
26
+ flowDiscovery: {
27
+ patterns: [],
28
+ exclude: [],
29
+ },
30
+ catalogScoring: {
31
+ priorityScores: {
32
+ P0: 10,
33
+ P1: 6,
34
+ P2: 3,
35
+ },
36
+ fileMatchWeight: 1,
37
+ },
38
+ impact: {
39
+ allowFallback: false,
40
+ dependencyGraph: {
41
+ enabled: true,
42
+ maxDepth: 3,
43
+ maxExpandedFiles: 1000,
44
+ filePatterns: ['**/*.{ts,tsx,js,jsx}'],
45
+ excludePatterns: [
46
+ '**/node_modules/**',
47
+ '**/.git/**',
48
+ '**/dist/**',
49
+ '**/build/**',
50
+ '**/coverage/**',
51
+ '**/__tests__/**',
52
+ '**/tests/**',
53
+ '**/*.spec.*',
54
+ '**/*.test.*',
55
+ ],
56
+ aliasRoots: ['src'],
57
+ pathAliases: {},
58
+ },
59
+ traceability: {
60
+ enabled: true,
61
+ manifestPath: '.e2e-ai-agents/traceability.json',
62
+ minSignalsPerTest: 1,
63
+ },
64
+ subsystemRisk: {
65
+ enabled: false,
66
+ mapPath: '.e2e-ai-agents/subsystem-risk-map.json',
67
+ maxRulesPerFile: 4,
68
+ },
69
+ },
70
+ pipeline: {
71
+ enabled: false,
72
+ scenarios: 3,
73
+ outputDir: 'specs/functional/ai-assisted',
74
+ heal: true,
75
+ mcp: false,
76
+ },
77
+ llm: {
78
+ provider: 'anthropic',
79
+ fallback: 'ollama',
80
+ },
81
+ risk: {
82
+ p0Threshold: 7,
83
+ p1Threshold: 4,
84
+ criticalKeywords: [
85
+ 'auth',
86
+ 'login',
87
+ 'logout',
88
+ 'signup',
89
+ 'register',
90
+ 'onboarding',
91
+ 'checkout',
92
+ 'payment',
93
+ 'billing',
94
+ 'subscription',
95
+ 'admin',
96
+ 'permissions',
97
+ 'settings',
98
+ 'profile',
99
+ 'search',
100
+ 'dashboard',
101
+ 'message',
102
+ 'notifications',
103
+ ],
104
+ },
105
+ policy: {
106
+ minConfidenceForTargeted: 60,
107
+ safeMergeMinConfidence: 85,
108
+ forceFullOnWarningsAtOrAbove: 2,
109
+ forceFullOnP0WithGaps: true,
110
+ forceFullOnRiskyFiles: true,
111
+ riskyFilePatterns: [
112
+ '**/auth/**',
113
+ '**/login/**',
114
+ '**/permissions/**',
115
+ '**/admin/**',
116
+ '**/security/**',
117
+ '**/migrations/**',
118
+ '**/schema/**',
119
+ '**/*.sql',
120
+ '**/webhook/**',
121
+ ],
122
+ },
123
+ flags: {
124
+ defaultState: 'on',
125
+ },
126
+ audience: {
127
+ defaultRoles: ['member'],
128
+ },
129
+ blastRadius: {
130
+ memberBonus: 1,
131
+ guestBonus: 1,
132
+ adminOnlyPenalty: -1,
133
+ flagOffPenalty: -2,
134
+ },
135
+ git: {
136
+ since: 'HEAD~1',
137
+ includeUncommitted: true,
138
+ },
139
+ };
140
+ function safeReadJson(path) {
141
+ try {
142
+ if (!existsSync(path)) {
143
+ return undefined;
144
+ }
145
+ const raw = readFileSync(path, 'utf-8');
146
+ return JSON.parse(raw);
147
+ }
148
+ catch {
149
+ return undefined;
150
+ }
151
+ }
152
+ function mergeConfig(base, patch) {
153
+ return {
154
+ ...base,
155
+ ...patch,
156
+ budget: {
157
+ ...base.budget,
158
+ ...(patch.budget || {}),
159
+ },
160
+ artifacts: {
161
+ ...base.artifacts,
162
+ ...(patch.artifacts || {}),
163
+ },
164
+ selectors: {
165
+ ...base.selectors,
166
+ ...(patch.selectors || {}),
167
+ },
168
+ testDiscovery: {
169
+ ...base.testDiscovery,
170
+ ...(patch.testDiscovery || {}),
171
+ },
172
+ flowDiscovery: {
173
+ ...base.flowDiscovery,
174
+ ...(patch.flowDiscovery || {}),
175
+ },
176
+ catalogScoring: {
177
+ ...base.catalogScoring,
178
+ ...(patch.catalogScoring || {}),
179
+ },
180
+ impact: {
181
+ ...base.impact,
182
+ ...(patch.impact || {}),
183
+ dependencyGraph: {
184
+ ...base.impact.dependencyGraph,
185
+ ...(patch.impact?.dependencyGraph || {}),
186
+ pathAliases: {
187
+ ...base.impact.dependencyGraph.pathAliases,
188
+ ...(patch.impact?.dependencyGraph?.pathAliases || {}),
189
+ },
190
+ },
191
+ traceability: {
192
+ ...base.impact.traceability,
193
+ ...(patch.impact?.traceability || {}),
194
+ },
195
+ subsystemRisk: {
196
+ ...base.impact.subsystemRisk,
197
+ ...(patch.impact?.subsystemRisk || {}),
198
+ },
199
+ },
200
+ pipeline: {
201
+ ...base.pipeline,
202
+ ...(patch.pipeline || {}),
203
+ },
204
+ llm: {
205
+ ...base.llm,
206
+ ...(patch.llm || {}),
207
+ },
208
+ risk: {
209
+ ...base.risk,
210
+ ...(patch.risk || {}),
211
+ },
212
+ policy: {
213
+ ...base.policy,
214
+ ...(patch.policy || {}),
215
+ },
216
+ flags: {
217
+ ...base.flags,
218
+ ...(patch.flags || {}),
219
+ },
220
+ audience: {
221
+ ...base.audience,
222
+ ...(patch.audience || {}),
223
+ },
224
+ blastRadius: {
225
+ ...base.blastRadius,
226
+ ...(patch.blastRadius || {}),
227
+ },
228
+ git: {
229
+ ...base.git,
230
+ ...(patch.git || {}),
231
+ },
232
+ };
233
+ }
234
+ function coerceNumber(value) {
235
+ if (typeof value === 'number' && Number.isFinite(value)) {
236
+ return value;
237
+ }
238
+ if (typeof value === 'string') {
239
+ const parsed = Number(value);
240
+ if (Number.isFinite(parsed)) {
241
+ return parsed;
242
+ }
243
+ }
244
+ return undefined;
245
+ }
246
+ function normalizeMode(value) {
247
+ if (value === 'impact' || value === 'gap') {
248
+ return value;
249
+ }
250
+ return undefined;
251
+ }
252
+ function normalizeFramework(value) {
253
+ if (value === 'auto' || value === 'playwright' || value === 'cypress' || value === 'selenium') {
254
+ return value;
255
+ }
256
+ return undefined;
257
+ }
258
+ function normalizeFlagState(value) {
259
+ if (typeof value !== 'string') {
260
+ return undefined;
261
+ }
262
+ const normalized = value.trim().toLowerCase();
263
+ if (normalized === 'on' || normalized === 'off' || normalized === 'unknown') {
264
+ return normalized;
265
+ }
266
+ return undefined;
267
+ }
268
+ function extractConfigPatch(raw) {
269
+ const patch = {};
270
+ if (typeof raw.path === 'string') {
271
+ patch.path = raw.path;
272
+ }
273
+ if (typeof raw.testsRoot === 'string') {
274
+ patch.testsRoot = raw.testsRoot;
275
+ }
276
+ if (typeof raw.flowCatalogPath === 'string') {
277
+ patch.flowCatalogPath = raw.flowCatalogPath;
278
+ }
279
+ const mode = normalizeMode(raw.mode);
280
+ if (mode) {
281
+ patch.mode = mode;
282
+ }
283
+ const framework = normalizeFramework(raw.framework);
284
+ if (framework) {
285
+ patch.framework = framework;
286
+ }
287
+ const timeLimitMinutes = coerceNumber(raw.timeLimitMinutes);
288
+ if (timeLimitMinutes !== undefined) {
289
+ patch.timeLimitMinutes = timeLimitMinutes;
290
+ }
291
+ if (raw.budget && typeof raw.budget === 'object') {
292
+ const budget = raw.budget;
293
+ patch.budget = {
294
+ maxUSD: coerceNumber(budget.maxUSD),
295
+ maxTokens: coerceNumber(budget.maxTokens),
296
+ };
297
+ }
298
+ if (raw.artifacts && typeof raw.artifacts === 'object') {
299
+ const artifacts = raw.artifacts;
300
+ const mode = artifacts.mode === 'commit' || artifacts.mode === 'keep-local' || artifacts.mode === 'none'
301
+ ? artifacts.mode
302
+ : undefined;
303
+ const specsDir = typeof artifacts.specsDir === 'string' ? artifacts.specsDir : undefined;
304
+ if (mode || specsDir) {
305
+ patch.artifacts = {
306
+ mode: mode ?? DEFAULT_CONFIG.artifacts.mode,
307
+ specsDir: specsDir ?? DEFAULT_CONFIG.artifacts.specsDir,
308
+ };
309
+ }
310
+ }
311
+ if (raw.selectors && typeof raw.selectors === 'object') {
312
+ const selectors = raw.selectors;
313
+ patch.selectors = {
314
+ patchOnApply: selectors.patchOnApply !== undefined ? Boolean(selectors.patchOnApply) : DEFAULT_CONFIG.selectors.patchOnApply,
315
+ };
316
+ }
317
+ if (raw.testDiscovery && typeof raw.testDiscovery === 'object') {
318
+ const testDiscovery = raw.testDiscovery;
319
+ patch.testDiscovery = {
320
+ patterns: Array.isArray(testDiscovery.patterns)
321
+ ? testDiscovery.patterns.filter((pattern) => typeof pattern === 'string')
322
+ : undefined,
323
+ };
324
+ }
325
+ if (raw.flowDiscovery && typeof raw.flowDiscovery === 'object') {
326
+ const flowDiscovery = raw.flowDiscovery;
327
+ patch.flowDiscovery = {
328
+ patterns: Array.isArray(flowDiscovery.patterns)
329
+ ? flowDiscovery.patterns.filter((pattern) => typeof pattern === 'string')
330
+ : undefined,
331
+ exclude: Array.isArray(flowDiscovery.exclude)
332
+ ? flowDiscovery.exclude.filter((pattern) => typeof pattern === 'string')
333
+ : undefined,
334
+ };
335
+ }
336
+ if (raw.catalogScoring && typeof raw.catalogScoring === 'object') {
337
+ const catalogScoring = raw.catalogScoring;
338
+ const priorityScores = catalogScoring.priorityScores;
339
+ patch.catalogScoring = {
340
+ priorityScores: {
341
+ P0: coerceNumber(priorityScores?.P0) ?? DEFAULT_CONFIG.catalogScoring.priorityScores.P0,
342
+ P1: coerceNumber(priorityScores?.P1) ?? DEFAULT_CONFIG.catalogScoring.priorityScores.P1,
343
+ P2: coerceNumber(priorityScores?.P2) ?? DEFAULT_CONFIG.catalogScoring.priorityScores.P2,
344
+ },
345
+ fileMatchWeight: coerceNumber(catalogScoring.fileMatchWeight) ?? DEFAULT_CONFIG.catalogScoring.fileMatchWeight,
346
+ };
347
+ }
348
+ if (raw.impact && typeof raw.impact === 'object') {
349
+ const impact = raw.impact;
350
+ const dependencyGraphRaw = impact.dependencyGraph && typeof impact.dependencyGraph === 'object'
351
+ ? impact.dependencyGraph
352
+ : undefined;
353
+ const traceabilityRaw = impact.traceability && typeof impact.traceability === 'object'
354
+ ? impact.traceability
355
+ : undefined;
356
+ const subsystemRiskRaw = impact.subsystemRisk && typeof impact.subsystemRisk === 'object'
357
+ ? impact.subsystemRisk
358
+ : undefined;
359
+ const pathAliasesRaw = dependencyGraphRaw?.pathAliases && typeof dependencyGraphRaw.pathAliases === 'object'
360
+ ? dependencyGraphRaw.pathAliases
361
+ : undefined;
362
+ const parsedPathAliases = {};
363
+ if (pathAliasesRaw) {
364
+ for (const [key, value] of Object.entries(pathAliasesRaw)) {
365
+ if (typeof value === 'string') {
366
+ parsedPathAliases[key] = [value];
367
+ continue;
368
+ }
369
+ if (Array.isArray(value)) {
370
+ const targets = value.filter((item) => typeof item === 'string');
371
+ if (targets.length > 0) {
372
+ parsedPathAliases[key] = targets;
373
+ }
374
+ }
375
+ }
376
+ }
377
+ patch.impact = {
378
+ allowFallback: impact.allowFallback !== undefined ? Boolean(impact.allowFallback) : DEFAULT_CONFIG.impact.allowFallback,
379
+ dependencyGraph: {
380
+ enabled: dependencyGraphRaw?.enabled !== undefined
381
+ ? Boolean(dependencyGraphRaw.enabled)
382
+ : DEFAULT_CONFIG.impact.dependencyGraph.enabled,
383
+ maxDepth: coerceNumber(dependencyGraphRaw?.maxDepth) ?? DEFAULT_CONFIG.impact.dependencyGraph.maxDepth,
384
+ maxExpandedFiles: coerceNumber(dependencyGraphRaw?.maxExpandedFiles) ?? DEFAULT_CONFIG.impact.dependencyGraph.maxExpandedFiles,
385
+ filePatterns: Array.isArray(dependencyGraphRaw?.filePatterns)
386
+ ? dependencyGraphRaw?.filePatterns.filter((pattern) => typeof pattern === 'string')
387
+ : DEFAULT_CONFIG.impact.dependencyGraph.filePatterns,
388
+ excludePatterns: Array.isArray(dependencyGraphRaw?.excludePatterns)
389
+ ? dependencyGraphRaw?.excludePatterns.filter((pattern) => typeof pattern === 'string')
390
+ : DEFAULT_CONFIG.impact.dependencyGraph.excludePatterns,
391
+ aliasRoots: Array.isArray(dependencyGraphRaw?.aliasRoots)
392
+ ? dependencyGraphRaw?.aliasRoots.filter((pattern) => typeof pattern === 'string')
393
+ : DEFAULT_CONFIG.impact.dependencyGraph.aliasRoots,
394
+ pathAliases: pathAliasesRaw
395
+ ? parsedPathAliases
396
+ : DEFAULT_CONFIG.impact.dependencyGraph.pathAliases,
397
+ },
398
+ traceability: {
399
+ enabled: traceabilityRaw?.enabled !== undefined
400
+ ? Boolean(traceabilityRaw.enabled)
401
+ : DEFAULT_CONFIG.impact.traceability.enabled,
402
+ manifestPath: typeof traceabilityRaw?.manifestPath === 'string'
403
+ ? traceabilityRaw.manifestPath
404
+ : DEFAULT_CONFIG.impact.traceability.manifestPath,
405
+ minSignalsPerTest: coerceNumber(traceabilityRaw?.minSignalsPerTest) ?? DEFAULT_CONFIG.impact.traceability.minSignalsPerTest,
406
+ },
407
+ subsystemRisk: {
408
+ enabled: subsystemRiskRaw?.enabled !== undefined
409
+ ? Boolean(subsystemRiskRaw.enabled)
410
+ : DEFAULT_CONFIG.impact.subsystemRisk.enabled,
411
+ mapPath: typeof subsystemRiskRaw?.mapPath === 'string'
412
+ ? subsystemRiskRaw.mapPath
413
+ : DEFAULT_CONFIG.impact.subsystemRisk.mapPath,
414
+ maxRulesPerFile: coerceNumber(subsystemRiskRaw?.maxRulesPerFile) ?? DEFAULT_CONFIG.impact.subsystemRisk.maxRulesPerFile,
415
+ },
416
+ };
417
+ }
418
+ if (raw.pipeline && typeof raw.pipeline === 'object') {
419
+ const pipeline = raw.pipeline;
420
+ patch.pipeline = {
421
+ enabled: pipeline.enabled !== undefined ? Boolean(pipeline.enabled) : DEFAULT_CONFIG.pipeline.enabled,
422
+ scenarios: coerceNumber(pipeline.scenarios) ?? DEFAULT_CONFIG.pipeline.scenarios,
423
+ outputDir: typeof pipeline.outputDir === 'string' ? pipeline.outputDir : DEFAULT_CONFIG.pipeline.outputDir,
424
+ heal: pipeline.heal !== undefined ? Boolean(pipeline.heal) : DEFAULT_CONFIG.pipeline.heal,
425
+ baseUrl: typeof pipeline.baseUrl === 'string' ? pipeline.baseUrl : undefined,
426
+ browser: pipeline.browser === 'chrome' ||
427
+ pipeline.browser === 'chromium' ||
428
+ pipeline.browser === 'firefox' ||
429
+ pipeline.browser === 'webkit'
430
+ ? pipeline.browser
431
+ : undefined,
432
+ headless: pipeline.headless !== undefined ? Boolean(pipeline.headless) : undefined,
433
+ project: typeof pipeline.project === 'string' ? pipeline.project : undefined,
434
+ parallel: pipeline.parallel !== undefined ? Boolean(pipeline.parallel) : undefined,
435
+ dryRun: pipeline.dryRun !== undefined ? Boolean(pipeline.dryRun) : undefined,
436
+ mcp: pipeline.mcp !== undefined ? Boolean(pipeline.mcp) : DEFAULT_CONFIG.pipeline.mcp,
437
+ };
438
+ }
439
+ if (raw.llm && typeof raw.llm === 'object') {
440
+ const llm = raw.llm;
441
+ patch.llm = {
442
+ provider: typeof llm.provider === 'string' ? llm.provider : undefined,
443
+ fallback: typeof llm.fallback === 'string' ? llm.fallback : undefined,
444
+ };
445
+ }
446
+ if (typeof raw.specPDF === 'string') {
447
+ patch.specPDF = raw.specPDF;
448
+ }
449
+ if (raw.risk && typeof raw.risk === 'object') {
450
+ const risk = raw.risk;
451
+ patch.risk = {
452
+ p0Threshold: coerceNumber(risk.p0Threshold) ?? DEFAULT_CONFIG.risk.p0Threshold,
453
+ p1Threshold: coerceNumber(risk.p1Threshold) ?? DEFAULT_CONFIG.risk.p1Threshold,
454
+ criticalKeywords: Array.isArray(risk.criticalKeywords)
455
+ ? risk.criticalKeywords.filter((keyword) => typeof keyword === 'string')
456
+ : DEFAULT_CONFIG.risk.criticalKeywords,
457
+ };
458
+ }
459
+ if (raw.policy && typeof raw.policy === 'object') {
460
+ const policy = raw.policy;
461
+ patch.policy = {
462
+ minConfidenceForTargeted: coerceNumber(policy.minConfidenceForTargeted) ?? DEFAULT_CONFIG.policy.minConfidenceForTargeted,
463
+ safeMergeMinConfidence: coerceNumber(policy.safeMergeMinConfidence) ?? DEFAULT_CONFIG.policy.safeMergeMinConfidence,
464
+ forceFullOnWarningsAtOrAbove: coerceNumber(policy.forceFullOnWarningsAtOrAbove) ?? DEFAULT_CONFIG.policy.forceFullOnWarningsAtOrAbove,
465
+ forceFullOnP0WithGaps: policy.forceFullOnP0WithGaps !== undefined
466
+ ? Boolean(policy.forceFullOnP0WithGaps)
467
+ : DEFAULT_CONFIG.policy.forceFullOnP0WithGaps,
468
+ forceFullOnRiskyFiles: policy.forceFullOnRiskyFiles !== undefined
469
+ ? Boolean(policy.forceFullOnRiskyFiles)
470
+ : DEFAULT_CONFIG.policy.forceFullOnRiskyFiles,
471
+ riskyFilePatterns: Array.isArray(policy.riskyFilePatterns)
472
+ ? policy.riskyFilePatterns.filter((pattern) => typeof pattern === 'string')
473
+ : DEFAULT_CONFIG.policy.riskyFilePatterns,
474
+ };
475
+ }
476
+ if (raw.flags && typeof raw.flags === 'object') {
477
+ const flags = raw.flags;
478
+ patch.flags = {
479
+ defaultState: normalizeFlagState(flags.defaultState) ?? DEFAULT_CONFIG.flags.defaultState,
480
+ };
481
+ }
482
+ if (raw.audience && typeof raw.audience === 'object') {
483
+ const audience = raw.audience;
484
+ const roles = Array.isArray(audience.defaultRoles)
485
+ ? audience.defaultRoles.filter((role) => typeof role === 'string')
486
+ : [];
487
+ patch.audience = {
488
+ defaultRoles: (roles.length > 0 ? roles : DEFAULT_CONFIG.audience.defaultRoles),
489
+ };
490
+ }
491
+ if (raw.blastRadius && typeof raw.blastRadius === 'object') {
492
+ const blastRadius = raw.blastRadius;
493
+ patch.blastRadius = {
494
+ memberBonus: coerceNumber(blastRadius.memberBonus) ?? DEFAULT_CONFIG.blastRadius.memberBonus,
495
+ guestBonus: coerceNumber(blastRadius.guestBonus) ?? DEFAULT_CONFIG.blastRadius.guestBonus,
496
+ adminOnlyPenalty: coerceNumber(blastRadius.adminOnlyPenalty) ?? DEFAULT_CONFIG.blastRadius.adminOnlyPenalty,
497
+ flagOffPenalty: coerceNumber(blastRadius.flagOffPenalty) ?? DEFAULT_CONFIG.blastRadius.flagOffPenalty,
498
+ };
499
+ }
500
+ if (raw.git && typeof raw.git === 'object') {
501
+ const git = raw.git;
502
+ patch.git = {
503
+ since: typeof git.since === 'string' ? git.since : DEFAULT_CONFIG.git.since,
504
+ includeUncommitted: git.includeUncommitted !== undefined ? Boolean(git.includeUncommitted) : DEFAULT_CONFIG.git.includeUncommitted,
505
+ };
506
+ }
507
+ return patch;
508
+ }
509
+ export function resolveConfig(cwd, configPath, overrides) {
510
+ const resolvedConfigPath = configPath ? resolve(cwd, configPath) : undefined;
511
+ const configDir = resolvedConfigPath ? dirname(resolvedConfigPath) : cwd;
512
+ const rawConfig = resolvedConfigPath ? safeReadJson(resolvedConfigPath) : undefined;
513
+ const configPatch = rawConfig ? extractConfigPatch(rawConfig) : {};
514
+ let config = mergeConfig(DEFAULT_CONFIG, configPatch);
515
+ if (overrides?.mode) {
516
+ config.mode = overrides.mode;
517
+ }
518
+ if (overrides?.framework) {
519
+ config.framework = overrides.framework;
520
+ }
521
+ if (overrides?.timeLimitMinutes !== undefined) {
522
+ config.timeLimitMinutes = overrides.timeLimitMinutes;
523
+ }
524
+ if (overrides?.budget) {
525
+ const budgetPatch = {};
526
+ if (overrides.budget.maxUSD !== undefined) {
527
+ budgetPatch.maxUSD = overrides.budget.maxUSD;
528
+ }
529
+ if (overrides.budget.maxTokens !== undefined) {
530
+ budgetPatch.maxTokens = overrides.budget.maxTokens;
531
+ }
532
+ config.budget = { ...config.budget, ...budgetPatch };
533
+ }
534
+ if (overrides?.testPatterns && overrides.testPatterns.length > 0) {
535
+ config.testDiscovery = { patterns: overrides.testPatterns };
536
+ }
537
+ if (overrides?.flowPatterns && overrides.flowPatterns.length > 0) {
538
+ config.flowDiscovery = {
539
+ patterns: overrides.flowPatterns,
540
+ exclude: overrides.flowExclude,
541
+ };
542
+ }
543
+ else if (overrides?.flowExclude && overrides.flowExclude.length > 0) {
544
+ config.flowDiscovery = {
545
+ ...config.flowDiscovery,
546
+ exclude: overrides.flowExclude,
547
+ };
548
+ }
549
+ if (overrides?.pipeline) {
550
+ const pipelinePatch = {};
551
+ if (overrides.pipeline.enabled !== undefined) {
552
+ pipelinePatch.enabled = overrides.pipeline.enabled;
553
+ }
554
+ if (overrides.pipeline.scenarios !== undefined) {
555
+ pipelinePatch.scenarios = overrides.pipeline.scenarios;
556
+ }
557
+ if (overrides.pipeline.outputDir) {
558
+ pipelinePatch.outputDir = overrides.pipeline.outputDir;
559
+ }
560
+ if (overrides.pipeline.heal !== undefined) {
561
+ pipelinePatch.heal = overrides.pipeline.heal;
562
+ }
563
+ if (overrides.pipeline.baseUrl) {
564
+ pipelinePatch.baseUrl = overrides.pipeline.baseUrl;
565
+ }
566
+ if (overrides.pipeline.browser) {
567
+ pipelinePatch.browser = overrides.pipeline.browser;
568
+ }
569
+ if (overrides.pipeline.headless !== undefined) {
570
+ pipelinePatch.headless = overrides.pipeline.headless;
571
+ }
572
+ if (overrides.pipeline.project) {
573
+ pipelinePatch.project = overrides.pipeline.project;
574
+ }
575
+ if (overrides.pipeline.parallel !== undefined) {
576
+ pipelinePatch.parallel = overrides.pipeline.parallel;
577
+ }
578
+ if (overrides.pipeline.dryRun !== undefined) {
579
+ pipelinePatch.dryRun = overrides.pipeline.dryRun;
580
+ }
581
+ config.pipeline = { ...config.pipeline, ...pipelinePatch };
582
+ }
583
+ if (overrides?.policy) {
584
+ const policyPatch = {};
585
+ if (overrides.policy.minConfidenceForTargeted !== undefined) {
586
+ policyPatch.minConfidenceForTargeted = overrides.policy.minConfidenceForTargeted;
587
+ }
588
+ if (overrides.policy.safeMergeMinConfidence !== undefined) {
589
+ policyPatch.safeMergeMinConfidence = overrides.policy.safeMergeMinConfidence;
590
+ }
591
+ if (overrides.policy.forceFullOnWarningsAtOrAbove !== undefined) {
592
+ policyPatch.forceFullOnWarningsAtOrAbove = overrides.policy.forceFullOnWarningsAtOrAbove;
593
+ }
594
+ if (overrides.policy.forceFullOnP0WithGaps !== undefined) {
595
+ policyPatch.forceFullOnP0WithGaps = overrides.policy.forceFullOnP0WithGaps;
596
+ }
597
+ if (overrides.policy.forceFullOnRiskyFiles !== undefined) {
598
+ policyPatch.forceFullOnRiskyFiles = overrides.policy.forceFullOnRiskyFiles;
599
+ }
600
+ if (overrides.policy.riskyFilePatterns && overrides.policy.riskyFilePatterns.length > 0) {
601
+ policyPatch.riskyFilePatterns = overrides.policy.riskyFilePatterns;
602
+ }
603
+ config.policy = { ...config.policy, ...policyPatch };
604
+ }
605
+ if (overrides?.specPDF) {
606
+ config.specPDF = overrides.specPDF;
607
+ }
608
+ if (overrides?.gitSince) {
609
+ config.git.since = overrides.gitSince;
610
+ }
611
+ if (overrides?.path) {
612
+ config.path = overrides.path;
613
+ }
614
+ if (overrides?.testsRoot) {
615
+ config.testsRoot = overrides.testsRoot;
616
+ }
617
+ if (overrides?.flowCatalogPath) {
618
+ config.flowCatalogPath = overrides.flowCatalogPath;
619
+ }
620
+ const resolvedRoot = resolve(configDir, config.path);
621
+ config.path = resolvedRoot;
622
+ if (config.testsRoot) {
623
+ config.testsRoot = resolve(configDir, config.testsRoot);
624
+ }
625
+ else {
626
+ config.testsRoot = resolvedRoot;
627
+ }
628
+ if (config.flowCatalogPath) {
629
+ config.flowCatalogPath = resolve(configDir, config.flowCatalogPath);
630
+ }
631
+ config.impact.subsystemRisk.mapPath = resolve(configDir, config.impact.subsystemRisk.mapPath);
632
+ return {
633
+ config,
634
+ configPath: resolvedConfigPath,
635
+ rootDir: resolvedRoot,
636
+ };
637
+ }