@yasserkhanorg/e2e-agents 0.9.0 → 0.11.0

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 (93) hide show
  1. package/README.md +112 -584
  2. package/dist/agent/api_catalog.d.ts +11 -0
  3. package/dist/agent/api_catalog.d.ts.map +1 -0
  4. package/dist/agent/api_catalog.js +210 -0
  5. package/dist/agent/llm_agents_flow.d.ts +15 -0
  6. package/dist/agent/llm_agents_flow.d.ts.map +1 -0
  7. package/dist/agent/llm_agents_flow.js +434 -0
  8. package/dist/agent/native_flow.d.ts +6 -0
  9. package/dist/agent/native_flow.d.ts.map +1 -0
  10. package/dist/agent/native_flow.js +179 -0
  11. package/dist/agent/pipeline.d.ts +2 -25
  12. package/dist/agent/pipeline.d.ts.map +1 -1
  13. package/dist/agent/pipeline.js +30 -1329
  14. package/dist/agent/pipeline_types.d.ts +54 -0
  15. package/dist/agent/pipeline_types.d.ts.map +1 -0
  16. package/dist/agent/pipeline_types.js +4 -0
  17. package/dist/agent/pipeline_utils.d.ts +12 -0
  18. package/dist/agent/pipeline_utils.d.ts.map +1 -0
  19. package/dist/agent/pipeline_utils.js +156 -0
  20. package/dist/agent/process_runner.d.ts +10 -0
  21. package/dist/agent/process_runner.d.ts.map +1 -0
  22. package/dist/agent/process_runner.js +92 -0
  23. package/dist/agent/spec_generator.d.ts +5 -0
  24. package/dist/agent/spec_generator.d.ts.map +1 -0
  25. package/dist/agent/spec_generator.js +253 -0
  26. package/dist/agent/validation_runner.d.ts +5 -0
  27. package/dist/agent/validation_runner.d.ts.map +1 -0
  28. package/dist/agent/validation_runner.js +77 -0
  29. package/dist/agentic/playwright_runner.js +1 -1
  30. package/dist/cli/commands/analyze.d.ts +3 -0
  31. package/dist/cli/commands/analyze.d.ts.map +1 -0
  32. package/dist/cli/commands/analyze.js +77 -0
  33. package/dist/cli/commands/feedback.d.ts +3 -0
  34. package/dist/cli/commands/feedback.d.ts.map +1 -0
  35. package/dist/cli/commands/feedback.js +39 -0
  36. package/dist/cli/commands/finalize.d.ts +3 -0
  37. package/dist/cli/commands/finalize.d.ts.map +1 -0
  38. package/dist/cli/commands/finalize.js +41 -0
  39. package/dist/cli/commands/generate.d.ts +4 -0
  40. package/dist/cli/commands/generate.d.ts.map +1 -0
  41. package/dist/cli/commands/generate.js +108 -0
  42. package/dist/cli/commands/heal.d.ts +3 -0
  43. package/dist/cli/commands/heal.d.ts.map +1 -0
  44. package/dist/cli/commands/heal.js +60 -0
  45. package/dist/cli/commands/impact.d.ts +4 -0
  46. package/dist/cli/commands/impact.d.ts.map +1 -0
  47. package/dist/cli/commands/impact.js +26 -0
  48. package/dist/cli/commands/llm_health.d.ts +2 -0
  49. package/dist/cli/commands/llm_health.d.ts.map +1 -0
  50. package/dist/cli/commands/llm_health.js +38 -0
  51. package/dist/cli/commands/plan.d.ts +4 -0
  52. package/dist/cli/commands/plan.d.ts.map +1 -0
  53. package/dist/cli/commands/plan.js +83 -0
  54. package/dist/cli/commands/traceability.d.ts +4 -0
  55. package/dist/cli/commands/traceability.d.ts.map +1 -0
  56. package/dist/cli/commands/traceability.js +77 -0
  57. package/dist/cli/parse_args.d.ts +6 -0
  58. package/dist/cli/parse_args.d.ts.map +1 -0
  59. package/dist/cli/parse_args.js +216 -0
  60. package/dist/cli/types.d.ts +70 -0
  61. package/dist/cli/types.d.ts.map +1 -0
  62. package/dist/cli/types.js +4 -0
  63. package/dist/cli/usage.d.ts +2 -0
  64. package/dist/cli/usage.d.ts.map +1 -0
  65. package/dist/cli/usage.js +86 -0
  66. package/dist/cli.js +26 -1057
  67. package/dist/esm/agent/api_catalog.js +199 -0
  68. package/dist/esm/agent/llm_agents_flow.js +421 -0
  69. package/dist/esm/agent/native_flow.js +175 -0
  70. package/dist/esm/agent/pipeline.js +8 -1307
  71. package/dist/esm/agent/pipeline_types.js +3 -0
  72. package/dist/esm/agent/pipeline_utils.js +146 -0
  73. package/dist/esm/agent/process_runner.js +83 -0
  74. package/dist/esm/agent/spec_generator.js +249 -0
  75. package/dist/esm/agent/validation_runner.js +73 -0
  76. package/dist/esm/agentic/playwright_runner.js +1 -1
  77. package/dist/esm/cli/commands/analyze.js +74 -0
  78. package/dist/esm/cli/commands/feedback.js +36 -0
  79. package/dist/esm/cli/commands/finalize.js +38 -0
  80. package/dist/esm/cli/commands/generate.js +105 -0
  81. package/dist/esm/cli/commands/heal.js +57 -0
  82. package/dist/esm/cli/commands/impact.js +23 -0
  83. package/dist/esm/cli/commands/llm_health.js +35 -0
  84. package/dist/esm/cli/commands/plan.js +80 -0
  85. package/dist/esm/cli/commands/traceability.js +73 -0
  86. package/dist/esm/cli/parse_args.js +210 -0
  87. package/dist/esm/cli/types.js +3 -0
  88. package/dist/esm/cli/usage.js +83 -0
  89. package/dist/esm/cli.js +20 -1051
  90. package/dist/esm/mcp-server.js +18 -1
  91. package/dist/mcp-server.d.ts.map +1 -1
  92. package/dist/mcp-server.js +17 -0
  93. package/package.json +2 -4
package/dist/cli.js CHANGED
@@ -3,840 +3,56 @@
3
3
  // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
4
4
  // See LICENSE.txt for license information.
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- const fs_1 = require("fs");
7
- const path_1 = require("path");
8
6
  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 impact_engine_js_1 = require("./engine/impact_engine.js");
