@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
package/dist/cli.js ADDED
@@ -0,0 +1,843 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
4
+ // See LICENSE.txt for license information.
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const fs_1 = require("fs");
7
+ const path_1 = require("path");
8
+ const config_js_1 = require("./agent/config.js");
9
+ const anthropic_provider_js_1 = require("./anthropic_provider.js");
10
+ const provider_interface_js_1 = require("./provider_interface.js");
11
+ const runner_js_1 = require("./agent/runner.js");
12
+ const plan_js_1 = require("./agent/plan.js");
13
+ const operational_insights_js_1 = require("./agent/operational_insights.js");
14
+ const feedback_js_1 = require("./agent/feedback.js");
15
+ const handoff_js_1 = require("./agent/handoff.js");
16
+ const traceability_ingest_js_1 = require("./agent/traceability_ingest.js");
17
+ const traceability_capture_js_1 = require("./agent/traceability_capture.js");
18
+ const pipeline_js_1 = require("./agent/pipeline.js");
19
+ const playwright_report_js_1 = require("./agent/playwright_report.js");
20
+ const CONFIG_CANDIDATES = ['e2e-ai-agents.config.json', '.e2e-ai-agents.config.json'];
21
+ function findConfigUpwards(startDir) {
22
+ if (!startDir) {
23
+ return undefined;
24
+ }
25
+ let current = (0, path_1.resolve)(startDir);
26
+ while (true) {
27
+ for (const candidate of CONFIG_CANDIDATES) {
28
+ const fullPath = (0, path_1.join)(current, candidate);
29
+ if ((0, fs_1.existsSync)(fullPath)) {
30
+ return fullPath;
31
+ }
32
+ }
33
+ const parent = (0, path_1.dirname)(current);
34
+ if (parent === current) {
35
+ break;
36
+ }
37
+ current = parent;
38
+ }
39
+ return undefined;
40
+ }
41
+ function resolveAutoConfig(args) {
42
+ if (args.configPath) {
43
+ return args.configPath;
44
+ }
45
+ const searchRoots = [
46
+ process.cwd(),
47
+ args.testsRoot,
48
+ args.path,
49
+ ].filter(Boolean);
50
+ for (const root of searchRoots) {
51
+ const found = findConfigUpwards(root);
52
+ if (found) {
53
+ return found;
54
+ }
55
+ }
56
+ return undefined;
57
+ }
58
+ function printUsage() {
59
+ // eslint-disable-next-line no-console
60
+ console.log([
61
+ 'Usage:',
62
+ ' e2e-ai-agents impact --path <app-root> [options]',
63
+ ' e2e-ai-agents gap --path <app-root> [options]',
64
+ ' e2e-ai-agents suggest --path <app-root> [options]',
65
+ ' e2e-ai-agents approve-and-generate --path <app-root> [options]',
66
+ ' e2e-ai-agents auto-heal-pr --path <app-root> [options]',
67
+ ' e2e-ai-agents finalize-generated-tests --path <app-root> [options]',
68
+ ' e2e-ai-agents feedback --path <app-root> --feedback-input <json>',
69
+ ' e2e-ai-agents traceability-capture --path <app-root> --traceability-report <json>',
70
+ ' e2e-ai-agents traceability-ingest --path <app-root> --traceability-input <json>',
71
+ ' e2e-ai-agents llm-health',
72
+ '',
73
+ 'Options:',
74
+ ' --config <path> Path to e2e-ai-agents.config.json (auto-discovered if present)',
75
+ ' --path <app-root> Path to the web app (required)',
76
+ ' --tests-root <path> Path to tests root (optional)',
77
+ ' --framework <name> auto | playwright | cypress | selenium',
78
+ ' --patterns <globs> Comma-separated test patterns',
79
+ ' --flow-patterns <g> Comma-separated flow discovery patterns',
80
+ ' --flow-exclude <g> Comma-separated flow exclude patterns',
81
+ ' --flow-catalog <path> Path to flow catalog JSON',
82
+ ' --allow-fallback Allow impact analysis without diff',
83
+ ' --pipeline Run Playwright AI pipeline for missing P0/P1 flows',
84
+ ' --pipeline-scenarios Number of scenarios per flow (default 3)',
85
+ ' --pipeline-output Output directory for generated tests',
86
+ ' --pipeline-base-url Base URL for Playwright runs',
87
+ ' --pipeline-browser Browser: chrome|chromium|firefox|webkit',
88
+ ' --pipeline-headless Run in headless mode',
89
+ ' --pipeline-project Playwright project name',
90
+ ' --pipeline-parallel Enable parallel mode in generator',
91
+ ' --pipeline-dry-run Do not execute pipeline (report only)',
92
+ ' --pipeline-mcp Use Playwright MCP server for exploration/healing',
93
+ ' --spec <path> Optional spec PDF for context',
94
+ ' --since <git-ref> Git ref for impact analysis (default HEAD~1)',
95
+ ' --time <minutes> Time limit in minutes',
96
+ ' --budget-usd <amount> Max LLM budget in USD',
97
+ ' --budget-tokens <n> Max LLM tokens',
98
+ ' --policy-min-confidence <n> Minimum confidence for targeted suite',
99
+ ' --policy-safe-merge-confidence <n> Confidence needed for safe-to-merge',
100
+ ' --policy-force-full-on-warnings <n> Escalate to full at warning count',
101
+ ' --policy-risky-patterns <globs> Comma-separated risky file globs',
102
+ ' --ci-comment-path <path> Write CI markdown summary',
103
+ ' --github-output <path> Write GitHub Actions outputs',
104
+ ' --fail-on-must-add-tests Exit non-zero on must-add-tests decision',
105
+ ' --feedback-input <path> Path to recommendation feedback JSON',
106
+ ' --traceability-report <path> Path to Playwright JSON report for traceability capture',
107
+ ' --traceability-capture-output <path> Output path for generated traceability ingest JSON',
108
+ ' --traceability-coverage-map <path> Optional coverage map (test<->files) to enrich traceability capture',
109
+ ' --traceability-changed-files <path> Optional changed-files list/JSON fallback for traceability capture',
110
+ ' --traceability-input <path> Path to traceability ingest JSON payload',
111
+ ' --traceability-min-hits <n> Minimum signal hits required per file mapping',
112
+ ' --traceability-max-files-per-test <n> Cap max mapped files per test',
113
+ ' --traceability-max-age-days <n> Drop stale mappings older than N days',
114
+ ' --branch <name> Optional handoff branch (prefixed with codex/)',
115
+ ' --commit-message <m> Commit message for finalize-generated-tests',
116
+ ' --create-pr Open PR with gh after commit',
117
+ ' --pr-title <title> PR title for finalize-generated-tests',
118
+ ' --pr-body <body> PR body for finalize-generated-tests',
119
+ ' --pr-base <branch> PR base branch for finalize-generated-tests',
120
+ ' (auto-heal-pr defaults to base=master)',
121
+ ' --dry-run Preview actions without mutating git state',
122
+ ' --apply Apply data-testid patches and generate tests',
123
+ ' (legacy shortcut; prefer approve-and-generate)',
124
+ ' --help Show help',
125
+ ].join('\n'));
126
+ }
127
+ function parseArgs(argv) {
128
+ const parsed = { apply: false, help: false };
129
+ if (argv.length === 0) {
130
+ return parsed;
131
+ }
132
+ const command = argv[0];
133
+ if (command === 'impact'
134
+ || command === 'gap'
135
+ || command === 'suggest'
136
+ || command === 'approve-and-generate'
137
+ || command === 'auto-heal-pr'
138
+ || command === 'finalize-generated-tests'
139
+ || command === 'feedback'
140
+ || command === 'traceability-capture'
141
+ || command === 'traceability-ingest'
142
+ || command === 'llm-health') {
143
+ parsed.command = command;
144
+ }
145
+ for (let i = 1; i < argv.length; i += 1) {
146
+ const arg = argv[i];
147
+ const next = argv[i + 1];
148
+ if (arg === '--help' || arg === '-h') {
149
+ parsed.help = true;
150
+ continue;
151
+ }
152
+ if (arg === '--apply') {
153
+ parsed.apply = true;
154
+ continue;
155
+ }
156
+ if (arg === '--config' && next) {
157
+ parsed.configPath = next;
158
+ i += 1;
159
+ continue;
160
+ }
161
+ if (arg === '--path' && next) {
162
+ parsed.path = next;
163
+ i += 1;
164
+ continue;
165
+ }
166
+ if (arg === '--tests-root' && next) {
167
+ parsed.testsRoot = next;
168
+ i += 1;
169
+ continue;
170
+ }
171
+ if (arg === '--framework' && next) {
172
+ parsed.framework = next;
173
+ i += 1;
174
+ continue;
175
+ }
176
+ if (arg === '--patterns' && next) {
177
+ parsed.testPatterns = next.split(',').map((value) => value.trim()).filter(Boolean);
178
+ i += 1;
179
+ continue;
180
+ }
181
+ if (arg === '--flow-patterns' && next) {
182
+ parsed.flowPatterns = next.split(',').map((value) => value.trim()).filter(Boolean);
183
+ i += 1;
184
+ continue;
185
+ }
186
+ if (arg === '--flow-exclude' && next) {
187
+ parsed.flowExclude = next.split(',').map((value) => value.trim()).filter(Boolean);
188
+ i += 1;
189
+ continue;
190
+ }
191
+ if (arg === '--flow-catalog' && next) {
192
+ parsed.flowCatalogPath = next;
193
+ i += 1;
194
+ continue;
195
+ }
196
+ if (arg === '--allow-fallback') {
197
+ parsed.allowFallback = true;
198
+ continue;
199
+ }
200
+ if (arg === '--pipeline') {
201
+ parsed.pipeline = true;
202
+ continue;
203
+ }
204
+ if (arg === '--pipeline-mcp') {
205
+ parsed.pipelineMcp = true;
206
+ continue;
207
+ }
208
+ if (arg === '--pipeline-scenarios' && next) {
209
+ const value = Number(next);
210
+ if (Number.isFinite(value)) {
211
+ parsed.pipelineScenarios = value;
212
+ }
213
+ i += 1;
214
+ continue;
215
+ }
216
+ if (arg === '--pipeline-output' && next) {
217
+ parsed.pipelineOutput = next;
218
+ i += 1;
219
+ continue;
220
+ }
221
+ if (arg === '--pipeline-base-url' && next) {
222
+ parsed.pipelineBaseUrl = next;
223
+ i += 1;
224
+ continue;
225
+ }
226
+ if (arg === '--pipeline-browser' && next) {
227
+ const value = next;
228
+ if (value === 'chrome' || value === 'chromium' || value === 'firefox' || value === 'webkit') {
229
+ parsed.pipelineBrowser = value;
230
+ }
231
+ i += 1;
232
+ continue;
233
+ }
234
+ if (arg === '--pipeline-headless') {
235
+ parsed.pipelineHeadless = true;
236
+ continue;
237
+ }
238
+ if (arg === '--pipeline-project' && next) {
239
+ parsed.pipelineProject = next;
240
+ i += 1;
241
+ continue;
242
+ }
243
+ if (arg === '--pipeline-parallel') {
244
+ parsed.pipelineParallel = true;
245
+ continue;
246
+ }
247
+ if (arg === '--pipeline-dry-run') {
248
+ parsed.pipelineDryRun = true;
249
+ continue;
250
+ }
251
+ if (arg === '--spec' && next) {
252
+ parsed.specPDF = next;
253
+ i += 1;
254
+ continue;
255
+ }
256
+ if (arg === '--since' && next) {
257
+ parsed.gitSince = next;
258
+ i += 1;
259
+ continue;
260
+ }
261
+ if (arg === '--time' && next) {
262
+ const value = Number(next);
263
+ if (Number.isFinite(value)) {
264
+ parsed.timeLimitMinutes = value;
265
+ }
266
+ i += 1;
267
+ continue;
268
+ }
269
+ if (arg === '--budget-usd' && next) {
270
+ const value = Number(next);
271
+ if (Number.isFinite(value)) {
272
+ parsed.budgetUSD = value;
273
+ }
274
+ i += 1;
275
+ continue;
276
+ }
277
+ if (arg === '--budget-tokens' && next) {
278
+ const value = Number(next);
279
+ if (Number.isFinite(value)) {
280
+ parsed.budgetTokens = value;
281
+ }
282
+ i += 1;
283
+ continue;
284
+ }
285
+ if (arg === '--policy-min-confidence' && next) {
286
+ const value = Number(next);
287
+ if (Number.isFinite(value)) {
288
+ parsed.policyMinConfidence = value;
289
+ }
290
+ i += 1;
291
+ continue;
292
+ }
293
+ if (arg === '--policy-safe-merge-confidence' && next) {
294
+ const value = Number(next);
295
+ if (Number.isFinite(value)) {
296
+ parsed.policySafeMergeConfidence = value;
297
+ }
298
+ i += 1;
299
+ continue;
300
+ }
301
+ if (arg === '--policy-force-full-on-warnings' && next) {
302
+ const value = Number(next);
303
+ if (Number.isFinite(value)) {
304
+ parsed.policyWarningsThreshold = value;
305
+ }
306
+ i += 1;
307
+ continue;
308
+ }
309
+ if (arg === '--policy-risky-patterns' && next) {
310
+ parsed.policyRiskyPatterns = next.split(',').map((value) => value.trim()).filter(Boolean);
311
+ i += 1;
312
+ continue;
313
+ }
314
+ if (arg === '--ci-comment-path' && next) {
315
+ parsed.ciCommentPath = next;
316
+ i += 1;
317
+ continue;
318
+ }
319
+ if (arg === '--github-output' && next) {
320
+ parsed.githubOutputPath = next;
321
+ i += 1;
322
+ continue;
323
+ }
324
+ if (arg === '--fail-on-must-add-tests') {
325
+ parsed.failOnMustAddTests = true;
326
+ continue;
327
+ }
328
+ if (arg === '--feedback-input' && next) {
329
+ parsed.feedbackInputPath = next;
330
+ i += 1;
331
+ continue;
332
+ }
333
+ if (arg === '--traceability-report' && next) {
334
+ parsed.traceabilityReportPath = next;
335
+ i += 1;
336
+ continue;
337
+ }
338
+ if (arg === '--traceability-capture-output' && next) {
339
+ parsed.traceabilityCaptureOutputPath = next;
340
+ i += 1;
341
+ continue;
342
+ }
343
+ if (arg === '--traceability-coverage-map' && next) {
344
+ parsed.traceabilityCoverageMapPath = next;
345
+ i += 1;
346
+ continue;
347
+ }
348
+ if (arg === '--traceability-changed-files' && next) {
349
+ parsed.traceabilityChangedFilesPath = next;
350
+ i += 1;
351
+ continue;
352
+ }
353
+ if (arg === '--traceability-input' && next) {
354
+ parsed.traceabilityInputPath = next;
355
+ i += 1;
356
+ continue;
357
+ }
358
+ if (arg === '--traceability-min-hits' && next) {
359
+ const value = Number(next);
360
+ if (Number.isFinite(value)) {
361
+ parsed.traceabilityMinHits = value;
362
+ }
363
+ i += 1;
364
+ continue;
365
+ }
366
+ if (arg === '--traceability-max-files-per-test' && next) {
367
+ const value = Number(next);
368
+ if (Number.isFinite(value)) {
369
+ parsed.traceabilityMaxFilesPerTest = value;
370
+ }
371
+ i += 1;
372
+ continue;
373
+ }
374
+ if (arg === '--traceability-max-age-days' && next) {
375
+ const value = Number(next);
376
+ if (Number.isFinite(value)) {
377
+ parsed.traceabilityMaxAgeDays = value;
378
+ }
379
+ i += 1;
380
+ continue;
381
+ }
382
+ if (arg === '--branch' && next) {
383
+ parsed.branch = next;
384
+ i += 1;
385
+ continue;
386
+ }
387
+ if (arg === '--commit-message' && next) {
388
+ parsed.commitMessage = next;
389
+ i += 1;
390
+ continue;
391
+ }
392
+ if (arg === '--create-pr') {
393
+ parsed.createPr = true;
394
+ continue;
395
+ }
396
+ if (arg === '--pr-title' && next) {
397
+ parsed.prTitle = next;
398
+ i += 1;
399
+ continue;
400
+ }
401
+ if (arg === '--pr-body' && next) {
402
+ parsed.prBody = next;
403
+ i += 1;
404
+ continue;
405
+ }
406
+ if (arg === '--pr-base' && next) {
407
+ parsed.prBase = next;
408
+ i += 1;
409
+ continue;
410
+ }
411
+ if (arg === '--dry-run') {
412
+ parsed.dryRun = true;
413
+ continue;
414
+ }
415
+ }
416
+ return parsed;
417
+ }
418
+ async function main() {
419
+ const args = parseArgs(process.argv.slice(2));
420
+ const autoConfig = resolveAutoConfig(args);
421
+ if (args.help || !args.command) {
422
+ printUsage();
423
+ process.exit(args.command ? 0 : 1);
424
+ }
425
+ if (args.command === 'llm-health') {
426
+ await runLlmHealth();
427
+ return;
428
+ }
429
+ if (args.command === 'feedback') {
430
+ if (!args.path && !autoConfig) {
431
+ // eslint-disable-next-line no-console
432
+ console.error('Error: --path is required for feedback command');
433
+ process.exit(1);
434
+ }
435
+ if (!args.feedbackInputPath) {
436
+ // eslint-disable-next-line no-console
437
+ console.error('Error: --feedback-input <path> is required for feedback command');
438
+ process.exit(1);
439
+ }
440
+ const { config } = (0, config_js_1.resolveConfig)(process.cwd(), autoConfig, {
441
+ path: args.path,
442
+ testsRoot: args.testsRoot,
443
+ mode: 'impact',
444
+ });
445
+ const reportRoot = config.testsRoot || config.path;
446
+ const raw = JSON.parse((0, fs_1.readFileSync)(args.feedbackInputPath, 'utf-8'));
447
+ const payload = {
448
+ timestamp: raw.timestamp || new Date().toISOString(),
449
+ runSet: raw.runSet || 'targeted',
450
+ recommendedTests: raw.recommendedTests || [],
451
+ executedTests: raw.executedTests || [],
452
+ failedTests: raw.failedTests || [],
453
+ escapedFailures: raw.escapedFailures || [],
454
+ };
455
+ const output = (0, feedback_js_1.appendFeedbackAndRecompute)(reportRoot, payload);
456
+ // eslint-disable-next-line no-console
457
+ console.log(`Feedback data: ${output.feedbackPath}`);
458
+ // eslint-disable-next-line no-console
459
+ console.log(`Calibration data: ${output.calibrationPath}`);
460
+ // eslint-disable-next-line no-console
461
+ console.log(`Calibration overall: precision=${output.calibration.overall.precision}, recall=${output.calibration.overall.recall}, fnr=${output.calibration.overall.falseNegativeRate}`);
462
+ return;
463
+ }
464
+ if (args.command === 'traceability-capture') {
465
+ if (!args.path && !autoConfig) {
466
+ // eslint-disable-next-line no-console
467
+ console.error('Error: --path is required for traceability-capture command');
468
+ process.exit(1);
469
+ }
470
+ if (!args.traceabilityReportPath) {
471
+ // eslint-disable-next-line no-console
472
+ console.error('Error: --traceability-report <path> is required for traceability-capture command');
473
+ process.exit(1);
474
+ }
475
+ const { config } = (0, config_js_1.resolveConfig)(process.cwd(), autoConfig, {
476
+ path: args.path,
477
+ testsRoot: args.testsRoot,
478
+ mode: 'impact',
479
+ gitSince: args.gitSince,
480
+ });
481
+ const reportRoot = config.testsRoot || config.path;
482
+ const output = (0, traceability_capture_js_1.captureTraceabilityInput)({
483
+ appPath: config.path,
484
+ testsRoot: reportRoot,
485
+ reportPath: args.traceabilityReportPath,
486
+ sinceRef: args.gitSince || config.git.since,
487
+ outputPath: args.traceabilityCaptureOutputPath,
488
+ coverageMapPath: args.traceabilityCoverageMapPath,
489
+ changedFilesPath: args.traceabilityChangedFilesPath,
490
+ });
491
+ // eslint-disable-next-line no-console
492
+ console.log(`Traceability input: ${output.outputPath}`);
493
+ // eslint-disable-next-line no-console
494
+ console.log(`Traceability tests seen: ${output.testsSeen}`);
495
+ // eslint-disable-next-line no-console
496
+ console.log(`Traceability runs generated: ${output.runsGenerated}`);
497
+ // eslint-disable-next-line no-console
498
+ console.log(`Traceability changed files used: ${output.changedFilesUsed}`);
499
+ if (output.warnings.length > 0) {
500
+ // eslint-disable-next-line no-console
501
+ console.log(`Traceability warnings: ${output.warnings.join(' | ')}`);
502
+ }
503
+ return;
504
+ }
505
+ if (args.command === 'traceability-ingest') {
506
+ if (!args.path && !autoConfig) {
507
+ // eslint-disable-next-line no-console
508
+ console.error('Error: --path is required for traceability-ingest command');
509
+ process.exit(1);
510
+ }
511
+ if (!args.traceabilityInputPath) {
512
+ // eslint-disable-next-line no-console
513
+ console.error('Error: --traceability-input <path> is required for traceability-ingest command');
514
+ process.exit(1);
515
+ }
516
+ const { config } = (0, config_js_1.resolveConfig)(process.cwd(), autoConfig, {
517
+ path: args.path,
518
+ testsRoot: args.testsRoot,
519
+ mode: 'impact',
520
+ });
521
+ const reportRoot = config.testsRoot || config.path;
522
+ const raw = JSON.parse((0, fs_1.readFileSync)(args.traceabilityInputPath, 'utf-8'));
523
+ const output = (0, traceability_ingest_js_1.ingestTraceabilityInput)(reportRoot, config.impact.traceability, raw, {
524
+ minHits: args.traceabilityMinHits,
525
+ maxFilesPerTest: args.traceabilityMaxFilesPerTest,
526
+ maxAgeDays: args.traceabilityMaxAgeDays,
527
+ });
528
+ // eslint-disable-next-line no-console
529
+ console.log(`Traceability manifest: ${output.manifestPath}`);
530
+ // eslint-disable-next-line no-console
531
+ console.log(`Traceability state: ${output.statePath}`);
532
+ // eslint-disable-next-line no-console
533
+ console.log(`Traceability ingested entries: ${output.entriesIngested}`);
534
+ // eslint-disable-next-line no-console
535
+ console.log(`Traceability tracked tests: ${output.testsTracked}`);
536
+ // eslint-disable-next-line no-console
537
+ console.log(`Traceability tracked edges: ${output.edgesTracked}`);
538
+ if (output.warnings.length > 0) {
539
+ // eslint-disable-next-line no-console
540
+ console.log(`Traceability warnings: ${output.warnings.join(' | ')}`);
541
+ }
542
+ return;
543
+ }
544
+ if (args.command === 'finalize-generated-tests') {
545
+ if (!args.path && !autoConfig) {
546
+ // eslint-disable-next-line no-console
547
+ console.error('Error: --path is required for finalize-generated-tests command');
548
+ process.exit(1);
549
+ }
550
+ const { config } = (0, config_js_1.resolveConfig)(process.cwd(), autoConfig, {
551
+ path: args.path,
552
+ testsRoot: args.testsRoot,
553
+ mode: 'gap',
554
+ });
555
+ const result = (0, handoff_js_1.finalizeGeneratedTests)({
556
+ appPath: config.path,
557
+ testsRoot: config.testsRoot || config.path,
558
+ branch: args.branch,
559
+ commitMessage: args.commitMessage,
560
+ createPr: args.createPr,
561
+ prTitle: args.prTitle,
562
+ prBody: args.prBody,
563
+ baseBranch: args.prBase,
564
+ dryRun: args.dryRun,
565
+ });
566
+ // eslint-disable-next-line no-console
567
+ console.log(`Finalize repo root: ${result.repoRoot}`);
568
+ // eslint-disable-next-line no-console
569
+ console.log(`Finalize branch: ${result.branch}`);
570
+ // eslint-disable-next-line no-console
571
+ console.log(`Finalize staged paths: ${result.stagedPaths.join(', ') || 'none'}`);
572
+ // eslint-disable-next-line no-console
573
+ console.log(`Finalize commit: ${result.committed ? 'created' : 'skipped'}`);
574
+ if (result.commitSha) {
575
+ // eslint-disable-next-line no-console
576
+ console.log(`Finalize commit sha: ${result.commitSha}`);
577
+ }
578
+ if (result.prUrl) {
579
+ // eslint-disable-next-line no-console
580
+ console.log(`Finalize PR: ${result.prUrl}`);
581
+ }
582
+ return;
583
+ }
584
+ if (args.command === 'auto-heal-pr') {
585
+ if (!args.path && !autoConfig) {
586
+ // eslint-disable-next-line no-console
587
+ console.error('Error: --path is required for auto-heal-pr command');
588
+ process.exit(1);
589
+ }
590
+ const { config } = (0, config_js_1.resolveConfig)(process.cwd(), autoConfig, {
591
+ path: args.path,
592
+ testsRoot: args.testsRoot,
593
+ mode: 'gap',
594
+ framework: args.framework,
595
+ timeLimitMinutes: args.timeLimitMinutes,
596
+ budget: {
597
+ maxUSD: args.budgetUSD,
598
+ maxTokens: args.budgetTokens,
599
+ },
600
+ testPatterns: args.testPatterns,
601
+ flowPatterns: args.flowPatterns,
602
+ flowExclude: args.flowExclude,
603
+ flowCatalogPath: args.flowCatalogPath,
604
+ specPDF: args.specPDF,
605
+ gitSince: args.gitSince,
606
+ pipeline: {
607
+ enabled: true,
608
+ scenarios: args.pipelineScenarios,
609
+ outputDir: args.pipelineOutput,
610
+ baseUrl: args.pipelineBaseUrl,
611
+ browser: args.pipelineBrowser,
612
+ headless: args.pipelineHeadless,
613
+ project: args.pipelineProject,
614
+ parallel: args.pipelineParallel,
615
+ dryRun: args.pipelineDryRun,
616
+ mcp: args.pipelineMcp,
617
+ },
618
+ });
619
+ if (args.allowFallback) {
620
+ config.impact.allowFallback = true;
621
+ }
622
+ await (0, runner_js_1.runGap)(config, { apply: true });
623
+ const reportRoot = config.testsRoot || config.path;
624
+ if (args.traceabilityReportPath) {
625
+ const unstableSpecs = (0, playwright_report_js_1.extractPlaywrightUnstableSpecs)(args.traceabilityReportPath, [reportRoot, config.path]);
626
+ if (unstableSpecs.length > 0) {
627
+ const targetedSummary = (0, pipeline_js_1.runTargetedSpecHeal)(reportRoot, unstableSpecs.map((spec) => ({
628
+ specPath: spec.specPath,
629
+ status: spec.status,
630
+ reason: `Playwright report: failingTests=${spec.failingTests}, flakyTests=${spec.flakyTests}`,
631
+ })), {
632
+ ...config.pipeline,
633
+ enabled: true,
634
+ heal: true,
635
+ });
636
+ const healedCount = targetedSummary.results.filter((result) => result.healStatus === 'success').length;
637
+ // eslint-disable-next-line no-console
638
+ console.log(`Auto-heal targeted unstable specs: ${unstableSpecs.length} (healed=${healedCount})`);
639
+ if (targetedSummary.warnings.length > 0) {
640
+ // eslint-disable-next-line no-console
641
+ console.log(`Auto-heal warnings: ${targetedSummary.warnings.join(' | ')}`);
642
+ }
643
+ const gapPath = (0, path_1.join)(reportRoot, '.e2e-ai-agents', 'gap.json');
644
+ if ((0, fs_1.existsSync)(gapPath)) {
645
+ const gap = JSON.parse((0, fs_1.readFileSync)(gapPath, 'utf-8'));
646
+ const existingResults = Array.isArray(gap.pipeline?.results) ? gap.pipeline?.results : [];
647
+ const existingWarnings = Array.isArray(gap.pipeline?.warnings) ? gap.pipeline?.warnings : [];
648
+ gap.pipeline = {
649
+ runner: gap.pipeline?.runner || targetedSummary.runner,
650
+ results: [...existingResults, ...targetedSummary.results],
651
+ warnings: Array.from(new Set([...(existingWarnings || []), ...targetedSummary.warnings])),
652
+ };
653
+ (0, fs_1.writeFileSync)(gapPath, `${JSON.stringify(gap, null, 2)}\n`, 'utf-8');
654
+ }
655
+ }
656
+ else {
657
+ // eslint-disable-next-line no-console
658
+ console.log('Auto-heal targeted unstable specs: 0');
659
+ }
660
+ }
661
+ const branchSuffix = new Date().toISOString().replace(/[:.]/g, '-');
662
+ const result = (0, handoff_js_1.finalizeGeneratedTests)({
663
+ appPath: config.path,
664
+ testsRoot: reportRoot,
665
+ branch: args.branch || `auto-heal-${branchSuffix}`,
666
+ commitMessage: args.commitMessage || 'test(e2e): auto-heal generated specs',
667
+ createPr: true,
668
+ prTitle: args.prTitle || 'test(e2e): auto-heal generated specs',
669
+ prBody: args.prBody || 'Automated e2e-heal run generated by @yasserkhanorg/e2e-agents.',
670
+ baseBranch: args.prBase || 'master',
671
+ dryRun: args.dryRun,
672
+ });
673
+ // eslint-disable-next-line no-console
674
+ console.log(`Auto-heal repo root: ${result.repoRoot}`);
675
+ // eslint-disable-next-line no-console
676
+ console.log(`Auto-heal branch: ${result.branch}`);
677
+ // eslint-disable-next-line no-console
678
+ console.log(`Auto-heal staged paths: ${result.stagedPaths.join(', ') || 'none'}`);
679
+ // eslint-disable-next-line no-console
680
+ console.log(`Auto-heal commit: ${result.committed ? 'created' : 'skipped'}`);
681
+ if (result.commitSha) {
682
+ // eslint-disable-next-line no-console
683
+ console.log(`Auto-heal commit sha: ${result.commitSha}`);
684
+ }
685
+ if (result.prUrl) {
686
+ // eslint-disable-next-line no-console
687
+ console.log(`Auto-heal PR: ${result.prUrl}`);
688
+ }
689
+ return;
690
+ }
691
+ if (!args.path && !autoConfig) {
692
+ // eslint-disable-next-line no-console
693
+ console.error('Error: --path is required (or provide a config file with path set)');
694
+ printUsage();
695
+ process.exit(1);
696
+ }
697
+ const forcePipelineFromApproval = args.command === 'approve-and-generate';
698
+ const { config } = (0, config_js_1.resolveConfig)(process.cwd(), autoConfig, {
699
+ path: args.path,
700
+ testsRoot: args.testsRoot,
701
+ mode: (args.command === 'gap' || args.command === 'approve-and-generate') ? 'gap' : 'impact',
702
+ framework: args.framework,
703
+ timeLimitMinutes: args.timeLimitMinutes,
704
+ budget: {
705
+ maxUSD: args.budgetUSD,
706
+ maxTokens: args.budgetTokens,
707
+ },
708
+ testPatterns: args.testPatterns,
709
+ flowPatterns: args.flowPatterns,
710
+ flowExclude: args.flowExclude,
711
+ flowCatalogPath: args.flowCatalogPath,
712
+ specPDF: args.specPDF,
713
+ gitSince: args.gitSince,
714
+ pipeline: (args.pipeline || forcePipelineFromApproval)
715
+ ? {
716
+ enabled: true,
717
+ scenarios: args.pipelineScenarios,
718
+ outputDir: args.pipelineOutput,
719
+ baseUrl: args.pipelineBaseUrl,
720
+ browser: args.pipelineBrowser,
721
+ headless: args.pipelineHeadless,
722
+ project: args.pipelineProject,
723
+ parallel: args.pipelineParallel,
724
+ dryRun: args.pipelineDryRun,
725
+ mcp: args.pipelineMcp !== undefined ? args.pipelineMcp : forcePipelineFromApproval,
726
+ }
727
+ : undefined,
728
+ policy: args.policyMinConfidence !== undefined ||
729
+ args.policySafeMergeConfidence !== undefined ||
730
+ args.policyWarningsThreshold !== undefined ||
731
+ (args.policyRiskyPatterns && args.policyRiskyPatterns.length > 0)
732
+ ? {
733
+ minConfidenceForTargeted: args.policyMinConfidence,
734
+ safeMergeMinConfidence: args.policySafeMergeConfidence,
735
+ forceFullOnWarningsAtOrAbove: args.policyWarningsThreshold,
736
+ riskyFilePatterns: args.policyRiskyPatterns,
737
+ }
738
+ : undefined,
739
+ });
740
+ if (args.allowFallback) {
741
+ config.impact.allowFallback = true;
742
+ }
743
+ if (args.command === 'impact') {
744
+ await (0, runner_js_1.runImpact)(config, { apply: args.apply });
745
+ return;
746
+ }
747
+ if (args.command === 'suggest') {
748
+ await (0, runner_js_1.runImpact)(config, { apply: args.apply });
749
+ const reportRoot = config.testsRoot || config.path;
750
+ const impactPath = (0, path_1.join)(reportRoot, '.e2e-ai-agents', 'impact.json');
751
+ if (!(0, fs_1.existsSync)(impactPath)) {
752
+ throw new Error(`Impact report not found at ${impactPath}`);
753
+ }
754
+ const impact = JSON.parse((0, fs_1.readFileSync)(impactPath, 'utf-8'));
755
+ const basePlan = (0, plan_js_1.buildPlanFromImpactReport)(impact, config.policy);
756
+ const withActions = (0, plan_js_1.attachDeveloperActions)(basePlan, {
757
+ appPath: config.path,
758
+ testsRoot: reportRoot,
759
+ sinceRef: config.git.since,
760
+ });
761
+ const plan = (0, operational_insights_js_1.applyOperationalInsights)(withActions, reportRoot);
762
+ const planPath = (0, plan_js_1.writePlanReport)(reportRoot, plan);
763
+ const summaryMarkdown = (0, plan_js_1.renderCiSummaryMarkdown)(plan);
764
+ const summaryPath = (0, plan_js_1.writeCiSummary)(reportRoot, summaryMarkdown, args.ciCommentPath);
765
+ const ghaOutput = args.githubOutputPath || process.env.GITHUB_OUTPUT;
766
+ if (ghaOutput) {
767
+ (0, fs_1.appendFileSync)(ghaOutput, `run_set=${plan.runSet}\n`);
768
+ (0, fs_1.appendFileSync)(ghaOutput, `action=${plan.decision.action}\n`);
769
+ (0, fs_1.appendFileSync)(ghaOutput, `confidence=${plan.confidence}\n`);
770
+ (0, fs_1.appendFileSync)(ghaOutput, `recommended_tests_count=${plan.recommendedTests.length}\n`);
771
+ (0, fs_1.appendFileSync)(ghaOutput, `required_new_tests_count=${plan.requiredNewTests.length}\n`);
772
+ (0, fs_1.appendFileSync)(ghaOutput, `plan_path=${planPath}\n`);
773
+ (0, fs_1.appendFileSync)(ghaOutput, `summary_path=${summaryPath}\n`);
774
+ }
775
+ // eslint-disable-next-line no-console
776
+ console.log(`Suggested run set: ${plan.runSet} (confidence ${plan.confidence})`);
777
+ // eslint-disable-next-line no-console
778
+ console.log(`Decision: ${plan.decision.action} - ${plan.decision.summary}`);
779
+ // eslint-disable-next-line no-console
780
+ console.log(`Plan data: ${planPath}`);
781
+ // eslint-disable-next-line no-console
782
+ console.log(`CI summary: ${summaryPath}`);
783
+ if (plan.nextActions) {
784
+ // eslint-disable-next-line no-console
785
+ console.log(`Next action (run existing): ${plan.nextActions.runRecommendedTests || plan.nextActions.runSmokeSuite}`);
786
+ // eslint-disable-next-line no-console
787
+ console.log(`Next action (approve + generate): ${plan.nextActions.approveAndGenerate || plan.nextActions.generateMissingTests}`);
788
+ // eslint-disable-next-line no-console
789
+ console.log(`Next action (heal): ${plan.nextActions.healGeneratedTests}`);
790
+ }
791
+ if (args.failOnMustAddTests && plan.decision.action === 'must-add-tests') {
792
+ process.exit(2);
793
+ }
794
+ return;
795
+ }
796
+ if (args.command === 'approve-and-generate') {
797
+ await (0, runner_js_1.runGap)(config, { apply: true });
798
+ return;
799
+ }
800
+ await (0, runner_js_1.runGap)(config, { apply: args.apply });
801
+ }
802
+ async function runLlmHealth() {
803
+ if (!process.env.ANTHROPIC_API_KEY) {
804
+ // eslint-disable-next-line no-console
805
+ console.error('ANTHROPIC_API_KEY is required for llm-health.');
806
+ process.exit(1);
807
+ }
808
+ const model = process.env.ANTHROPIC_MODEL || 'claude-sonnet-4-5-20250929';
809
+ const provider = new anthropic_provider_js_1.AnthropicProvider({
810
+ apiKey: process.env.ANTHROPIC_API_KEY,
811
+ model,
812
+ });
813
+ try {
814
+ const response = await provider.generateText('Reply with OK.', { maxTokens: 8, timeout: 15000 });
815
+ const text = response.text.trim();
816
+ // eslint-disable-next-line no-console
817
+ console.log(`Anthropic OK (${model}) -> ${text}`);
818
+ }
819
+ catch (error) {
820
+ if (error instanceof provider_interface_js_1.LLMProviderError) {
821
+ // eslint-disable-next-line no-console
822
+ console.error(`Anthropic failed: ${error.message}`);
823
+ if (error.cause instanceof Error) {
824
+ // eslint-disable-next-line no-console
825
+ console.error(`Cause: ${error.cause.message}`);
826
+ }
827
+ }
828
+ else if (error instanceof Error) {
829
+ // eslint-disable-next-line no-console
830
+ console.error(`Anthropic failed: ${error.message}`);
831
+ }
832
+ else {
833
+ // eslint-disable-next-line no-console
834
+ console.error(`Anthropic failed: ${String(error)}`);
835
+ }
836
+ process.exit(1);
837
+ }
838
+ }
839
+ main().catch((error) => {
840
+ // eslint-disable-next-line no-console
841
+ console.error(error instanceof Error ? error.message : String(error));
842
+ process.exit(1);
843
+ });