@yasserkhanorg/e2e-agents 0.10.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.
- package/README.md +112 -584
- package/dist/agent/api_catalog.d.ts +11 -0
- package/dist/agent/api_catalog.d.ts.map +1 -0
- package/dist/agent/api_catalog.js +210 -0
- package/dist/agent/llm_agents_flow.d.ts +15 -0
- package/dist/agent/llm_agents_flow.d.ts.map +1 -0
- package/dist/agent/llm_agents_flow.js +434 -0
- package/dist/agent/native_flow.d.ts +6 -0
- package/dist/agent/native_flow.d.ts.map +1 -0
- package/dist/agent/native_flow.js +179 -0
- package/dist/agent/pipeline.d.ts +2 -25
- package/dist/agent/pipeline.d.ts.map +1 -1
- package/dist/agent/pipeline.js +30 -1329
- package/dist/agent/pipeline_types.d.ts +54 -0
- package/dist/agent/pipeline_types.d.ts.map +1 -0
- package/dist/agent/pipeline_types.js +4 -0
- package/dist/agent/pipeline_utils.d.ts +12 -0
- package/dist/agent/pipeline_utils.d.ts.map +1 -0
- package/dist/agent/pipeline_utils.js +156 -0
- package/dist/agent/process_runner.d.ts +10 -0
- package/dist/agent/process_runner.d.ts.map +1 -0
- package/dist/agent/process_runner.js +92 -0
- package/dist/agent/spec_generator.d.ts +5 -0
- package/dist/agent/spec_generator.d.ts.map +1 -0
- package/dist/agent/spec_generator.js +253 -0
- package/dist/agent/validation_runner.d.ts +5 -0
- package/dist/agent/validation_runner.d.ts.map +1 -0
- package/dist/agent/validation_runner.js +77 -0
- package/dist/agentic/playwright_runner.js +1 -1
- package/dist/cli/commands/analyze.d.ts +3 -0
- package/dist/cli/commands/analyze.d.ts.map +1 -0
- package/dist/cli/commands/analyze.js +77 -0
- package/dist/cli/commands/feedback.d.ts +3 -0
- package/dist/cli/commands/feedback.d.ts.map +1 -0
- package/dist/cli/commands/feedback.js +39 -0
- package/dist/cli/commands/finalize.d.ts +3 -0
- package/dist/cli/commands/finalize.d.ts.map +1 -0
- package/dist/cli/commands/finalize.js +41 -0
- package/dist/cli/commands/generate.d.ts +4 -0
- package/dist/cli/commands/generate.d.ts.map +1 -0
- package/dist/cli/commands/generate.js +108 -0
- package/dist/cli/commands/heal.d.ts +3 -0
- package/dist/cli/commands/heal.d.ts.map +1 -0
- package/dist/cli/commands/heal.js +60 -0
- package/dist/cli/commands/impact.d.ts +4 -0
- package/dist/cli/commands/impact.d.ts.map +1 -0
- package/dist/cli/commands/impact.js +26 -0
- package/dist/cli/commands/llm_health.d.ts +2 -0
- package/dist/cli/commands/llm_health.d.ts.map +1 -0
- package/dist/cli/commands/llm_health.js +38 -0
- package/dist/cli/commands/plan.d.ts +4 -0
- package/dist/cli/commands/plan.d.ts.map +1 -0
- package/dist/cli/commands/plan.js +83 -0
- package/dist/cli/commands/traceability.d.ts +4 -0
- package/dist/cli/commands/traceability.d.ts.map +1 -0
- package/dist/cli/commands/traceability.js +77 -0
- package/dist/cli/parse_args.d.ts +6 -0
- package/dist/cli/parse_args.d.ts.map +1 -0
- package/dist/cli/parse_args.js +216 -0
- package/dist/cli/types.d.ts +70 -0
- package/dist/cli/types.d.ts.map +1 -0
- package/dist/cli/types.js +4 -0
- package/dist/cli/usage.d.ts +2 -0
- package/dist/cli/usage.d.ts.map +1 -0
- package/dist/cli/usage.js +86 -0
- package/dist/cli.js +26 -1060
- package/dist/esm/agent/api_catalog.js +199 -0
- package/dist/esm/agent/llm_agents_flow.js +421 -0
- package/dist/esm/agent/native_flow.js +175 -0
- package/dist/esm/agent/pipeline.js +8 -1307
- package/dist/esm/agent/pipeline_types.js +3 -0
- package/dist/esm/agent/pipeline_utils.js +146 -0
- package/dist/esm/agent/process_runner.js +83 -0
- package/dist/esm/agent/spec_generator.js +249 -0
- package/dist/esm/agent/validation_runner.js +73 -0
- package/dist/esm/agentic/playwright_runner.js +1 -1
- package/dist/esm/cli/commands/analyze.js +74 -0
- package/dist/esm/cli/commands/feedback.js +36 -0
- package/dist/esm/cli/commands/finalize.js +38 -0
- package/dist/esm/cli/commands/generate.js +105 -0
- package/dist/esm/cli/commands/heal.js +57 -0
- package/dist/esm/cli/commands/impact.js +23 -0
- package/dist/esm/cli/commands/llm_health.js +35 -0
- package/dist/esm/cli/commands/plan.js +80 -0
- package/dist/esm/cli/commands/traceability.js +73 -0
- package/dist/esm/cli/parse_args.js +210 -0
- package/dist/esm/cli/types.js +3 -0
- package/dist/esm/cli/usage.js +83 -0
- package/dist/esm/cli.js +20 -1054
- package/dist/esm/mcp-server.js +18 -1
- package/dist/mcp-server.d.ts.map +1 -1
- package/dist/mcp-server.js +17 -0
- 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
|
|
10
|
-
const
|
|
11
|
-
const
|
|
12
|
-
const
|
|
13
|
-
const
|
|
14
|
-
const
|
|
15
|
-
const
|
|
16
|
-
const
|
|
17
|
-
const
|
|
18
|
-
const
|
|
19
|
-
const
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,272 +108,22 @@ async function main() {
|
|
|
892
108
|
: undefined,
|
|
893
109
|
});
|
|
894
110
|
if (args.command === 'impact') {
|
|
895
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
// Try plan.json first (written by plan/suggest command), then plan-report.json (legacy)
|
|
1029
|
-
const planJsonPath = (0, path_1.join)(reportRoot, '.e2e-ai-agents', 'plan.json');
|
|
1030
|
-
const planReportPath = (0, path_1.join)(reportRoot, '.e2e-ai-agents', 'plan-report.json');
|
|
1031
|
-
const resolvedPlanPath = (0, fs_1.existsSync)(planJsonPath) ? planJsonPath : (0, fs_1.existsSync)(planReportPath) ? planReportPath : null;
|
|
1032
|
-
if (!resolvedPlanPath) {
|
|
1033
|
-
// eslint-disable-next-line no-console
|
|
1034
|
-
console.error('No plan report found. Run `plan` first or pass --scenarios.');
|
|
1035
|
-
process.exit(1);
|
|
1036
|
-
}
|
|
1037
|
-
const planReport = JSON.parse((0, fs_1.readFileSync)(resolvedPlanPath, 'utf-8'));
|
|
1038
|
-
scenarios = (planReport.gapDetails || []).map((gap) => ({
|
|
1039
|
-
id: gap.id,
|
|
1040
|
-
name: gap.id,
|
|
1041
|
-
scenarios: gap.missingScenarios || gap.reasons || ['Verify core user flow'],
|
|
1042
|
-
routeFamily: gap.id.split('.')[0] || gap.id,
|
|
1043
|
-
priority: 'P1',
|
|
1044
|
-
}));
|
|
1045
|
-
}
|
|
1046
|
-
if (scenarios.length === 0) {
|
|
1047
|
-
// eslint-disable-next-line no-console
|
|
1048
|
-
console.log('No scenarios to generate tests for.');
|
|
1049
|
-
return;
|
|
1050
|
-
}
|
|
1051
|
-
let apiSurface;
|
|
1052
|
-
try {
|
|
1053
|
-
apiSurface = (0, api_surface_js_1.loadOrBuildApiSurface)(reportRoot, config.apiSurface);
|
|
1054
|
-
}
|
|
1055
|
-
catch {
|
|
1056
|
-
// eslint-disable-next-line no-console
|
|
1057
|
-
console.warn('Could not load API surface catalog. Generation will use generic selectors.');
|
|
1058
|
-
}
|
|
1059
|
-
const provider = await provider_factory_js_1.LLMProviderFactory.createFromEnv();
|
|
1060
|
-
// eslint-disable-next-line no-console
|
|
1061
|
-
console.log(`Generating tests for ${scenarios.length} scenario(s)...`);
|
|
1062
|
-
const summary = await (0, runner_js_1.runAgenticGeneration)({
|
|
1063
|
-
scenarios,
|
|
1064
|
-
config: {
|
|
1065
|
-
maxAttempts: args.maxAttempts || 3,
|
|
1066
|
-
project: args.pipelineProject || 'chrome',
|
|
1067
|
-
baseUrl: args.pipelineBaseUrl,
|
|
1068
|
-
testTimeoutMs: 120000,
|
|
1069
|
-
testsRoot: reportRoot,
|
|
1070
|
-
dryRun: args.dryRun,
|
|
1071
|
-
},
|
|
1072
|
-
provider,
|
|
1073
|
-
apiSurface,
|
|
1074
|
-
});
|
|
1075
|
-
// eslint-disable-next-line no-console
|
|
1076
|
-
console.log(`\nAgentic Generation Summary:`);
|
|
1077
|
-
// eslint-disable-next-line no-console
|
|
1078
|
-
console.log(` Generated: ${summary.totalGenerated}`);
|
|
1079
|
-
// eslint-disable-next-line no-console
|
|
1080
|
-
console.log(` Passed: ${summary.totalPassed}`);
|
|
1081
|
-
// eslint-disable-next-line no-console
|
|
1082
|
-
console.log(` Failed: ${summary.totalFailed}`);
|
|
1083
|
-
// eslint-disable-next-line no-console
|
|
1084
|
-
console.log(` Attempts: ${summary.totalAttempts}`);
|
|
1085
|
-
// eslint-disable-next-line no-console
|
|
1086
|
-
console.log(` Duration: ${(summary.durationMs / 1000).toFixed(1)}s`);
|
|
1087
|
-
for (const result of summary.results) {
|
|
1088
|
-
const icon = result.status === 'passed' ? 'PASS' : result.status === 'skipped' ? 'SKIP' : 'FAIL';
|
|
1089
|
-
// eslint-disable-next-line no-console
|
|
1090
|
-
console.log(` [${icon}] ${result.scenarioSource} (${result.attempts} attempts)`);
|
|
1091
|
-
if (result.status === 'passed' || result.status === 'skipped') {
|
|
1092
|
-
// eslint-disable-next-line no-console
|
|
1093
|
-
console.log(` ${result.specPath}`);
|
|
1094
|
-
}
|
|
1095
|
-
}
|
|
1096
|
-
if (summary.warnings.length > 0) {
|
|
1097
|
-
// eslint-disable-next-line no-console
|
|
1098
|
-
console.log(`\nWarnings:`);
|
|
1099
|
-
for (const w of summary.warnings) {
|
|
1100
|
-
// eslint-disable-next-line no-console
|
|
1101
|
-
console.warn(` - ${w}`);
|
|
1102
|
-
}
|
|
1103
|
-
}
|
|
1104
|
-
const summaryDir = (0, path_1.join)(reportRoot, '.e2e-ai-agents');
|
|
1105
|
-
if (!(0, fs_1.existsSync)(summaryDir)) {
|
|
1106
|
-
(0, fs_1.mkdirSync)(summaryDir, { recursive: true });
|
|
1107
|
-
}
|
|
1108
|
-
const summaryPath = (0, path_1.join)(summaryDir, 'agentic-summary.json');
|
|
1109
|
-
(0, fs_1.writeFileSync)(summaryPath, JSON.stringify(summary, null, 2), 'utf-8');
|
|
1110
|
-
// eslint-disable-next-line no-console
|
|
1111
|
-
console.log(`\nReport: ${summaryPath}`);
|
|
1112
|
-
if (summary.totalFailed > 0) {
|
|
1113
|
-
process.exit(1);
|
|
1114
|
-
}
|
|
119
|
+
await (0, generate_js_1.runGenerateCommand)(args, config);
|
|
1115
120
|
return;
|
|
1116
121
|
}
|
|
1117
|
-
// eslint-disable-next-line no-console
|
|
1118
122
|
console.error(`Unknown command: ${args.command}`);
|
|
1119
|
-
printUsage();
|
|
123
|
+
(0, usage_js_1.printUsage)();
|
|
1120
124
|
process.exit(1);
|
|
1121
125
|
}
|
|
1122
|
-
async function runLlmHealth() {
|
|
1123
|
-
if (!process.env.ANTHROPIC_API_KEY) {
|
|
1124
|
-
// eslint-disable-next-line no-console
|
|
1125
|
-
console.error('ANTHROPIC_API_KEY is required for llm-health.');
|
|
1126
|
-
process.exit(1);
|
|
1127
|
-
}
|
|
1128
|
-
const model = process.env.ANTHROPIC_MODEL || 'claude-sonnet-4-5-20250929';
|
|
1129
|
-
const provider = new anthropic_provider_js_1.AnthropicProvider({
|
|
1130
|
-
apiKey: process.env.ANTHROPIC_API_KEY,
|
|
1131
|
-
model,
|
|
1132
|
-
});
|
|
1133
|
-
try {
|
|
1134
|
-
const response = await provider.generateText('Reply with OK.', { maxTokens: 8, timeout: 15000 });
|
|
1135
|
-
const text = response.text.trim();
|
|
1136
|
-
// eslint-disable-next-line no-console
|
|
1137
|
-
console.log(`Anthropic OK (${model}) -> ${text}`);
|
|
1138
|
-
}
|
|
1139
|
-
catch (error) {
|
|
1140
|
-
if (error instanceof provider_interface_js_1.LLMProviderError) {
|
|
1141
|
-
// eslint-disable-next-line no-console
|
|
1142
|
-
console.error(`Anthropic failed: ${error.message}`);
|
|
1143
|
-
if (error.cause instanceof Error) {
|
|
1144
|
-
// eslint-disable-next-line no-console
|
|
1145
|
-
console.error(`Cause: ${error.cause.message}`);
|
|
1146
|
-
}
|
|
1147
|
-
}
|
|
1148
|
-
else if (error instanceof Error) {
|
|
1149
|
-
// eslint-disable-next-line no-console
|
|
1150
|
-
console.error(`Anthropic failed: ${error.message}`);
|
|
1151
|
-
}
|
|
1152
|
-
else {
|
|
1153
|
-
// eslint-disable-next-line no-console
|
|
1154
|
-
console.error(`Anthropic failed: ${String(error)}`);
|
|
1155
|
-
}
|
|
1156
|
-
process.exit(1);
|
|
1157
|
-
}
|
|
1158
|
-
}
|
|
1159
126
|
main().catch((error) => {
|
|
1160
|
-
// eslint-disable-next-line no-console
|
|
1161
127
|
console.error(error instanceof Error ? error.message : String(error));
|
|
1162
128
|
process.exit(1);
|
|
1163
129
|
});
|