12
- const plan_builder_js_1 = require("./engine/plan_builder.js");
13
- const git_js_1 = require("./agent/git.js");
14
- const api_js_1 = require("./api.js");
15
- const feedback_js_1 = require("./agent/feedback.js");
16
- const handoff_js_1 = require("./agent/handoff.js");
17
- const traceability_ingest_js_1 = require("./agent/traceability_ingest.js");
18
- const traceability_capture_js_1 = require("./agent/traceability_capture.js");
19
- const pipeline_js_1 = require("./agent/pipeline.js");
20
- const playwright_report_js_1 = require("./agent/playwright_report.js");
21
- const orchestrator_js_1 = require("./pipeline/orchestrator.js");
22
- const provider_factory_js_1 = require("./provider_factory.js");
23
- const runner_js_1 = require("./agentic/runner.js");
24
- const api_surface_js_1 = require("./knowledge/api_surface.js");
25
- const CONFIG_CANDIDATES = ['e2e-ai-agents.config.json', '.e2e-ai-agents.config.json'];
26
- function findConfigUpwards(startDir) {
27
- if (!startDir) {
28
- return undefined;
29
- }
30
- let current = (0, path_1.resolve)(startDir);
31
- while (true) {
32
- for (const candidate of CONFIG_CANDIDATES) {
33
- const fullPath = (0, path_1.join)(current, candidate);
34
- if ((0, fs_1.existsSync)(fullPath)) {
35
- return fullPath;
36
- }
37
- }
38
- const parent = (0, path_1.dirname)(current);
39
- if (parent === current) {
40
- break;
41
- }
42
- current = parent;
43
- }
44
- return undefined;
45
- }
46
- function resolveAutoConfig(args) {
47
- if (args.configPath) {
48
- return args.configPath;
49
- }
50
- const searchRoots = [
51
- process.cwd(),
52
- args.testsRoot,
53
- args.path,
54
- ].filter(Boolean);
55
- for (const root of searchRoots) {
56
- const found = findConfigUpwards(root);
57
- if (found) {
58
- return found;
59
- }
60
- }
61
- return undefined;
62
- }
63
- function printUsage() {
64
- // eslint-disable-next-line no-console
65
- console.log([
66
- 'Usage:',
67
- ' e2e-ai-agents impact --path <app-root> [options]',
68
- ' e2e-ai-agents plan --path <app-root> [options]',
69
- ' e2e-ai-agents suggest --path <app-root> [options]',
70
- ' e2e-ai-agents heal --path <app-root> --traceability-report <json> [options]',
71
- ' e2e-ai-agents finalize-generated-tests --path <app-root> [options]',
72
- ' e2e-ai-agents feedback --path <app-root> --feedback-input <json>',
73
- ' e2e-ai-agents traceability-capture --path <app-root> --traceability-report <json>',
74
- ' e2e-ai-agents traceability-ingest --path <app-root> --traceability-input <json>',
75
- ' e2e-ai-agents generate [--scenarios <path|json>] [--max-attempts <n>] [--dry-run]',
76
- ' e2e-ai-agents analyze --path <app-root> [--tests-root <path>] [--since <ref>] [--generate] [--generate-output <dir>] [--heal] [--heal-report <json>]',
77
- ' e2e-ai-agents llm-health',
78
- '',
79
- 'Options:',
80
- ' --config <path> Path to e2e-ai-agents.config.json (auto-discovered if present)',
81
- ' --path <app-root> Path to the web app (required)',
82
- ' --profile <name> default | mattermost',
83
- ' --mattermost Shortcut for --profile mattermost',
84
- ' --tests-root <path> Path to tests root (optional)',
85
- ' --framework <name> auto | playwright | cypress | selenium',
86
- ' --patterns <globs> Comma-separated test patterns',
87
- ' --flow-patterns <g> Comma-separated flow discovery patterns',
88
- ' --flow-exclude <g> Comma-separated flow exclude patterns',
89
- ' --flow-catalog <path> Path to flow catalog JSON',
90
- ' --allow-fallback Allow impact analysis without diff',
91
- ' --pipeline Run Playwright AI pipeline for missing P0/P1 flows',
92
- ' --pipeline-scenarios Number of scenarios per flow (default 3)',
93
- ' --pipeline-output Output directory for generated tests',
94
- ' --pipeline-base-url Base URL for Playwright runs',
95
- ' --pipeline-browser Browser: chrome|chromium|firefox|webkit',
96
- ' --pipeline-headless Run in headless mode',
97
- ' --pipeline-headed Run in headed mode',
98
- ' --pipeline-project Playwright project name',
99
- ' --pipeline-parallel Enable parallel mode in generator',
100
- ' --pipeline-dry-run Do not execute pipeline (report only)',
101
- ' --pipeline-mcp Use Playwright MCP server for exploration/healing',
102
- ' --pipeline-mcp-allow-fallback Allow non-MCP fallback if official MCP setup fails',
103
- ' --pipeline-mcp-only Require MCP for UI exploration (fail if unavailable)',
104
- ' --pipeline-mcp-timeout-ms <n> Timeout per MCP CLI invocation in milliseconds',
105
- ' --pipeline-mcp-retries <n> Retry count for retryable MCP CLI failures',
106
- ' --spec <path> Optional spec PDF for context',
107
- ' --since <git-ref> Git ref for impact analysis (default HEAD~1)',
108
- ' --time <minutes> Time limit in minutes',
109
- ' --budget-usd <amount> Max LLM budget in USD',
110
- ' --budget-tokens <n> Max LLM tokens',
111
- ' --llm-provider <name> LLM provider: auto | anthropic | openai | ollama',
112
- ' --policy-min-confidence <n> Minimum confidence for targeted suite',
113
- ' --policy-safe-merge-confidence <n> Confidence needed for safe-to-merge',
114
- ' --policy-force-full-on-warnings <n> Escalate to full at warning count',
115
- ' --policy-risky-patterns <globs> Comma-separated risky file globs',
116
- ' --policy-enforcement-mode <mode> advisory | warn | block',
117
- ' --policy-block-actions <actions> Comma-separated CI actions to block/warn',
118
- ' --ci-comment-path <path> Write CI markdown summary',
119
- ' --github-output <path> Write GitHub Actions outputs',
120
- ' --fail-on-must-add-tests Exit non-zero on must-add-tests decision',
121
- ' --feedback-input <path> Path to recommendation feedback JSON',
122
- ' --traceability-report <path> Path to Playwright JSON report for traceability capture',
123
- ' --traceability-capture-output <path> Output path for generated traceability ingest JSON',
124
- ' --traceability-coverage-map <path> Optional coverage map (test<->files) to enrich traceability capture',
125
- ' --traceability-changed-files <path> Optional changed-files list/JSON fallback for traceability capture',
126
- ' --traceability-input <path> Path to traceability ingest JSON payload',
127
- ' --traceability-min-hits <n> Minimum signal hits required per file mapping',
128
- ' --traceability-max-files-per-test <n> Cap max mapped files per test',
129
- ' --traceability-max-age-days <n> Drop stale mappings older than N days',
130
- ' --branch <name> Optional handoff branch (prefixed with codex/)',
131
- ' --commit-message <m> Commit message for finalize-generated-tests',
132
- ' --create-pr Open PR with gh after commit',
133
- ' --pr-title <title> PR title for finalize-generated-tests',
134
- ' --pr-body <body> PR body for finalize-generated-tests',
135
- ' --pr-base <branch> PR base branch for finalize-generated-tests',
136
- ' (auto-heal-pr defaults to base=master)',
137
- ' --dry-run Preview actions without mutating git state',
138
- ' --max-attempts <n> Max fix attempts per scenario (default: 3)',
139
- ' --scenarios <path|json> Scenarios file/JSON for generate command',
140
- ' --apply Apply data-testid patches and generate tests',
141
- ' (legacy shortcut; prefer approve-and-generate)',
142
- ' --help Show help',
143
- ].join('\n'));
144
- }
145
- function parseArgs(argv) {
146
- const parsed = { apply: false, help: false };
147
- if (argv.length === 0) {
148
- return parsed;
149
- }
150
- const command = argv[0];
151
- if (command === 'impact'
152
- || command === 'plan'
153
- || command === 'heal'
154
- || command === 'suggest'
155
- || command === 'generate'
156
- || command === 'finalize-generated-tests'
157
- || command === 'feedback'
158
- || command === 'traceability-capture'
159
- || command === 'traceability-ingest'
160
- || command === 'analyze'
161
- || command === 'llm-health') {
162
- parsed.command = command;
163
- }
164
- for (let i = 1; i < argv.length; i += 1) {
165
- const arg = argv[i];
166
- const next = argv[i + 1];
167
- if (arg === '--help' || arg === '-h') {
168
- parsed.help = true;
169
- continue;
170
- }
171
- if (arg === '--max-attempts' && next) {
172
- parsed.maxAttempts = parseInt(next, 10);
173
- i += 1;
174
- continue;
175
- }
176
- if (arg === '--scenarios' && next) {
177
- parsed.generateScenarios = next;
178
- i += 1;
179
- continue;
180
- }
181
- if (arg === '--apply') {
182
- parsed.apply = true;
183
- continue;
184
- }
185
- if (arg === '--config' && next) {
186
- parsed.configPath = next;
187
- i += 1;
188
- continue;
189
- }
190
- if (arg === '--path' && next) {
191
- parsed.path = next;
192
- i += 1;
193
- continue;
194
- }
195
- if (arg === '--profile' && next) {
196
- if (next === 'default' || next === 'mattermost') {
197
- parsed.profile = next;
198
- }
199
- i += 1;
200
- continue;
201
- }
202
- if (arg === '--mattermost') {
203
- parsed.profile = 'mattermost';
204
- continue;
205
- }
206
- if (arg === '--tests-root' && next) {
207
- parsed.testsRoot = next;
208
- i += 1;
209
- continue;
210
- }
211
- if (arg === '--framework' && next) {
212
- parsed.framework = next;
213
- i += 1;
214
- continue;
215
- }
216
- if (arg === '--patterns' && next) {
217
- parsed.testPatterns = next.split(',').map((value) => value.trim()).filter(Boolean);
218
- i += 1;
219
- continue;
220
- }
221
- if (arg === '--flow-patterns' && next) {
222
- parsed.flowPatterns = next.split(',').map((value) => value.trim()).filter(Boolean);
223
- i += 1;
224
- continue;
225
- }
226
- if (arg === '--flow-exclude' && next) {
227
- parsed.flowExclude = next.split(',').map((value) => value.trim()).filter(Boolean);
228
- i += 1;
229
- continue;
230
- }
231
- if (arg === '--flow-catalog' && next) {
232
- parsed.flowCatalogPath = next;
233
- i += 1;
234
- continue;
235
- }
236
- if (arg === '--allow-fallback') {
237
- parsed.allowFallback = true;
238
- continue;
239
- }
240
- if (arg === '--pipeline') {
241
- parsed.pipeline = true;
242
- continue;
243
- }
244
- if (arg === '--pipeline-mcp') {
245
- parsed.pipelineMcp = true;
246
- continue;
247
- }
248
- if (arg === '--pipeline-mcp-allow-fallback') {
249
- parsed.pipelineMcpAllowFallback = true;
250
- continue;
251
- }
252
- if (arg === '--pipeline-mcp-only') {
253
- parsed.pipelineMcpOnly = true;
254
- continue;
255
- }
256
- if (arg === '--pipeline-mcp-timeout-ms' && next) {
257
- parsed.pipelineMcpTimeoutMs = Number(next);
258
- i += 1;
259
- continue;
260
- }
261
- if (arg === '--pipeline-mcp-retries' && next) {
262
- parsed.pipelineMcpRetries = Number(next);
263
- i += 1;
264
- continue;
265
- }
266
- if (arg === '--pipeline-scenarios' && next) {
267
- const value = Number(next);
268
- if (Number.isFinite(value)) {
269
- parsed.pipelineScenarios = value;
270
- }
271
- i += 1;
272
- continue;
273
- }
274
- if (arg === '--pipeline-output' && next) {
275
- parsed.pipelineOutput = next;
276
- i += 1;
277
- continue;
278
- }
279
- if (arg === '--pipeline-base-url' && next) {
280
- parsed.pipelineBaseUrl = next;
281
- i += 1;
282
- continue;
283
- }
284
- if (arg === '--pipeline-browser' && next) {
285
- const value = next;
286
- if (value === 'chrome' || value === 'chromium' || value === 'firefox' || value === 'webkit') {
287
- parsed.pipelineBrowser = value;
288
- }
289
- i += 1;
290
- continue;
291
- }
292
- if (arg === '--pipeline-headless') {
293
- parsed.pipelineHeadless = true;
294
- continue;
295
- }
296
- if (arg === '--pipeline-headed') {
297
- parsed.pipelineHeadless = false;
298
- continue;
299
- }
300
- if (arg === '--pipeline-project' && next) {
301
- parsed.pipelineProject = next;
302
- i += 1;
303
- continue;
304
- }
305
- if (arg === '--pipeline-parallel') {
306
- parsed.pipelineParallel = true;
307
- continue;
308
- }
309
- if (arg === '--pipeline-dry-run') {
310
- parsed.pipelineDryRun = true;
311
- continue;
312
- }
313
- if (arg === '--spec' && next) {
314
- parsed.specPDF = next;
315
- i += 1;
316
- continue;
317
- }
318
- if (arg === '--since' && next) {
319
- parsed.gitSince = next;
320
- i += 1;
321
- continue;
322
- }
323
- if (arg === '--time' && next) {
324
- const value = Number(next);
325
- if (Number.isFinite(value)) {
326
- parsed.timeLimitMinutes = value;
327
- }
328
- i += 1;
329
- continue;
330
- }
331
- if (arg === '--budget-usd' && next) {
332
- const value = Number(next);
333
- if (Number.isFinite(value)) {
334
- parsed.budgetUSD = value;
335
- }
336
- i += 1;
337
- continue;
338
- }
339
- if (arg === '--budget-tokens' && next) {
340
- const value = Number(next);
341
- if (Number.isFinite(value)) {
342
- parsed.budgetTokens = value;
343
- }
344
- i += 1;
345
- continue;
346
- }
347
- if (arg === '--llm-provider' && next) {
348
- parsed.llmProvider = next;
349
- i += 1;
350
- continue;
351
- }
352
- if (arg === '--policy-min-confidence' && next) {
353
- const value = Number(next);
354
- if (Number.isFinite(value)) {
355
- parsed.policyMinConfidence = value;
356
- }
357
- i += 1;
358
- continue;
359
- }
360
- if (arg === '--policy-safe-merge-confidence' && next) {
361
- const value = Number(next);
362
- if (Number.isFinite(value)) {
363
- parsed.policySafeMergeConfidence = value;
364
- }
365
- i += 1;
366
- continue;
367
- }
368
- if (arg === '--policy-force-full-on-warnings' && next) {
369
- const value = Number(next);
370
- if (Number.isFinite(value)) {
371
- parsed.policyWarningsThreshold = value;
372
- }
373
- i += 1;
374
- continue;
375
- }
376
- if (arg === '--policy-risky-patterns' && next) {
377
- parsed.policyRiskyPatterns = next.split(',').map((value) => value.trim()).filter(Boolean);
378
- i += 1;
379
- continue;
380
- }
381
- if (arg === '--policy-enforcement-mode' && next) {
382
- if (next === 'advisory' || next === 'warn' || next === 'block') {
383
- parsed.policyEnforcementMode = next;
384
- }
385
- i += 1;
386
- continue;
387
- }
388
- if (arg === '--policy-block-actions' && next) {
389
- parsed.policyBlockActions = next
390
- .split(',')
391
- .map((value) => value.trim())
392
- .filter((value) => (value === 'run-now' || value === 'must-add-tests' || value === 'safe-to-merge'));
393
- i += 1;
394
- continue;
395
- }
396
- if (arg === '--ci-comment-path' && next) {
397
- parsed.ciCommentPath = next;
398
- i += 1;
399
- continue;
400
- }
401
- if (arg === '--github-output' && next) {
402
- parsed.githubOutputPath = next;
403
- i += 1;
404
- continue;
405
- }
406
- if (arg === '--fail-on-must-add-tests') {
407
- parsed.failOnMustAddTests = true;
408
- continue;
409
- }
410
- if (arg === '--feedback-input' && next) {
411
- parsed.feedbackInputPath = next;
412
- i += 1;
413
- continue;
414
- }
415
- if (arg === '--traceability-report' && next) {
416
- parsed.traceabilityReportPath = next;
417
- i += 1;
418
- continue;
419
- }
420
- if (arg === '--traceability-capture-output' && next) {
421
- parsed.traceabilityCaptureOutputPath = next;
422
- i += 1;
423
- continue;
424
- }
425
- if (arg === '--traceability-coverage-map' && next) {
426
- parsed.traceabilityCoverageMapPath = next;
427
- i += 1;
428
- continue;
429
- }
430
- if (arg === '--traceability-changed-files' && next) {
431
- parsed.traceabilityChangedFilesPath = next;
432
- i += 1;
433
- continue;
434
- }
435
- if (arg === '--traceability-input' && next) {
436
- parsed.traceabilityInputPath = next;
437
- i += 1;
438
- continue;
439
- }
440
- if (arg === '--traceability-min-hits' && next) {
441
- const value = Number(next);
442
- if (Number.isFinite(value)) {
443
- parsed.traceabilityMinHits = value;
444
- }
445
- i += 1;
446
- continue;
447
- }
448
- if (arg === '--traceability-max-files-per-test' && next) {
449
- const value = Number(next);
450
- if (Number.isFinite(value)) {
451
- parsed.traceabilityMaxFilesPerTest = value;
452
- }
453
- i += 1;
454
- continue;
455
- }
456
- if (arg === '--traceability-max-age-days' && next) {
457
- const value = Number(next);
458
- if (Number.isFinite(value)) {
459
- parsed.traceabilityMaxAgeDays = value;
460
- }
461
- i += 1;
462
- continue;
463
- }
464
- if (arg === '--branch' && next) {
465
- parsed.branch = next;
466
- i += 1;
467
- continue;
468
- }
469
- if (arg === '--commit-message' && next) {
470
- parsed.commitMessage = next;
471
- i += 1;
472
- continue;
473
- }
474
- if (arg === '--create-pr') {
475
- parsed.createPr = true;
476
- continue;
477
- }
478
- if (arg === '--pr-title' && next) {
479
- parsed.prTitle = next;
480
- i += 1;
481
- continue;
482
- }
483
- if (arg === '--pr-body' && next) {
484
- parsed.prBody = next;
485
- i += 1;
486
- continue;
487
- }
488
- if (arg === '--pr-base' && next) {
489
- parsed.prBase = next;
490
- i += 1;
491
- continue;
492
- }
493
- if (arg === '--dry-run') {
494
- parsed.dryRun = true;
495
- continue;
496
- }
497
- if (arg === '--generate') {
498
- parsed.analyzeGenerate = true;
499
- continue;
500
- }
501
- if (arg === '--generate-output' && next) {
502
- parsed.analyzeGenerateOutputDir = next;
503
- i += 1;
504
- continue;
505
- }
506
- if (arg === '--heal') {
507
- parsed.analyzeHeal = true;
508
- continue;
509
- }
510
- if (arg === '--heal-report' && next) {
511
- parsed.analyzeHealReport = next;
512
- i += 1;
513
- continue;
514
- }
515
- if (arg === '--no-ai') {
516
- parsed.noAi = true;
517
- continue;
518
- }
519
- }
520
- return parsed;
521
- }
7
+ const parse_args_js_1 = require("./cli/parse_args.js");
8
+ const usage_js_1 = require("./cli/usage.js");
9
+ const llm_health_js_1 = require("./cli/commands/llm_health.js");
10
+ const analyze_js_1 = require("./cli/commands/analyze.js");
11
+ const feedback_js_1 = require("./cli/commands/feedback.js");
12
+ const traceability_js_1 = require("./cli/commands/traceability.js");
13
+ const finalize_js_1 = require("./cli/commands/finalize.js");
14
+ const heal_js_1 = require("./cli/commands/heal.js");
15
+ const impact_js_1 = require("./cli/commands/impact.js");
16
+ const plan_js_1 = require("./cli/commands/plan.js");
17
+ const generate_js_1 = require("./cli/commands/generate.js");
522
18
  async function main() {
523
- const args = parseArgs(process.argv.slice(2));
524
- const autoConfig = resolveAutoConfig(args);
19
+ const args = (0, parse_args_js_1.parseArgs)(process.argv.slice(2));
20
+ const autoConfig = (0, parse_args_js_1.resolveAutoConfig)(args);
525
21
  if (args.help || !args.command) {
526
- printUsage();
22
+ (0, usage_js_1.printUsage)();
527
23
  process.exit(args.command ? 0 : 1);
528
24
  }
529
25
  if (args.command === 'llm-health') {
530
- await runLlmHealth();
26
+ await (0, llm_health_js_1.runLlmHealth)();
531
27
  return;
532
28
  }
533
29
  if (args.command === 'analyze') {
534
- if (!args.path && !autoConfig) {
535
- // eslint-disable-next-line no-console
536
- console.error('Error: --path is required for analyze command');
537
- process.exit(1);
538
- }
539
- const { config } = (0, config_js_1.resolveConfig)(process.cwd(), autoConfig, {
540
- path: args.path,
541
- profile: args.profile,
542
- testsRoot: args.testsRoot,
543
- mode: 'impact',
544
- gitSince: args.gitSince,
545
- llmProvider: args.llmProvider,
546
- });
547
- const testsRoot = config.testsRoot || config.path;
548
- const analyzeStages = [
549
- 'preprocess', 'impact', 'coverage',
550
- ];
551
- if (args.analyzeGenerate) {
552
- analyzeStages.push('generation');
553
- }
554
- if (args.analyzeHeal || args.analyzeHealReport) {
555
- analyzeStages.push('heal');
556
- }
557
- const result = await (0, orchestrator_js_1.runPipeline)({
558
- appPath: config.path,
559
- testsRoot,
560
- gitSince: args.gitSince || config.git.since,
561
- routeFamilies: config.routeFamilies,
562
- apiSurface: config.apiSurface,
563
- stages: analyzeStages,
564
- generation: args.analyzeGenerate
565
- ? {
566
- defaultOutputDir: args.analyzeGenerateOutputDir || 'specs/functional/ai-assisted',
567
- dryRun: args.dryRun,
568
- }
569
- : undefined,
570
- heal: (args.analyzeHeal || args.analyzeHealReport)
571
- ? {
572
- mcp: args.pipelineMcp ?? true,
573
- mcpAllowFallback: args.pipelineMcpAllowFallback ?? false,
574
- mcpOnly: args.pipelineMcpOnly ?? false,
575
- mcpCommandTimeoutMs: args.pipelineMcpTimeoutMs,
576
- mcpRetries: args.pipelineMcpRetries ?? 1,
577
- dryRun: args.dryRun,
578
- }
579
- : undefined,
580
- playwrightReportPath: args.analyzeHealReport,
581
- });
582
- // eslint-disable-next-line no-console
583
- console.log(`Analyze report: ${result.reportPath}`);
584
- // eslint-disable-next-line no-console
585
- console.log(`Analyze flows identified: ${result.report.summary.flowsIdentified}`);
586
- // eslint-disable-next-line no-console
587
- console.log(`Analyze flows covered: ${result.report.summary.flowsCovered}`);
588
- // eslint-disable-next-line no-console
589
- console.log(`Analyze flows uncovered: ${result.report.summary.flowsUncovered}`);
590
- // eslint-disable-next-line no-console
591
- console.log(`Analyze overall confidence: ${result.report.summary.overallConfidence}`);
592
- // eslint-disable-next-line no-console
593
- console.log(`Analyze route families: ${result.report.summary.routeFamiliesImpacted.join(', ') || 'none'}`);
594
- if (result.generated && result.generated.length > 0) {
595
- const written = result.generated.filter((g) => g.written).length;
596
- // eslint-disable-next-line no-console
597
- console.log(`Analyze generated specs: ${result.generated.length} (written=${written})`);
598
- for (const g of result.generated) {
599
- // eslint-disable-next-line no-console
600
- console.log(` ${g.mode}: ${g.specPath}`);
601
- }
602
- }
603
- if (result.healResult) {
604
- const healed = result.healResult.summary.results.filter((r) => r.healStatus === 'success').length;
605
- const healFailed = result.healResult.summary.results.filter((r) => r.healStatus === 'failed').length;
606
- // eslint-disable-next-line no-console
607
- console.log(`Analyze heal targets: ${result.healResult.targets.length} (healed=${healed}, failed=${healFailed})`);
608
- }
609
- if (result.warnings.length > 0) {
610
- // eslint-disable-next-line no-console
611
- console.log(`Analyze warnings: ${result.warnings.join(' | ')}`);
612
- }
30
+ await (0, analyze_js_1.runAnalyzeCommand)(args, autoConfig);
613
31
  return;
614
32
  }
615
33
  if (args.command === 'feedback') {
616
- if (!args.path && !autoConfig) {
617
- // eslint-disable-next-line no-console
618
- console.error('Error: --path is required for feedback command');
619
- process.exit(1);
620
- }
621
- if (!args.feedbackInputPath) {
622
- // eslint-disable-next-line no-console
623
- console.error('Error: --feedback-input <path> is required for feedback command');
624
- process.exit(1);
625
- }
626
- const { config } = (0, config_js_1.resolveConfig)(process.cwd(), autoConfig, {
627
- path: args.path,
628
- profile: args.profile,
629
- testsRoot: args.testsRoot,
630
- mode: 'impact',
631
- llmProvider: args.llmProvider,
632
- });
633
- const reportRoot = config.testsRoot || config.path;
634
- const raw = JSON.parse((0, fs_1.readFileSync)(args.feedbackInputPath, 'utf-8'));
635
- const payload = {
636
- timestamp: raw.timestamp || new Date().toISOString(),
637
- runSet: raw.runSet || 'targeted',
638
- recommendedTests: raw.recommendedTests || [],
639
- executedTests: raw.executedTests || [],
640
- failedTests: raw.failedTests || [],
641
- escapedFailures: raw.escapedFailures || [],
642
- };
643
- const output = (0, feedback_js_1.appendFeedbackAndRecompute)(reportRoot, payload);
644
- // eslint-disable-next-line no-console
645
- console.log(`Feedback data: ${output.feedbackPath}`);
646
- // eslint-disable-next-line no-console
647
- console.log(`Calibration data: ${output.calibrationPath}`);
648
- // eslint-disable-next-line no-console
649
- console.log(`Calibration overall: precision=${output.calibration.overall.precision}, recall=${output.calibration.overall.recall}, fnr=${output.calibration.overall.falseNegativeRate}`);
34
+ (0, feedback_js_1.runFeedbackCommand)(args, autoConfig);
650
35
  return;
651
36
  }
652
37
  if (args.command === 'traceability-capture') {
653
- if (!args.path && !autoConfig) {
654
- // eslint-disable-next-line no-console
655
- console.error('Error: --path is required for traceability-capture command');
656
- process.exit(1);
657
- }
658
- if (!args.traceabilityReportPath) {
659
- // eslint-disable-next-line no-console
660
- console.error('Error: --traceability-report <path> is required for traceability-capture command');
661
- process.exit(1);
662
- }
663
- const { config } = (0, config_js_1.resolveConfig)(process.cwd(), autoConfig, {
664
- path: args.path,
665
- profile: args.profile,
666
- testsRoot: args.testsRoot,
667
- mode: 'impact',
668
- gitSince: args.gitSince,
669
- llmProvider: args.llmProvider,
670
- });
671
- const reportRoot = config.testsRoot || config.path;
672
- const output = (0, traceability_capture_js_1.captureTraceabilityInput)({
673
- appPath: config.path,
674
- testsRoot: reportRoot,
675
- reportPath: args.traceabilityReportPath,
676
- sinceRef: args.gitSince || config.git.since,
677
- outputPath: args.traceabilityCaptureOutputPath,
678
- coverageMapPath: args.traceabilityCoverageMapPath,
679
- changedFilesPath: args.traceabilityChangedFilesPath,
680
- });
681
- // eslint-disable-next-line no-console
682
- console.log(`Traceability input: ${output.outputPath}`);
683
- // eslint-disable-next-line no-console
684
- console.log(`Traceability tests seen: ${output.testsSeen}`);
685
- // eslint-disable-next-line no-console
686
- console.log(`Traceability runs generated: ${output.runsGenerated}`);
687
- // eslint-disable-next-line no-console
688
- console.log(`Traceability changed files used: ${output.changedFilesUsed}`);
689
- if (output.warnings.length > 0) {
690
- // eslint-disable-next-line no-console
691
- console.log(`Traceability warnings: ${output.warnings.join(' | ')}`);
692
- }
38
+ (0, traceability_js_1.runTraceabilityCaptureCommand)(args, autoConfig);
693
39
  return;
694
40
  }
695
41
  if (args.command === 'traceability-ingest') {
696
- if (!args.path && !autoConfig) {
697
- // eslint-disable-next-line no-console
698
- console.error('Error: --path is required for traceability-ingest command');
699
- process.exit(1);
700
- }
701
- if (!args.traceabilityInputPath) {
702
- // eslint-disable-next-line no-console
703
- console.error('Error: --traceability-input <path> is required for traceability-ingest command');
704
- process.exit(1);
705
- }
706
- const { config } = (0, config_js_1.resolveConfig)(process.cwd(), autoConfig, {
707
- path: args.path,
708
- profile: args.profile,
709
- testsRoot: args.testsRoot,
710
- mode: 'impact',
711
- llmProvider: args.llmProvider,
712
- });
713
- const reportRoot = config.testsRoot || config.path;
714
- const raw = JSON.parse((0, fs_1.readFileSync)(args.traceabilityInputPath, 'utf-8'));
715
- const output = (0, traceability_ingest_js_1.ingestTraceabilityInput)(reportRoot, config.impact.traceability, raw, {
716
- minHits: args.traceabilityMinHits,
717
- maxFilesPerTest: args.traceabilityMaxFilesPerTest,
718
- maxAgeDays: args.traceabilityMaxAgeDays,
719
- });
720
- // eslint-disable-next-line no-console
721
- console.log(`Traceability manifest: ${output.manifestPath}`);
722
- // eslint-disable-next-line no-console
723
- console.log(`Traceability state: ${output.statePath}`);
724
- // eslint-disable-next-line no-console
725
- console.log(`Traceability ingested entries: ${output.entriesIngested}`);
726
- // eslint-disable-next-line no-console
727
- console.log(`Traceability tracked tests: ${output.testsTracked}`);
728
- // eslint-disable-next-line no-console
729
- console.log(`Traceability tracked edges: ${output.edgesTracked}`);
730
- if (output.warnings.length > 0) {
731
- // eslint-disable-next-line no-console
732
- console.log(`Traceability warnings: ${output.warnings.join(' | ')}`);
733
- }
42
+ (0, traceability_js_1.runTraceabilityIngestCommand)(args, autoConfig);
734
43
  return;
735
44
  }
736
45
  if (args.command === 'finalize-generated-tests') {
737
- if (!args.path && !autoConfig) {
738
- // eslint-disable-next-line no-console
739
- console.error('Error: --path is required for finalize-generated-tests command');
740
- process.exit(1);
741
- }
742
- const { config } = (0, config_js_1.resolveConfig)(process.cwd(), autoConfig, {
743
- path: args.path,
744
- profile: args.profile,
745
- testsRoot: args.testsRoot,
746
- mode: 'gap',
747
- llmProvider: args.llmProvider,
748
- });
749
- const result = (0, handoff_js_1.finalizeGeneratedTests)({
750
- appPath: config.path,
751
- testsRoot: config.testsRoot || config.path,
752
- branch: args.branch,
753
- commitMessage: args.commitMessage,
754
- createPr: args.createPr,
755
- prTitle: args.prTitle,
756
- prBody: args.prBody,
757
- baseBranch: args.prBase,
758
- dryRun: args.dryRun,
759
- });
760
- // eslint-disable-next-line no-console
761
- console.log(`Finalize repo root: ${result.repoRoot}`);
762
- // eslint-disable-next-line no-console
763
- console.log(`Finalize branch: ${result.branch}`);
764
- // eslint-disable-next-line no-console
765
- console.log(`Finalize staged paths: ${result.stagedPaths.join(', ') || 'none'}`);
766
- // eslint-disable-next-line no-console
767
- console.log(`Finalize commit: ${result.committed ? 'created' : 'skipped'}`);
768
- if (result.commitSha) {
769
- // eslint-disable-next-line no-console
770
- console.log(`Finalize commit sha: ${result.commitSha}`);
771
- }
772
- if (result.prUrl) {
773
- // eslint-disable-next-line no-console
774
- console.log(`Finalize PR: ${result.prUrl}`);
775
- }
46
+ (0, finalize_js_1.runFinalizeCommand)(args, autoConfig);
776
47
  return;
777
48
  }
778
49
  if (args.command === 'heal') {
779
- if (!args.path && !autoConfig) {
780
- // eslint-disable-next-line no-console
781
- console.error('Error: --path is required for heal command');
782
- process.exit(1);
783
- }
784
- if (!args.traceabilityReportPath) {
785
- // eslint-disable-next-line no-console
786
- console.error('Error: --traceability-report <path> is required for heal command');
787
- process.exit(1);
788
- }
789
- const { config } = (0, config_js_1.resolveConfig)(process.cwd(), autoConfig, {
790
- path: args.path,
791
- profile: args.profile,
792
- testsRoot: args.testsRoot,
793
- mode: 'gap',
794
- framework: args.framework,
795
- pipeline: {
796
- enabled: true,
797
- scenarios: args.pipelineScenarios,
798
- outputDir: args.pipelineOutput,
799
- baseUrl: args.pipelineBaseUrl,
800
- browser: args.pipelineBrowser,
801
- headless: args.pipelineHeadless,
802
- project: args.pipelineProject,
803
- parallel: args.pipelineParallel,
804
- dryRun: args.pipelineDryRun,
805
- mcp: args.pipelineMcp,
806
- mcpAllowFallback: args.pipelineMcpAllowFallback,
807
- mcpOnly: args.pipelineMcpOnly,
808
- },
809
- llmProvider: args.llmProvider,
810
- });
811
- const reportRoot = config.testsRoot || config.path;
812
- const unstableSpecs = (0, playwright_report_js_1.extractPlaywrightUnstableSpecs)(args.traceabilityReportPath, [reportRoot, config.path]);
813
- if (unstableSpecs.length === 0) {
814
- // eslint-disable-next-line no-console
815
- console.log('Heal targeted unstable specs: 0');
816
- return;
817
- }
818
- const targetedSummary = (0, pipeline_js_1.runTargetedSpecHeal)(reportRoot, unstableSpecs.map((spec) => ({
819
- specPath: spec.specPath,
820
- status: spec.status,
821
- reason: `Playwright report: failingTests=${spec.failingTests}, flakyTests=${spec.flakyTests}`,
822
- })), {
823
- ...config.pipeline,
824
- enabled: true,
825
- heal: true,
826
- });
827
- const healedCount = targetedSummary.results.filter((result) => result.healStatus === 'success').length;
828
- // eslint-disable-next-line no-console
829
- console.log(`Heal targeted unstable specs: ${unstableSpecs.length} (healed=${healedCount})`);
830
- if (targetedSummary.warnings.length > 0) {
831
- // eslint-disable-next-line no-console
832
- console.log(`Heal warnings: ${targetedSummary.warnings.join(' | ')}`);
833
- }
50
+ (0, heal_js_1.runHealCommand)(args, autoConfig);
834
51
  return;
835
52
  }
836
53
  if (!args.path && !autoConfig) {
837
- // eslint-disable-next-line no-console
838
54
  console.error('Error: --path is required (or provide a config file with path set)');
839
- printUsage();
55
+ (0, usage_js_1.printUsage)();
840
56
  process.exit(1);
841
57
  }
842
58
  const { config } = (0, config_js_1.resolveConfig)(process.cwd(), autoConfig, {
@@ -892,269 +108,22 @@ async function main() {
892
108
  : undefined,
893
109
  });
894
110
  if (args.command === 'impact') {
895
- const reportRoot = config.testsRoot || config.path;
896
- const gitResult = (0, git_js_1.getChangedFiles)(config.path, config.git.since, { includeUncommitted: config.git.includeUncommitted });
897
- const impactResult = (0, impact_engine_js_1.analyzeImpact)(gitResult.files, {
898
- testsRoot: reportRoot,
899
- routeFamilies: config.routeFamilies,
900
- });
901
- // eslint-disable-next-line no-console
902
- console.log(`Impact: ${impactResult.changedFiles.length} changed files → ${impactResult.impactedFeatures.length} features impacted`);
903
- // eslint-disable-next-line no-console
904
- console.log(`Unbound files: ${impactResult.unboundFiles.length}`);
905
- for (const f of impactResult.impactedFeatures) {
906
- const label = f.featureId || f.familyId;
907
- // eslint-disable-next-line no-console
908
- console.log(` [${f.priority}] ${label}: ${f.coverageStatus} (PW=${f.playwrightSpecs.length}, Cy=${f.cypressSpecs.length})`);
909
- }
910
- if (impactResult.warnings.length > 0) {
911
- for (const w of impactResult.warnings) {
912
- // eslint-disable-next-line no-console
913
- console.warn(` Warning: ${w}`);
914
- }
915
- }
111
+ (0, impact_js_1.runImpactCommand)(args, config);
916
112
  return;
917
113
  }
918
114
  if (args.command === 'suggest' || args.command === 'plan') {
919
- const reportRoot = config.testsRoot || config.path;
920
- const apiOptions = {
921
- cwd: process.cwd(),
922
- configPath: autoConfig,
923
- path: args.path,
924
- profile: args.profile,
925
- testsRoot: args.testsRoot,
926
- gitSince: args.gitSince,
927
- llmProvider: args.llmProvider,
928
- policy: args.policyMinConfidence !== undefined ||
929
- args.policySafeMergeConfidence !== undefined ||
930
- args.policyWarningsThreshold !== undefined ||
931
- (args.policyRiskyPatterns && args.policyRiskyPatterns.length > 0) ||
932
- args.policyEnforcementMode !== undefined ||
933
- (args.policyBlockActions && args.policyBlockActions.length > 0)
934
- ? {
935
- minConfidenceForTargeted: args.policyMinConfidence,
936
- safeMergeMinConfidence: args.policySafeMergeConfidence,
937
- forceFullOnWarningsAtOrAbove: args.policyWarningsThreshold,
938
- riskyFilePatterns: args.policyRiskyPatterns,
939
- enforcementMode: args.policyEnforcementMode,
940
- blockOnActions: args.policyBlockActions,
941
- }
942
- : undefined,
943
- };
944
- let result;
945
- if (args.noAi) {
946
- result = (0, api_js_1.recommendTestsDeterministic)(apiOptions);
947
- }
948
- else {
949
- result = await (0, api_js_1.recommendTestsAI)(apiOptions);
950
- if (result.aiEnrichment) {
951
- const { aiEnrichment } = result;
952
- // eslint-disable-next-line no-console
953
- console.log(`AI enrichment: ${aiEnrichment.enrichedFeatures.length} features enriched (${aiEnrichment.tokenUsage.input + aiEnrichment.tokenUsage.output} tokens)`);
954
- }
955
- else if (!process.env.ANTHROPIC_API_KEY) {
956
- // eslint-disable-next-line no-console
957
- console.log('Tip: set ANTHROPIC_API_KEY to enable AI-powered enrichment');
958
- }
959
- }
960
- const { plan, planPath, ciSummaryMarkdown, ciSummaryPath } = result;
961
- // Write CI summary to an additional path if --ci-comment-path was specified
962
- if (args.ciCommentPath) {
963
- (0, plan_builder_js_1.writeCiSummary)(reportRoot, ciSummaryMarkdown, args.ciCommentPath);
964
- }
965
- const summaryPath = ciSummaryPath;
966
- // Compute metrics paths (api already wrote metrics; derive paths for GHA output)
967
- const metricsEventsPath = (0, path_1.join)(reportRoot, '.e2e-ai-agents/metrics.jsonl');
968
- const metricsSummaryPath = (0, path_1.join)(reportRoot, '.e2e-ai-agents/metrics-summary.json');
969
- const ghaOutput = args.githubOutputPath || process.env.GITHUB_OUTPUT;
970
- if (ghaOutput) {
971
- (0, fs_1.appendFileSync)(ghaOutput, `run_set=${plan.runSet}\n`);
972
- (0, fs_1.appendFileSync)(ghaOutput, `action=${plan.decision.action}\n`);
973
- (0, fs_1.appendFileSync)(ghaOutput, `confidence=${plan.confidence}\n`);
974
- (0, fs_1.appendFileSync)(ghaOutput, `enforcement_mode=${plan.enforcement.mode}\n`);
975
- (0, fs_1.appendFileSync)(ghaOutput, `enforcement_should_fail=${plan.enforcement.shouldFail}\n`);
976
- (0, fs_1.appendFileSync)(ghaOutput, `recommended_tests_count=${plan.recommendedTests.length}\n`);
977
- (0, fs_1.appendFileSync)(ghaOutput, `required_new_tests_count=${plan.requiredNewTests.length}\n`);
978
- (0, fs_1.appendFileSync)(ghaOutput, `plan_path=${planPath}\n`);
979
- (0, fs_1.appendFileSync)(ghaOutput, `summary_path=${summaryPath}\n`);
980
- (0, fs_1.appendFileSync)(ghaOutput, `metrics_events_path=${metricsEventsPath}\n`);
981
- (0, fs_1.appendFileSync)(ghaOutput, `metrics_summary_path=${metricsSummaryPath}\n`);
982
- }
983
- // eslint-disable-next-line no-console
984
- console.log(`Suggested run set: ${plan.runSet} (confidence ${plan.confidence})`);
985
- // eslint-disable-next-line no-console
986
- console.log(`Decision: ${plan.decision.action} - ${plan.decision.summary}`);
987
- // eslint-disable-next-line no-console
988
- console.log(`Enforcement: ${plan.enforcement.mode} (shouldFail=${plan.enforcement.shouldFail})`);
989
- // eslint-disable-next-line no-console
990
- console.log(`Plan data: ${planPath}`);
991
- // eslint-disable-next-line no-console
992
- console.log(`CI summary: ${summaryPath}`);
993
- // eslint-disable-next-line no-console
994
- console.log(`Plan metrics: ${metricsSummaryPath}`);
995
- const failOnLegacyFlag = args.failOnMustAddTests && plan.decision.action === 'must-add-tests';
996
- if (failOnLegacyFlag || plan.enforcement.shouldFail) {
997
- process.exit(2);
998
- }
115
+ await (0, plan_js_1.runPlanCommand)(args, autoConfig, config);
999
116
  return;
1000
117
  }
1001
118
  if (args.command === 'generate') {
1002
- const reportRoot = config.testsRoot || config.path;
1003
- // Load scenarios from --scenarios flag or plan-report.json
1004
- let scenarios = [];
1005
- if (args.generateScenarios) {
1006
- let raw;
1007
- if ((0, fs_1.existsSync)(args.generateScenarios)) {
1008
- raw = JSON.parse((0, fs_1.readFileSync)(args.generateScenarios, 'utf-8'));
1009
- }
1010
- else {
1011
- raw = JSON.parse(args.generateScenarios);
1012
- }
1013
- if (!Array.isArray(raw)) {
1014
- // eslint-disable-next-line no-console
1015
- console.error('--scenarios must be a JSON array of ScenarioInput objects.');
1016
- process.exit(1);
1017
- }
1018
- for (const item of raw) {
1019
- if (!item.id || !item.name || !Array.isArray(item.scenarios) || !item.routeFamily || !item.priority) {
1020
- // eslint-disable-next-line no-console
1021
- console.error(`Invalid scenario: each must have id, name, scenarios[], routeFamily, priority.`);
1022
- process.exit(1);
1023
- }
1024
- }
1025
- scenarios = raw;
1026
- }
1027
- else {
1028
- const planReportPath = (0, path_1.join)(reportRoot, '.e2e-ai-agents', 'plan-report.json');
1029
- if (!(0, fs_1.existsSync)(planReportPath)) {
1030
- // eslint-disable-next-line no-console
1031
- console.error('No plan report found. Run `plan` first or pass --scenarios.');
1032
- process.exit(1);
1033
- }
1034
- const planReport = JSON.parse((0, fs_1.readFileSync)(planReportPath, 'utf-8'));
1035
- scenarios = (planReport.gapDetails || []).map((gap) => ({
1036
- id: gap.id,
1037
- name: gap.id,
1038
- scenarios: gap.missingScenarios || gap.reasons || ['Verify core user flow'],
1039
- routeFamily: gap.id.split('.')[0] || gap.id,
1040
- priority: 'P1',
1041
- }));
1042
- }
1043
- if (scenarios.length === 0) {
1044
- // eslint-disable-next-line no-console
1045
- console.log('No scenarios to generate tests for.');
1046
- return;
1047
- }
1048
- let apiSurface;
1049
- try {
1050
- apiSurface = (0, api_surface_js_1.loadOrBuildApiSurface)(reportRoot, config.apiSurface);
1051
- }
1052
- catch {
1053
- // eslint-disable-next-line no-console
1054
- console.warn('Could not load API surface catalog. Generation will use generic selectors.');
1055
- }
1056
- const provider = await provider_factory_js_1.LLMProviderFactory.createFromEnv();
1057
- // eslint-disable-next-line no-console
1058
- console.log(`Generating tests for ${scenarios.length} scenario(s)...`);
1059
- const summary = await (0, runner_js_1.runAgenticGeneration)({
1060
- scenarios,
1061
- config: {
1062
- maxAttempts: args.maxAttempts || 3,
1063
- project: args.pipelineProject || 'chrome',
1064
- baseUrl: args.pipelineBaseUrl,
1065
- testTimeoutMs: 120000,
1066
- testsRoot: reportRoot,
1067
- dryRun: args.dryRun,
1068
- },
1069
- provider,
1070
- apiSurface,
1071
- });
1072
- // eslint-disable-next-line no-console
1073
- console.log(`\nAgentic Generation Summary:`);
1074
- // eslint-disable-next-line no-console
1075
- console.log(` Generated: ${summary.totalGenerated}`);
1076
- // eslint-disable-next-line no-console
1077
- console.log(` Passed: ${summary.totalPassed}`);
1078
- // eslint-disable-next-line no-console
1079
- console.log(` Failed: ${summary.totalFailed}`);
1080
- // eslint-disable-next-line no-console
1081
- console.log(` Attempts: ${summary.totalAttempts}`);
1082
- // eslint-disable-next-line no-console
1083
- console.log(` Duration: ${(summary.durationMs / 1000).toFixed(1)}s`);
1084
- for (const result of summary.results) {
1085
- const icon = result.status === 'passed' ? 'PASS' : result.status === 'skipped' ? 'SKIP' : 'FAIL';
1086
- // eslint-disable-next-line no-console
1087
- console.log(` [${icon}] ${result.scenarioSource} (${result.attempts} attempts)`);
1088
- if (result.status === 'passed' || result.status === 'skipped') {
1089
- // eslint-disable-next-line no-console
1090
- console.log(` ${result.specPath}`);
1091
- }
1092
- }
1093
- if (summary.warnings.length > 0) {
1094
- // eslint-disable-next-line no-console
1095
- console.log(`\nWarnings:`);
1096
- for (const w of summary.warnings) {
1097
- // eslint-disable-next-line no-console
1098
- console.warn(` - ${w}`);
1099
- }
1100
- }
1101
- const summaryDir = (0, path_1.join)(reportRoot, '.e2e-ai-agents');
1102
- if (!(0, fs_1.existsSync)(summaryDir)) {
1103
- (0, fs_1.mkdirSync)(summaryDir, { recursive: true });
1104
- }
1105
- const summaryPath = (0, path_1.join)(summaryDir, 'agentic-summary.json');
1106
- (0, fs_1.writeFileSync)(summaryPath, JSON.stringify(summary, null, 2), 'utf-8');
1107
- // eslint-disable-next-line no-console
1108
- console.log(`\nReport: ${summaryPath}`);
1109
- if (summary.totalFailed > 0) {
1110
- process.exit(1);
1111
- }
119
+ await (0, generate_js_1.runGenerateCommand)(args, config);
1112
120
  return;
1113
121
  }
1114
- // eslint-disable-next-line no-console
1115
122
  console.error(`Unknown command: ${args.command}`);
1116
- printUsage();
123
+ (0, usage_js_1.printUsage)();
1117
124
  process.exit(1);
1118
125
  }
1119
- async function runLlmHealth() {
1120
- if (!process.env.ANTHROPIC_API_KEY) {
1121
- // eslint-disable-next-line no-console
1122
- console.error('ANTHROPIC_API_KEY is required for llm-health.');
1123
- process.exit(1);
1124
- }
1125
- const model = process.env.ANTHROPIC_MODEL || 'claude-sonnet-4-5-20250929';
1126
- const provider = new anthropic_provider_js_1.AnthropicProvider({
1127
- apiKey: process.env.ANTHROPIC_API_KEY,
1128
- model,
1129
- });
1130
- try {
1131
- const response = await provider.generateText('Reply with OK.', { maxTokens: 8, timeout: 15000 });
1132
- const text = response.text.trim();
1133
- // eslint-disable-next-line no-console
1134
- console.log(`Anthropic OK (${model}) -> ${text}`);
1135
- }
1136
- catch (error) {
1137
- if (error instanceof provider_interface_js_1.LLMProviderError) {
1138
- // eslint-disable-next-line no-console
1139
- console.error(`Anthropic failed: ${error.message}`);
1140
- if (error.cause instanceof Error) {
1141
- // eslint-disable-next-line no-console
1142
- console.error(`Cause: ${error.cause.message}`);
1143
- }
1144
- }
1145
- else if (error instanceof Error) {
1146
- // eslint-disable-next-line no-console
1147
- console.error(`Anthropic failed: ${error.message}`);
1148
- }
1149
- else {
1150
- // eslint-disable-next-line no-console
1151
- console.error(`Anthropic failed: ${String(error)}`);
1152
- }
1153
- process.exit(1);
1154
- }
1155
- }
1156
126
  main().catch((error) => {
1157
- // eslint-disable-next-line no-console
1158
127
  console.error(error instanceof Error ? error.message : String(error));
1159
128
  process.exit(1);
1160
129
  });