@a11yst/cli 1.0.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/LICENSE +373 -0
- package/NOTICE.md +11 -0
- package/README.md +60 -0
- package/TRADEMARKS.md +52 -0
- package/dist/bin.d.ts +3 -0
- package/dist/bin.d.ts.map +1 -0
- package/dist/bin.js +9 -0
- package/dist/bin.js.map +1 -0
- package/dist/ci-policy-options.d.ts +15 -0
- package/dist/ci-policy-options.d.ts.map +1 -0
- package/dist/ci-policy-options.js +54 -0
- package/dist/ci-policy-options.js.map +1 -0
- package/dist/cli-progress.d.ts +16 -0
- package/dist/cli-progress.d.ts.map +1 -0
- package/dist/cli-progress.js +35 -0
- package/dist/cli-progress.js.map +1 -0
- package/dist/command-program.d.ts +7 -0
- package/dist/command-program.d.ts.map +1 -0
- package/dist/command-program.js +950 -0
- package/dist/command-program.js.map +1 -0
- package/dist/commands/audit-flow-output.d.ts +4 -0
- package/dist/commands/audit-flow-output.d.ts.map +1 -0
- package/dist/commands/audit-flow-output.js +119 -0
- package/dist/commands/audit-flow-output.js.map +1 -0
- package/dist/commands/audit-policy-output.d.ts +9 -0
- package/dist/commands/audit-policy-output.d.ts.map +1 -0
- package/dist/commands/audit-policy-output.js +95 -0
- package/dist/commands/audit-policy-output.js.map +1 -0
- package/dist/commands/audit.d.ts +15 -0
- package/dist/commands/audit.d.ts.map +1 -0
- package/dist/commands/audit.js +178 -0
- package/dist/commands/audit.js.map +1 -0
- package/dist/commands/baseline-config.d.ts +14 -0
- package/dist/commands/baseline-config.d.ts.map +1 -0
- package/dist/commands/baseline-config.js +33 -0
- package/dist/commands/baseline-config.js.map +1 -0
- package/dist/commands/baseline.d.ts +86 -0
- package/dist/commands/baseline.d.ts.map +1 -0
- package/dist/commands/baseline.js +392 -0
- package/dist/commands/baseline.js.map +1 -0
- package/dist/commands/classify.d.ts +30 -0
- package/dist/commands/classify.d.ts.map +1 -0
- package/dist/commands/classify.js +105 -0
- package/dist/commands/classify.js.map +1 -0
- package/dist/commands/detect.d.ts +12 -0
- package/dist/commands/detect.d.ts.map +1 -0
- package/dist/commands/detect.js +213 -0
- package/dist/commands/detect.js.map +1 -0
- package/dist/commands/doctor.d.ts +16 -0
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/commands/doctor.js +404 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/findings.d.ts +45 -0
- package/dist/commands/findings.d.ts.map +1 -0
- package/dist/commands/findings.js +220 -0
- package/dist/commands/findings.js.map +1 -0
- package/dist/commands/flows.d.ts +30 -0
- package/dist/commands/flows.d.ts.map +1 -0
- package/dist/commands/flows.js +108 -0
- package/dist/commands/flows.js.map +1 -0
- package/dist/commands/init.d.ts +64 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +293 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/profiles.d.ts +5 -0
- package/dist/commands/profiles.d.ts.map +1 -0
- package/dist/commands/profiles.js +39 -0
- package/dist/commands/profiles.js.map +1 -0
- package/dist/commands/report.d.ts +72 -0
- package/dist/commands/report.d.ts.map +1 -0
- package/dist/commands/report.js +284 -0
- package/dist/commands/report.js.map +1 -0
- package/dist/commands/results-loader.d.ts +15 -0
- package/dist/commands/results-loader.d.ts.map +1 -0
- package/dist/commands/results-loader.js +100 -0
- package/dist/commands/results-loader.js.map +1 -0
- package/dist/commands/routes.d.ts +35 -0
- package/dist/commands/routes.d.ts.map +1 -0
- package/dist/commands/routes.js +201 -0
- package/dist/commands/routes.js.map +1 -0
- package/dist/commands/unclassify.d.ts +22 -0
- package/dist/commands/unclassify.d.ts.map +1 -0
- package/dist/commands/unclassify.js +79 -0
- package/dist/commands/unclassify.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/output.d.ts +9 -0
- package/dist/output.d.ts.map +1 -0
- package/dist/output.js +29 -0
- package/dist/output.js.map +1 -0
- package/dist/presentation/ansi.d.ts +12 -0
- package/dist/presentation/ansi.d.ts.map +1 -0
- package/dist/presentation/ansi.js +51 -0
- package/dist/presentation/ansi.js.map +1 -0
- package/dist/presentation/audit-presentation.d.ts +20 -0
- package/dist/presentation/audit-presentation.d.ts.map +1 -0
- package/dist/presentation/audit-presentation.js +409 -0
- package/dist/presentation/audit-presentation.js.map +1 -0
- package/dist/presentation/boundary.d.ts +5 -0
- package/dist/presentation/boundary.d.ts.map +1 -0
- package/dist/presentation/boundary.js +4 -0
- package/dist/presentation/boundary.js.map +1 -0
- package/dist/presentation/brand-header.d.ts +6 -0
- package/dist/presentation/brand-header.d.ts.map +1 -0
- package/dist/presentation/brand-header.js +18 -0
- package/dist/presentation/brand-header.js.map +1 -0
- package/dist/presentation/capabilities.d.ts +4 -0
- package/dist/presentation/capabilities.d.ts.map +1 -0
- package/dist/presentation/capabilities.js +25 -0
- package/dist/presentation/capabilities.js.map +1 -0
- package/dist/presentation/color.d.ts +13 -0
- package/dist/presentation/color.d.ts.map +1 -0
- package/dist/presentation/color.js +27 -0
- package/dist/presentation/color.js.map +1 -0
- package/dist/presentation/disclaimers.d.ts +3 -0
- package/dist/presentation/disclaimers.d.ts.map +1 -0
- package/dist/presentation/disclaimers.js +3 -0
- package/dist/presentation/disclaimers.js.map +1 -0
- package/dist/presentation/finding-groups.d.ts +14 -0
- package/dist/presentation/finding-groups.d.ts.map +1 -0
- package/dist/presentation/finding-groups.js +77 -0
- package/dist/presentation/finding-groups.js.map +1 -0
- package/dist/presentation/finding-source-report.d.ts +21 -0
- package/dist/presentation/finding-source-report.d.ts.map +1 -0
- package/dist/presentation/finding-source-report.js +134 -0
- package/dist/presentation/finding-source-report.js.map +1 -0
- package/dist/presentation/human-output.d.ts +4 -0
- package/dist/presentation/human-output.d.ts.map +1 -0
- package/dist/presentation/human-output.js +8 -0
- package/dist/presentation/human-output.js.map +1 -0
- package/dist/presentation/index.d.ts +15 -0
- package/dist/presentation/index.d.ts.map +1 -0
- package/dist/presentation/index.js +13 -0
- package/dist/presentation/index.js.map +1 -0
- package/dist/presentation/mode.d.ts +4 -0
- package/dist/presentation/mode.d.ts.map +1 -0
- package/dist/presentation/mode.js +8 -0
- package/dist/presentation/mode.js.map +1 -0
- package/dist/presentation/progress-mode.d.ts +20 -0
- package/dist/presentation/progress-mode.d.ts.map +1 -0
- package/dist/presentation/progress-mode.js +38 -0
- package/dist/presentation/progress-mode.js.map +1 -0
- package/dist/presentation/progress-reporter.d.ts +18 -0
- package/dist/presentation/progress-reporter.d.ts.map +1 -0
- package/dist/presentation/progress-reporter.js +236 -0
- package/dist/presentation/progress-reporter.js.map +1 -0
- package/dist/presentation/progress-signals.d.ts +2 -0
- package/dist/presentation/progress-signals.d.ts.map +1 -0
- package/dist/presentation/progress-signals.js +14 -0
- package/dist/presentation/progress-signals.js.map +1 -0
- package/dist/presentation/status.d.ts +11 -0
- package/dist/presentation/status.d.ts.map +1 -0
- package/dist/presentation/status.js +14 -0
- package/dist/presentation/status.js.map +1 -0
- package/dist/presentation/types.d.ts +23 -0
- package/dist/presentation/types.d.ts.map +1 -0
- package/dist/presentation/types.js +2 -0
- package/dist/presentation/types.js.map +1 -0
- package/dist/run-cli.d.ts +6 -0
- package/dist/run-cli.d.ts.map +1 -0
- package/dist/run-cli.js +15 -0
- package/dist/run-cli.js.map +1 -0
- package/package.json +69 -0
|
@@ -0,0 +1,950 @@
|
|
|
1
|
+
import { resolve } from "node:path";
|
|
2
|
+
import { Command } from "commander";
|
|
3
|
+
import { ConfigError, loadConfig } from "@a11yst/config";
|
|
4
|
+
import { getAuditExitCode, resolveCiPolicyConfig } from "@a11yst/policy";
|
|
5
|
+
import { productMetadata } from "@a11yst/types";
|
|
6
|
+
import { parseCiPolicyCliOptions } from "./ci-policy-options.js";
|
|
7
|
+
import { formatAuditHuman, formatAuditJson } from "./commands/audit.js";
|
|
8
|
+
import { createBaselineAfterAudit, formatBaselineCreateHuman, formatBaselineCreateJson, formatBaselineMigrateHuman, formatBaselineMigrateJson, formatBaselineStatusHuman, formatBaselineStatusJson, formatBaselineUpdateHuman, formatBaselineUpdateJson, runBaselineCreate, runBaselineMigrate, runBaselineStatus, runBaselineUpdate, } from "./commands/baseline.js";
|
|
9
|
+
import { formatClassifyHuman, formatClassifyJson, formatClassifyPreviewFromError, runClassify, } from "./commands/classify.js";
|
|
10
|
+
import { formatFindingsHuman, formatFindingsJson, parseFindingsDisposition, parseFindingsStatus, runFindings, } from "./commands/findings.js";
|
|
11
|
+
import { formatUnclassifyHuman, formatUnclassifyJson, formatUnclassifyPreviewFromError, runUnclassify, } from "./commands/unclassify.js";
|
|
12
|
+
import { formatReportHuman, formatReportJson, runReport, } from "./commands/report.js";
|
|
13
|
+
import { formatDetectHuman, formatDetectJson, runDetect, } from "./commands/detect.js";
|
|
14
|
+
import { formatDoctorHuman, formatDoctorJson, runDoctor, } from "./commands/doctor.js";
|
|
15
|
+
import { formatInitHuman, formatInitJson, parseInitFramework, parseInitPlatform, runInit } from "./commands/init.js";
|
|
16
|
+
import { formatFlowsHuman, formatFlowsJson, runFlows, } from "./commands/flows.js";
|
|
17
|
+
import { formatProfilesHuman, runProfiles, } from "./commands/profiles.js";
|
|
18
|
+
import { formatRoutesHuman, formatRoutesJson, runRoutes, } from "./commands/routes.js";
|
|
19
|
+
import { createCliProgressReporter } from "./cli-progress.js";
|
|
20
|
+
import { writeJson, writeStderr, writeStdout } from "./output.js";
|
|
21
|
+
import { AUDIT_HELP_DISCLAIMER, createBrandHeader, parseColorMode, prependHumanBrandHeader, resolveTerminalCapabilities, resolveTerminalPresentationMode, } from "./presentation/index.js";
|
|
22
|
+
function exitCodeFromDoctorStatus(status) {
|
|
23
|
+
if (status === "fail")
|
|
24
|
+
return 1;
|
|
25
|
+
return 0;
|
|
26
|
+
}
|
|
27
|
+
function exitCodeFromError(error) {
|
|
28
|
+
if (error &&
|
|
29
|
+
typeof error === "object" &&
|
|
30
|
+
"exitCode" in error &&
|
|
31
|
+
typeof error.exitCode === "number") {
|
|
32
|
+
return error.exitCode;
|
|
33
|
+
}
|
|
34
|
+
return 1;
|
|
35
|
+
}
|
|
36
|
+
function collectCheckpointNames(value, previous) {
|
|
37
|
+
return [...previous, value];
|
|
38
|
+
}
|
|
39
|
+
function collectRuleNames(value, previous) {
|
|
40
|
+
return [...previous, value];
|
|
41
|
+
}
|
|
42
|
+
function collectFindingStatus(value, previous) {
|
|
43
|
+
return [...previous, parseFindingsStatus(value)];
|
|
44
|
+
}
|
|
45
|
+
function collectFindingDisposition(value, previous) {
|
|
46
|
+
return [...previous, parseFindingsDisposition(value)];
|
|
47
|
+
}
|
|
48
|
+
/** Commander "collect" helper for repeatable `--project <name>` flags. */
|
|
49
|
+
function collectProjectNames(value, previous) {
|
|
50
|
+
return [...previous, value];
|
|
51
|
+
}
|
|
52
|
+
function collectProfileNames(value, previous) {
|
|
53
|
+
return [...previous, value];
|
|
54
|
+
}
|
|
55
|
+
function collectFlowNames(value, previous) {
|
|
56
|
+
return [...previous, value];
|
|
57
|
+
}
|
|
58
|
+
function parsePositiveInteger(value, optionName) {
|
|
59
|
+
if (!/^\d+$/.test(value)) {
|
|
60
|
+
throw new Error(`${optionName} must be a positive integer; received "${value}".`);
|
|
61
|
+
}
|
|
62
|
+
const parsed = Number(value);
|
|
63
|
+
if (!Number.isSafeInteger(parsed) || parsed <= 0) {
|
|
64
|
+
throw new Error(`${optionName} must be a positive integer; received "${value}".`);
|
|
65
|
+
}
|
|
66
|
+
return parsed;
|
|
67
|
+
}
|
|
68
|
+
function readGlobalPresentationOptions(command) {
|
|
69
|
+
const root = command.parent && typeof command.parent.opts === "function" ? command.parent : command;
|
|
70
|
+
const opts = root.opts();
|
|
71
|
+
return {
|
|
72
|
+
progress: opts.progress,
|
|
73
|
+
noProgress: opts.noProgress,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
function createCommandProgress(command, opts) {
|
|
77
|
+
const global = readGlobalPresentationOptions(command);
|
|
78
|
+
return createCliProgressReporter({
|
|
79
|
+
json: opts.json,
|
|
80
|
+
progress: global.progress,
|
|
81
|
+
noProgress: global.noProgress,
|
|
82
|
+
colorMode: parseColorMode(typeof opts.color === "string" ? opts.color : undefined),
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
export function createCliProgram(options = {}) {
|
|
86
|
+
const cwd = resolve(options.cwd ?? process.cwd());
|
|
87
|
+
const argv = options.argv ?? process.argv;
|
|
88
|
+
function resolveCwd(cwdFlag) {
|
|
89
|
+
return cwdFlag ? resolve(cwd, cwdFlag) : cwd;
|
|
90
|
+
}
|
|
91
|
+
const program = new Command();
|
|
92
|
+
program
|
|
93
|
+
.name(productMetadata.command)
|
|
94
|
+
.description(`${productMetadata.name} — ${productMetadata.tagline}`)
|
|
95
|
+
.version(productMetadata.version, "-V, --version", "Print the version number")
|
|
96
|
+
.helpOption("-h, --help", "Display help information")
|
|
97
|
+
.showHelpAfterError()
|
|
98
|
+
.configureOutput({
|
|
99
|
+
writeOut: (str) => process.stdout.write(str),
|
|
100
|
+
writeErr: (str) => process.stderr.write(str),
|
|
101
|
+
})
|
|
102
|
+
.option("--progress <mode>", "Progress feedback while long operations run (auto, always, never)", "auto")
|
|
103
|
+
.option("--no-progress", "Disable progress feedback");
|
|
104
|
+
program.addHelpText("before", () => `${createBrandHeader({ tagline: true })}\n\n`);
|
|
105
|
+
program
|
|
106
|
+
.command("detect")
|
|
107
|
+
.description("Detect project platform, framework, and package manager")
|
|
108
|
+
.option("--json", "Emit machine-readable JSON on stdout", false)
|
|
109
|
+
.option("--workspace", "Detect every project across a monorepo workspace", false)
|
|
110
|
+
.option("--cwd <path>", "Directory to run detection in")
|
|
111
|
+
.action(async function (opts) {
|
|
112
|
+
const progress = createCommandProgress(this, opts);
|
|
113
|
+
try {
|
|
114
|
+
progress.start("Scanning project…");
|
|
115
|
+
const result = await runDetect({
|
|
116
|
+
cwd: resolveCwd(opts.cwd),
|
|
117
|
+
workspace: opts.workspace,
|
|
118
|
+
});
|
|
119
|
+
progress.succeed("Scan complete");
|
|
120
|
+
if (opts.json) {
|
|
121
|
+
writeJson(formatDetectJson(result));
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
writeStdout(formatDetectHuman(result));
|
|
125
|
+
}
|
|
126
|
+
process.exitCode = 0;
|
|
127
|
+
}
|
|
128
|
+
catch (error) {
|
|
129
|
+
progress.fail("Detection failed");
|
|
130
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
131
|
+
if (opts.json) {
|
|
132
|
+
writeJson({ status: "error", message }, process.stdout);
|
|
133
|
+
}
|
|
134
|
+
writeStderr(message);
|
|
135
|
+
process.exitCode = 1;
|
|
136
|
+
}
|
|
137
|
+
finally {
|
|
138
|
+
progress.stop();
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
program
|
|
142
|
+
.command("init")
|
|
143
|
+
.description("Create a starter configuration file")
|
|
144
|
+
.option("--force", "Overwrite an existing configuration file", false)
|
|
145
|
+
.option("--platform <platform>", 'Target platform: "web". Detected when omitted.')
|
|
146
|
+
.option("--framework <framework>", "Framework hint (e.g. html, react, next). Detected when omitted.")
|
|
147
|
+
.option("--base-url <url>", "Base URL for web projects (e.g. http://localhost:3000)")
|
|
148
|
+
.option("--dev-command <command>", "Command used to start the development server")
|
|
149
|
+
.option("--cwd <path>", "Directory to initialize (defaults to the current working directory)")
|
|
150
|
+
.option("--json", "Emit machine-readable JSON on stdout", false)
|
|
151
|
+
.action(async function (opts) {
|
|
152
|
+
const progress = createCommandProgress(this, opts);
|
|
153
|
+
try {
|
|
154
|
+
progress.start("Initializing project…");
|
|
155
|
+
const platform = opts.platform !== undefined ? parseInitPlatform(opts.platform) : undefined;
|
|
156
|
+
const framework = opts.framework !== undefined ? parseInitFramework(opts.framework) : undefined;
|
|
157
|
+
const result = await runInit({
|
|
158
|
+
cwd: resolveCwd(opts.cwd),
|
|
159
|
+
force: opts.force,
|
|
160
|
+
platform,
|
|
161
|
+
framework,
|
|
162
|
+
baseUrl: opts.baseUrl,
|
|
163
|
+
devCommand: opts.devCommand,
|
|
164
|
+
json: opts.json,
|
|
165
|
+
});
|
|
166
|
+
progress.succeed("Initialization complete");
|
|
167
|
+
if (opts.json) {
|
|
168
|
+
writeJson(formatInitJson(result));
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
writeStdout(prependHumanBrandHeader(formatInitHuman(result)));
|
|
172
|
+
}
|
|
173
|
+
process.exitCode = 0;
|
|
174
|
+
}
|
|
175
|
+
catch (error) {
|
|
176
|
+
progress.fail("Initialization failed");
|
|
177
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
178
|
+
if (opts.json) {
|
|
179
|
+
writeJson({ status: "error", message }, process.stdout);
|
|
180
|
+
}
|
|
181
|
+
writeStderr(message);
|
|
182
|
+
process.exitCode = 1;
|
|
183
|
+
}
|
|
184
|
+
finally {
|
|
185
|
+
progress.stop();
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
program
|
|
189
|
+
.command("flows")
|
|
190
|
+
.description("List configured user flows, steps, and checkpoints without running a browser")
|
|
191
|
+
.option("--json", "Emit machine-readable JSON on stdout", false)
|
|
192
|
+
.option("--config <path>", "Path to a configuration file")
|
|
193
|
+
.option("--cwd <path>", "Directory to load configuration from")
|
|
194
|
+
.option("--project <name>", "Show flows for this project only (repeatable)", collectProjectNames, [])
|
|
195
|
+
.action(async (opts) => {
|
|
196
|
+
try {
|
|
197
|
+
const result = await runFlows({
|
|
198
|
+
cwd: resolveCwd(opts.cwd),
|
|
199
|
+
configPath: opts.config,
|
|
200
|
+
projectName: opts.project,
|
|
201
|
+
json: opts.json,
|
|
202
|
+
});
|
|
203
|
+
if (opts.json) {
|
|
204
|
+
writeJson(formatFlowsJson(result));
|
|
205
|
+
}
|
|
206
|
+
else {
|
|
207
|
+
writeStdout(formatFlowsHuman(result));
|
|
208
|
+
}
|
|
209
|
+
process.exitCode = 0;
|
|
210
|
+
}
|
|
211
|
+
catch (error) {
|
|
212
|
+
const message = error instanceof ConfigError
|
|
213
|
+
? error.format()
|
|
214
|
+
: error instanceof Error
|
|
215
|
+
? error.message
|
|
216
|
+
: String(error);
|
|
217
|
+
if (opts.json) {
|
|
218
|
+
writeJson({
|
|
219
|
+
status: "error",
|
|
220
|
+
message,
|
|
221
|
+
code: error instanceof ConfigError ? error.code : "FLOWS_FAILED",
|
|
222
|
+
issues: error instanceof ConfigError ? error.issues : undefined,
|
|
223
|
+
}, process.stdout);
|
|
224
|
+
}
|
|
225
|
+
writeStderr(message);
|
|
226
|
+
process.exitCode = 1;
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
program
|
|
230
|
+
.command("routes")
|
|
231
|
+
.description("Resolve configured routes via framework adapters")
|
|
232
|
+
.option("--json", "Emit machine-readable JSON on stdout", false)
|
|
233
|
+
.option("--config <path>", "Path to a configuration file")
|
|
234
|
+
.option("--cwd <path>", "Directory to load configuration from")
|
|
235
|
+
.option("--project <name>", "Resolve routes for this project only (repeatable)", collectProjectNames, [])
|
|
236
|
+
.option("--explain", "Show route discovery provenance and unresolved patterns", false)
|
|
237
|
+
.action(async function (opts) {
|
|
238
|
+
const progress = createCommandProgress(this, opts);
|
|
239
|
+
try {
|
|
240
|
+
progress.start("Discovering routes…");
|
|
241
|
+
const result = await runRoutes({
|
|
242
|
+
cwd: resolveCwd(opts.cwd),
|
|
243
|
+
configPath: opts.config,
|
|
244
|
+
projectName: opts.project,
|
|
245
|
+
json: opts.json,
|
|
246
|
+
explain: opts.explain,
|
|
247
|
+
});
|
|
248
|
+
const routeCount = result.projects.reduce((total, project) => total + project.routes.length, 0);
|
|
249
|
+
progress.succeed(`${routeCount} route${routeCount === 1 ? "" : "s"} resolved`);
|
|
250
|
+
if (opts.json) {
|
|
251
|
+
writeJson(formatRoutesJson(result));
|
|
252
|
+
}
|
|
253
|
+
else {
|
|
254
|
+
writeStdout(formatRoutesHuman(result, { explain: opts.explain }));
|
|
255
|
+
}
|
|
256
|
+
process.exitCode = 0;
|
|
257
|
+
}
|
|
258
|
+
catch (error) {
|
|
259
|
+
progress.fail("Route discovery failed");
|
|
260
|
+
const message = error instanceof ConfigError
|
|
261
|
+
? error.format()
|
|
262
|
+
: error instanceof Error
|
|
263
|
+
? error.message
|
|
264
|
+
: String(error);
|
|
265
|
+
if (opts.json) {
|
|
266
|
+
writeJson({
|
|
267
|
+
status: "error",
|
|
268
|
+
message,
|
|
269
|
+
code: error instanceof ConfigError ? error.code : "ROUTES_FAILED",
|
|
270
|
+
issues: error instanceof ConfigError ? error.issues : undefined,
|
|
271
|
+
}, process.stdout);
|
|
272
|
+
}
|
|
273
|
+
writeStderr(message);
|
|
274
|
+
process.exitCode = 1;
|
|
275
|
+
}
|
|
276
|
+
finally {
|
|
277
|
+
progress.stop();
|
|
278
|
+
}
|
|
279
|
+
});
|
|
280
|
+
program
|
|
281
|
+
.command("audit")
|
|
282
|
+
.description("Run an accessibility audit against your configured projects")
|
|
283
|
+
.option("--json", "Emit machine-readable JSON on stdout", false)
|
|
284
|
+
.option("--config <path>", "Path to a configuration file")
|
|
285
|
+
.option("--cwd <path>", "Directory to load configuration from")
|
|
286
|
+
.option("--headed", "Run with a visible browser window instead of headless", false)
|
|
287
|
+
.option("--timeout <ms>", "Navigation timeout in milliseconds", "30000")
|
|
288
|
+
.option("--no-start-server", "Never start a dev server; fail fast if nothing is already listening")
|
|
289
|
+
.option("--project <name>", "Audit only this project (repeatable)", collectProjectNames, [])
|
|
290
|
+
.option("--profile <id>", "Audit only this accessibility profile (repeatable)", collectProfileNames, [])
|
|
291
|
+
.option("--flow <id>", "Audit only this flow (repeatable)", collectFlowNames, [])
|
|
292
|
+
.option("--flows-only", "Audit only configured flows, not static routes", false)
|
|
293
|
+
.option("--routes-only", "Audit only static routes, not flows", false)
|
|
294
|
+
.option("--flow-timeout <ms>", "Flow step timeout in milliseconds", "10000")
|
|
295
|
+
.option("--output <path>", "Directory for audit results and reports")
|
|
296
|
+
.option("--no-html", "Do not generate an HTML report")
|
|
297
|
+
.option("--sarif", "Generate a SARIF 2.1.0 report in the audit bundle", false)
|
|
298
|
+
.option("--no-sarif", "Do not generate a SARIF report even when enabled in config")
|
|
299
|
+
.option("--sarif-output <path>", "Also write the SARIF report to this path")
|
|
300
|
+
.option("--junit", "Generate a JUnit XML report in the audit bundle", false)
|
|
301
|
+
.option("--no-junit", "Do not generate a JUnit report even when enabled in config")
|
|
302
|
+
.option("--junit-output <path>", "Also write the JUnit report to this path")
|
|
303
|
+
.option("--no-markdown", "Do not generate a Markdown report")
|
|
304
|
+
.option("--markdown-output <path>", "Also write the Markdown report to this path")
|
|
305
|
+
.option("--github-annotations", "Generate GitHub Actions workflow annotation commands in the audit bundle", false)
|
|
306
|
+
.option("--no-github-annotations", "Do not generate GitHub annotation commands even when enabled in config")
|
|
307
|
+
.option("--github-annotations-output <path>", "Also write GitHub annotation commands to this path")
|
|
308
|
+
.option("--github-step-summary", "Append the Markdown report to GITHUB_STEP_SUMMARY when set", false)
|
|
309
|
+
.option("--no-github-step-summary", "Do not write to GITHUB_STEP_SUMMARY even when enabled in config")
|
|
310
|
+
.option("--no-screenshots", "Do not capture screenshot evidence")
|
|
311
|
+
.option("--full-page-screenshots", "Capture full-page screenshots", false)
|
|
312
|
+
.option("--no-baseline", "Do not compare against a baseline file")
|
|
313
|
+
.option("--baseline <path>", "Compare against this baseline file")
|
|
314
|
+
.option("--create-baseline", "Create a baseline from this audit result", false)
|
|
315
|
+
.option("--force", "Overwrite an existing baseline when used with --create-baseline", false)
|
|
316
|
+
.option("--fail-on-new", "Fail CI policy when new findings meet the severity threshold", false)
|
|
317
|
+
.option("--no-fail-on-new", "Do not fail CI policy on new findings")
|
|
318
|
+
.option("--fail-on-regression", "Fail CI policy when regressed findings meet the severity threshold", false)
|
|
319
|
+
.option("--no-fail-on-regression", "Do not fail CI policy on regressions")
|
|
320
|
+
.option("--fail-on-expired-classification", "Fail CI policy when an accepted classification has expired", false)
|
|
321
|
+
.option("--no-fail-on-expired-classification", "Do not fail CI policy on expired classifications")
|
|
322
|
+
.option("--minimum-severity <severity>", "Minimum severity for CI policy breaches (minor, medium, high, critical)")
|
|
323
|
+
.option("--color <mode>", "Color output for human presentation (auto, always, never)", "auto")
|
|
324
|
+
.option("--verbose", "Include technical finding details in human output", false)
|
|
325
|
+
.addHelpText("after", `
|
|
326
|
+
CI policy flags override the ci section in your a11yst config. CLI values take
|
|
327
|
+
precedence over configuration. Setting --minimum-severity alone does not enable
|
|
328
|
+
the policy; at least one fail-on flag must be enabled.
|
|
329
|
+
|
|
330
|
+
Enabled CI policy requires baseline comparison. Use a baseline file or remove
|
|
331
|
+
--no-baseline. With --create-baseline, policy evaluation compares against any
|
|
332
|
+
baseline that existed before this run; a baseline created during the same run is
|
|
333
|
+
not used for policy evaluation.
|
|
334
|
+
|
|
335
|
+
Exit codes:
|
|
336
|
+
0 Audit completed; CI policy disabled or passed
|
|
337
|
+
1 Operational/config error, or CI policy could not be evaluated
|
|
338
|
+
2 Audit completed but CI policy failed
|
|
339
|
+
|
|
340
|
+
false-positive and not-applicable classifications are excluded from policy
|
|
341
|
+
breaches. accepted-risk, third-party, and manual-review may still block when
|
|
342
|
+
new or regressed. A baseline records known debt; it does not make findings pass.
|
|
343
|
+
|
|
344
|
+
SARIF reports are disabled by default. Use --sarif to generate
|
|
345
|
+
reports/a11yst.sarif inside the audit bundle. Use --sarif-output to
|
|
346
|
+
also write an identical copy to a custom path.
|
|
347
|
+
|
|
348
|
+
JUnit reports are disabled by default. Use --junit to generate
|
|
349
|
+
reports/a11yst.junit.xml inside the audit bundle. Use --junit-output to
|
|
350
|
+
also write an identical copy to a custom path.
|
|
351
|
+
|
|
352
|
+
Markdown reports are generated by default in the audit bundle at
|
|
353
|
+
reports/a11yst.md. Use --no-markdown to skip generation. Use --markdown-output for an
|
|
354
|
+
external copy.
|
|
355
|
+
|
|
356
|
+
GitHub annotation commands are disabled by default. Use --github-annotations
|
|
357
|
+
to write reports/github-annotations.txt in the bundle. Pipe that file to
|
|
358
|
+
stdout in GitHub Actions; a11yst does not upload results automatically.
|
|
359
|
+
|
|
360
|
+
Use --github-step-summary to append the Markdown report to GITHUB_STEP_SUMMARY
|
|
361
|
+
when the environment variable is set in the runner.
|
|
362
|
+
|
|
363
|
+
${AUDIT_HELP_DISCLAIMER}
|
|
364
|
+
`)
|
|
365
|
+
.action(async function (opts) {
|
|
366
|
+
const progress = createCommandProgress(this, opts);
|
|
367
|
+
const controller = new AbortController();
|
|
368
|
+
const onSignal = () => controller.abort();
|
|
369
|
+
process.once("SIGINT", onSignal);
|
|
370
|
+
process.once("SIGTERM", onSignal);
|
|
371
|
+
try {
|
|
372
|
+
const navigationTimeoutMs = parsePositiveInteger(opts.timeout ?? "30000", "--timeout");
|
|
373
|
+
const stepTimeoutMs = parsePositiveInteger(opts.flowTimeout ?? "10000", "--flow-timeout");
|
|
374
|
+
progress.start("Loading configuration…");
|
|
375
|
+
const config = await loadConfig({
|
|
376
|
+
cwd: resolveCwd(opts.cwd),
|
|
377
|
+
configPath: opts.config,
|
|
378
|
+
});
|
|
379
|
+
progress.succeed("Configuration loaded");
|
|
380
|
+
const { overrides: ciOverrides, explicitFlagsUsed } = parseCiPolicyCliOptions({
|
|
381
|
+
failOnNew: opts.failOnNew,
|
|
382
|
+
noFailOnNew: opts.noFailOnNew,
|
|
383
|
+
failOnRegression: opts.failOnRegression,
|
|
384
|
+
noFailOnRegression: opts.noFailOnRegression,
|
|
385
|
+
failOnExpiredClassification: opts.failOnExpiredClassification,
|
|
386
|
+
noFailOnExpiredClassification: opts.noFailOnExpiredClassification,
|
|
387
|
+
minimumSeverity: opts.minimumSeverity,
|
|
388
|
+
}, argv);
|
|
389
|
+
const resolvedCiPolicy = resolveCiPolicyConfig({
|
|
390
|
+
configPolicy: config.ci,
|
|
391
|
+
cliOverrides: ciOverrides,
|
|
392
|
+
});
|
|
393
|
+
const baselineOverride = typeof opts.baseline === "string" ? opts.baseline : undefined;
|
|
394
|
+
const noBaseline = opts.baseline === false;
|
|
395
|
+
const { executeAudit, resolveSarifReportOptions, resolveJunitReportOptions, resolveMarkdownReportOptions, resolveGitHubAnnotationsOptions, resolveGitHubStepSummaryOptions, } = await import("@a11yst/core");
|
|
396
|
+
const sarifOptions = resolveSarifReportOptions({
|
|
397
|
+
config: config.reports,
|
|
398
|
+
cli: {
|
|
399
|
+
sarif: opts.sarif || undefined,
|
|
400
|
+
noSarif: opts.noSarif || undefined,
|
|
401
|
+
sarifOutput: opts.sarifOutput,
|
|
402
|
+
},
|
|
403
|
+
});
|
|
404
|
+
const sarifExternalOutputPath = opts.sarifOutput
|
|
405
|
+
? resolve(resolveCwd(opts.cwd), opts.sarifOutput)
|
|
406
|
+
: undefined;
|
|
407
|
+
const junitOptions = resolveJunitReportOptions({
|
|
408
|
+
config: config.reports,
|
|
409
|
+
cli: {
|
|
410
|
+
junit: opts.junit || undefined,
|
|
411
|
+
noJunit: opts.noJunit || undefined,
|
|
412
|
+
junitOutput: opts.junitOutput,
|
|
413
|
+
},
|
|
414
|
+
});
|
|
415
|
+
const junitExternalOutputPath = opts.junitOutput
|
|
416
|
+
? resolve(resolveCwd(opts.cwd), opts.junitOutput)
|
|
417
|
+
: undefined;
|
|
418
|
+
const markdownOptions = resolveMarkdownReportOptions({
|
|
419
|
+
config: config.reports,
|
|
420
|
+
cli: {
|
|
421
|
+
markdown: opts.markdown === false ? false : undefined,
|
|
422
|
+
noMarkdown: opts.noMarkdown || undefined,
|
|
423
|
+
markdownOutput: opts.markdownOutput,
|
|
424
|
+
},
|
|
425
|
+
});
|
|
426
|
+
const markdownExternalOutputPath = opts.markdownOutput
|
|
427
|
+
? resolve(resolveCwd(opts.cwd), opts.markdownOutput)
|
|
428
|
+
: undefined;
|
|
429
|
+
const githubAnnotationsOptions = resolveGitHubAnnotationsOptions({
|
|
430
|
+
config: config.reports,
|
|
431
|
+
cli: {
|
|
432
|
+
githubAnnotations: opts.githubAnnotations || undefined,
|
|
433
|
+
noGitHubAnnotations: opts.noGitHubAnnotations || undefined,
|
|
434
|
+
githubAnnotationsOutput: opts.githubAnnotationsOutput,
|
|
435
|
+
},
|
|
436
|
+
});
|
|
437
|
+
const githubAnnotationsExternalOutputPath = opts.githubAnnotationsOutput
|
|
438
|
+
? resolve(resolveCwd(opts.cwd), opts.githubAnnotationsOutput)
|
|
439
|
+
: undefined;
|
|
440
|
+
const githubStepSummaryOptions = resolveGitHubStepSummaryOptions({
|
|
441
|
+
config: config.reports,
|
|
442
|
+
cli: {
|
|
443
|
+
githubStepSummary: opts.githubStepSummary || undefined,
|
|
444
|
+
noGitHubStepSummary: opts.noGitHubStepSummary || undefined,
|
|
445
|
+
},
|
|
446
|
+
});
|
|
447
|
+
const githubStepSummaryPath = process.env.GITHUB_STEP_SUMMARY?.trim() || undefined;
|
|
448
|
+
const result = await executeAudit(config, {
|
|
449
|
+
headed: opts.headed,
|
|
450
|
+
navigationTimeoutMs,
|
|
451
|
+
stepTimeoutMs,
|
|
452
|
+
noStartServer: opts.startServer === false,
|
|
453
|
+
projectNames: opts.project,
|
|
454
|
+
profileNames: opts.profile,
|
|
455
|
+
flowNames: opts.flow,
|
|
456
|
+
flowsOnly: opts.flowsOnly || undefined,
|
|
457
|
+
routesOnly: opts.routesOnly || undefined,
|
|
458
|
+
outputDir: opts.output,
|
|
459
|
+
html: opts.html === false ? false : undefined,
|
|
460
|
+
screenshots: opts.screenshots === false ? false : undefined,
|
|
461
|
+
fullPageScreenshots: opts.fullPageScreenshots || undefined,
|
|
462
|
+
signal: controller.signal,
|
|
463
|
+
noBaseline: noBaseline || undefined,
|
|
464
|
+
baselinePath: baselineOverride,
|
|
465
|
+
explicitBaselineRequired: Boolean(baselineOverride),
|
|
466
|
+
ciPolicy: resolvedCiPolicy,
|
|
467
|
+
sarif: sarifOptions,
|
|
468
|
+
sarifExternalOutputPath,
|
|
469
|
+
junit: junitOptions,
|
|
470
|
+
junitExternalOutputPath,
|
|
471
|
+
markdown: markdownOptions,
|
|
472
|
+
markdownExternalOutputPath,
|
|
473
|
+
githubAnnotations: githubAnnotationsOptions,
|
|
474
|
+
githubAnnotationsExternalOutputPath,
|
|
475
|
+
githubStepSummary: githubStepSummaryOptions.enabled,
|
|
476
|
+
githubStepSummaryPath,
|
|
477
|
+
progress,
|
|
478
|
+
});
|
|
479
|
+
if (opts.createBaseline) {
|
|
480
|
+
if (result.status === "failed") {
|
|
481
|
+
throw new Error("Cannot create a baseline from a failed audit.");
|
|
482
|
+
}
|
|
483
|
+
await createBaselineAfterAudit({
|
|
484
|
+
cwd: resolveCwd(opts.cwd),
|
|
485
|
+
configPath: opts.config,
|
|
486
|
+
result,
|
|
487
|
+
force: opts.force,
|
|
488
|
+
});
|
|
489
|
+
}
|
|
490
|
+
if (opts.json) {
|
|
491
|
+
writeJson(formatAuditJson(result));
|
|
492
|
+
}
|
|
493
|
+
else {
|
|
494
|
+
const capabilities = resolveTerminalCapabilities();
|
|
495
|
+
const body = formatAuditHuman(result, {
|
|
496
|
+
explicitCiFlagsUsed: explicitFlagsUsed,
|
|
497
|
+
minimumSeverity: resolvedCiPolicy.minimumSeverity,
|
|
498
|
+
sarifExternalPath: sarifExternalOutputPath,
|
|
499
|
+
junitExternalPath: junitExternalOutputPath,
|
|
500
|
+
markdownExternalPath: markdownExternalOutputPath,
|
|
501
|
+
githubAnnotationsExternalPath: githubAnnotationsExternalOutputPath,
|
|
502
|
+
githubStepSummaryWritten: result.githubStepSummaryWritten,
|
|
503
|
+
colorMode: parseColorMode(typeof opts.color === "string" ? opts.color : undefined),
|
|
504
|
+
capabilities,
|
|
505
|
+
presentationMode: resolveTerminalPresentationMode(capabilities),
|
|
506
|
+
verbose: opts.verbose || undefined,
|
|
507
|
+
});
|
|
508
|
+
writeStdout(prependHumanBrandHeader(body, capabilities));
|
|
509
|
+
}
|
|
510
|
+
process.exitCode = getAuditExitCode({
|
|
511
|
+
auditIncomplete: result.status !== "completed",
|
|
512
|
+
policyEvaluation: result.policyEvaluation,
|
|
513
|
+
});
|
|
514
|
+
}
|
|
515
|
+
catch (error) {
|
|
516
|
+
progress.fail("Audit failed");
|
|
517
|
+
if (opts.json) {
|
|
518
|
+
writeJson({
|
|
519
|
+
status: "error",
|
|
520
|
+
message: error instanceof ConfigError
|
|
521
|
+
? error.message
|
|
522
|
+
: error instanceof Error
|
|
523
|
+
? error.message
|
|
524
|
+
: String(error),
|
|
525
|
+
code: error instanceof ConfigError ? error.code : "AUDIT_FAILED",
|
|
526
|
+
issues: error instanceof ConfigError ? error.issues : undefined,
|
|
527
|
+
}, process.stdout);
|
|
528
|
+
}
|
|
529
|
+
writeStderr(error instanceof ConfigError
|
|
530
|
+
? error.format()
|
|
531
|
+
: error instanceof Error
|
|
532
|
+
? error.message
|
|
533
|
+
: String(error));
|
|
534
|
+
process.exitCode = 1;
|
|
535
|
+
}
|
|
536
|
+
finally {
|
|
537
|
+
progress.stop();
|
|
538
|
+
process.removeListener("SIGINT", onSignal);
|
|
539
|
+
process.removeListener("SIGTERM", onSignal);
|
|
540
|
+
}
|
|
541
|
+
});
|
|
542
|
+
program
|
|
543
|
+
.command("report [resultsPath]")
|
|
544
|
+
.description("Generate HTML, SARIF, JUnit, Markdown, or GitHub annotations reports from persisted audit results")
|
|
545
|
+
.option("--from <path>", "Path to a results.json file (alias for positional argument)")
|
|
546
|
+
.option("--format <format>", "Report format: html, sarif, junit, markdown, or github-annotations", "html")
|
|
547
|
+
.option("--output <path>", "Output directory for HTML or output file path for machine formats")
|
|
548
|
+
.option("--json", "Emit machine-readable JSON on stdout", false)
|
|
549
|
+
.option("--cwd <path>", "Directory used to resolve paths and configuration")
|
|
550
|
+
.addHelpText("after", `
|
|
551
|
+
When --format sarif is used, a11yst reads an existing results.json file,
|
|
552
|
+
generates SARIF 2.1.0 offline, and writes a11yst.sarif by default.
|
|
553
|
+
|
|
554
|
+
When --format junit is used, a11yst reads an existing results.json file,
|
|
555
|
+
generates JUnit XML offline, and writes a11yst.junit.xml by default.
|
|
556
|
+
|
|
557
|
+
When --format markdown is used, a11yst generates reports/a11yst.md offline.
|
|
558
|
+
|
|
559
|
+
When --format github-annotations is used, a11yst generates workflow commands
|
|
560
|
+
offline and writes github-annotations.txt by default.
|
|
561
|
+
|
|
562
|
+
No browser, server, baseline comparison, or policy re-evaluation runs.`)
|
|
563
|
+
.action(async function (resultsPath, opts) {
|
|
564
|
+
const progress = createCommandProgress(this, opts);
|
|
565
|
+
try {
|
|
566
|
+
progress.start("Regenerating report…");
|
|
567
|
+
const format = opts.format === "sarif"
|
|
568
|
+
? "sarif"
|
|
569
|
+
: opts.format === "junit"
|
|
570
|
+
? "junit"
|
|
571
|
+
: opts.format === "markdown"
|
|
572
|
+
? "markdown"
|
|
573
|
+
: opts.format === "github-annotations"
|
|
574
|
+
? "github-annotations"
|
|
575
|
+
: "html";
|
|
576
|
+
const result = await runReport({
|
|
577
|
+
cwd: resolveCwd(opts.cwd),
|
|
578
|
+
resultsPath: opts.from ?? resultsPath,
|
|
579
|
+
output: opts.output,
|
|
580
|
+
format,
|
|
581
|
+
});
|
|
582
|
+
progress.succeed("Report ready");
|
|
583
|
+
if (opts.json) {
|
|
584
|
+
writeJson(formatReportJson(result));
|
|
585
|
+
}
|
|
586
|
+
else {
|
|
587
|
+
writeStdout(formatReportHuman(result));
|
|
588
|
+
}
|
|
589
|
+
process.exitCode = 0;
|
|
590
|
+
}
|
|
591
|
+
catch (error) {
|
|
592
|
+
progress.fail("Report generation failed");
|
|
593
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
594
|
+
if (opts.json) {
|
|
595
|
+
writeJson({ status: "error", message }, process.stdout);
|
|
596
|
+
}
|
|
597
|
+
writeStderr(message);
|
|
598
|
+
process.exitCode = 1;
|
|
599
|
+
}
|
|
600
|
+
finally {
|
|
601
|
+
progress.stop();
|
|
602
|
+
}
|
|
603
|
+
});
|
|
604
|
+
program
|
|
605
|
+
.command("profiles")
|
|
606
|
+
.description("List accessibility profiles, capabilities, and limitations")
|
|
607
|
+
.option("--json", "Emit machine-readable JSON on stdout", false)
|
|
608
|
+
.action((opts) => {
|
|
609
|
+
const profiles = runProfiles();
|
|
610
|
+
if (opts.json) {
|
|
611
|
+
writeJson({ profiles });
|
|
612
|
+
}
|
|
613
|
+
else {
|
|
614
|
+
writeStdout(formatProfilesHuman(profiles));
|
|
615
|
+
}
|
|
616
|
+
process.exitCode = 0;
|
|
617
|
+
});
|
|
618
|
+
program
|
|
619
|
+
.command("doctor")
|
|
620
|
+
.description("Check environment readiness for a11yst")
|
|
621
|
+
.option("--json", "Emit machine-readable JSON on stdout", false)
|
|
622
|
+
.option("--cwd <path>", "Directory to check")
|
|
623
|
+
.action(async function (opts) {
|
|
624
|
+
const progress = createCommandProgress(this, opts);
|
|
625
|
+
try {
|
|
626
|
+
progress.start("Running diagnostics…");
|
|
627
|
+
const report = await runDoctor(resolveCwd(opts.cwd));
|
|
628
|
+
progress.succeed("Diagnostics complete");
|
|
629
|
+
if (opts.json) {
|
|
630
|
+
writeJson(formatDoctorJson(report));
|
|
631
|
+
}
|
|
632
|
+
else {
|
|
633
|
+
writeStdout(prependHumanBrandHeader(formatDoctorHuman(report)));
|
|
634
|
+
}
|
|
635
|
+
process.exitCode = exitCodeFromDoctorStatus(report.status);
|
|
636
|
+
}
|
|
637
|
+
catch (error) {
|
|
638
|
+
progress.fail("Diagnostics failed");
|
|
639
|
+
writeStderr(error instanceof Error ? error.message : String(error));
|
|
640
|
+
process.exitCode = 1;
|
|
641
|
+
}
|
|
642
|
+
finally {
|
|
643
|
+
progress.stop();
|
|
644
|
+
}
|
|
645
|
+
});
|
|
646
|
+
program
|
|
647
|
+
.command("baseline")
|
|
648
|
+
.description("Manage versioned accessibility baselines")
|
|
649
|
+
.addCommand(new Command("create")
|
|
650
|
+
.description("Create a baseline from audit results")
|
|
651
|
+
.option("--from <path>", "Results JSON path (defaults to latest results)")
|
|
652
|
+
.option("--force", "Overwrite an existing baseline file", false)
|
|
653
|
+
.option("--json", "Emit machine-readable JSON on stdout", false)
|
|
654
|
+
.option("--config <path>", "Path to a configuration file")
|
|
655
|
+
.option("--cwd <path>", "Directory to load configuration from")
|
|
656
|
+
.action(async (opts) => {
|
|
657
|
+
try {
|
|
658
|
+
const result = await runBaselineCreate({
|
|
659
|
+
cwd: resolveCwd(opts.cwd),
|
|
660
|
+
configPath: opts.config,
|
|
661
|
+
from: opts.from,
|
|
662
|
+
force: opts.force,
|
|
663
|
+
});
|
|
664
|
+
if (opts.json) {
|
|
665
|
+
writeJson(formatBaselineCreateJson(result));
|
|
666
|
+
}
|
|
667
|
+
else {
|
|
668
|
+
writeStdout(formatBaselineCreateHuman(result));
|
|
669
|
+
}
|
|
670
|
+
process.exitCode = 0;
|
|
671
|
+
}
|
|
672
|
+
catch (error) {
|
|
673
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
674
|
+
if (opts.json) {
|
|
675
|
+
writeJson({ status: "error", message }, process.stdout);
|
|
676
|
+
}
|
|
677
|
+
writeStderr(message);
|
|
678
|
+
process.exitCode = exitCodeFromError(error);
|
|
679
|
+
}
|
|
680
|
+
}))
|
|
681
|
+
.addCommand(new Command("status")
|
|
682
|
+
.description("Show baseline file status and latest comparison")
|
|
683
|
+
.option("--json", "Emit machine-readable JSON on stdout", false)
|
|
684
|
+
.option("--config <path>", "Path to a configuration file")
|
|
685
|
+
.option("--cwd <path>", "Directory to load configuration from")
|
|
686
|
+
.option("--baseline <path>", "Baseline file path override")
|
|
687
|
+
.action(async (opts) => {
|
|
688
|
+
try {
|
|
689
|
+
const result = await runBaselineStatus({
|
|
690
|
+
cwd: resolveCwd(opts.cwd),
|
|
691
|
+
configPath: opts.config,
|
|
692
|
+
baselineOverride: opts.baseline,
|
|
693
|
+
});
|
|
694
|
+
if (opts.json) {
|
|
695
|
+
writeJson(formatBaselineStatusJson(result));
|
|
696
|
+
}
|
|
697
|
+
else {
|
|
698
|
+
writeStdout(formatBaselineStatusHuman(result));
|
|
699
|
+
}
|
|
700
|
+
process.exitCode = 0;
|
|
701
|
+
}
|
|
702
|
+
catch (error) {
|
|
703
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
704
|
+
if (opts.json) {
|
|
705
|
+
writeJson({ status: "error", message }, process.stdout);
|
|
706
|
+
}
|
|
707
|
+
writeStderr(message);
|
|
708
|
+
process.exitCode = 1;
|
|
709
|
+
}
|
|
710
|
+
}))
|
|
711
|
+
.addCommand(new Command("update")
|
|
712
|
+
.description("Preview or apply baseline updates from audit results")
|
|
713
|
+
.option("--from <path>", "Results JSON path (defaults to latest results)")
|
|
714
|
+
.option("--dry-run", "Preview changes without writing", false)
|
|
715
|
+
.option("--accept-new", "Add new findings to the baseline", false)
|
|
716
|
+
.option("--remove-resolved", "Remove resolved findings from the baseline", false)
|
|
717
|
+
.option("--yes", "Apply changes without interactive confirmation", false)
|
|
718
|
+
.option("--json", "Emit machine-readable JSON on stdout", false)
|
|
719
|
+
.option("--config <path>", "Path to a configuration file")
|
|
720
|
+
.option("--cwd <path>", "Directory to load configuration from")
|
|
721
|
+
.action(async (opts) => {
|
|
722
|
+
try {
|
|
723
|
+
const result = await runBaselineUpdate({
|
|
724
|
+
cwd: resolveCwd(opts.cwd),
|
|
725
|
+
configPath: opts.config,
|
|
726
|
+
from: opts.from,
|
|
727
|
+
dryRun: opts.dryRun,
|
|
728
|
+
acceptNew: opts.acceptNew,
|
|
729
|
+
removeResolved: opts.removeResolved,
|
|
730
|
+
yes: opts.yes,
|
|
731
|
+
});
|
|
732
|
+
if (opts.json) {
|
|
733
|
+
writeJson(formatBaselineUpdateJson(result));
|
|
734
|
+
}
|
|
735
|
+
else {
|
|
736
|
+
writeStdout(formatBaselineUpdateHuman(result));
|
|
737
|
+
}
|
|
738
|
+
process.exitCode = 0;
|
|
739
|
+
}
|
|
740
|
+
catch (error) {
|
|
741
|
+
const pending = error;
|
|
742
|
+
if (pending.preview) {
|
|
743
|
+
if (opts.json) {
|
|
744
|
+
writeJson(formatBaselineUpdateJson(pending.preview));
|
|
745
|
+
}
|
|
746
|
+
else {
|
|
747
|
+
writeStdout(formatBaselineUpdateHuman(pending.preview));
|
|
748
|
+
}
|
|
749
|
+
writeStderr(error instanceof Error ? error.message : String(error));
|
|
750
|
+
process.exitCode = 2;
|
|
751
|
+
return;
|
|
752
|
+
}
|
|
753
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
754
|
+
if (opts.json) {
|
|
755
|
+
writeJson({ status: "error", message }, process.stdout);
|
|
756
|
+
}
|
|
757
|
+
writeStderr(message);
|
|
758
|
+
process.exitCode = exitCodeFromError(error);
|
|
759
|
+
}
|
|
760
|
+
}))
|
|
761
|
+
.addCommand(new Command("migrate")
|
|
762
|
+
.description("Migrate a baseline file to the current schema version")
|
|
763
|
+
.option("--dry-run", "Preview migration without writing", false)
|
|
764
|
+
.option("--yes", "Apply migration without interactive confirmation", false)
|
|
765
|
+
.option("--json", "Emit machine-readable JSON on stdout", false)
|
|
766
|
+
.option("--config <path>", "Path to a configuration file")
|
|
767
|
+
.option("--cwd <path>", "Directory to load configuration from")
|
|
768
|
+
.option("--baseline <path>", "Baseline file path override")
|
|
769
|
+
.action(async (opts) => {
|
|
770
|
+
try {
|
|
771
|
+
const result = await runBaselineMigrate({
|
|
772
|
+
cwd: resolveCwd(opts.cwd),
|
|
773
|
+
configPath: opts.config,
|
|
774
|
+
baselineOverride: opts.baseline,
|
|
775
|
+
dryRun: opts.dryRun,
|
|
776
|
+
yes: opts.yes,
|
|
777
|
+
});
|
|
778
|
+
if (opts.json) {
|
|
779
|
+
writeJson(formatBaselineMigrateJson(result));
|
|
780
|
+
}
|
|
781
|
+
else {
|
|
782
|
+
writeStdout(formatBaselineMigrateHuman(result));
|
|
783
|
+
}
|
|
784
|
+
process.exitCode = 0;
|
|
785
|
+
}
|
|
786
|
+
catch (error) {
|
|
787
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
788
|
+
if (opts.json) {
|
|
789
|
+
writeJson({ status: "error", message }, process.stdout);
|
|
790
|
+
}
|
|
791
|
+
writeStderr(message);
|
|
792
|
+
process.exitCode = exitCodeFromError(error);
|
|
793
|
+
}
|
|
794
|
+
}));
|
|
795
|
+
program
|
|
796
|
+
.command("findings")
|
|
797
|
+
.description("List findings from the latest audit results or a results file")
|
|
798
|
+
.option("--json", "Emit machine-readable JSON on stdout", false)
|
|
799
|
+
.option("--from <path>", "Results JSON path (defaults to latest results)")
|
|
800
|
+
.option("--config <path>", "Path to a configuration file")
|
|
801
|
+
.option("--cwd <path>", "Directory to load configuration from")
|
|
802
|
+
.option("--status <status>", "Filter by lifecycle status (repeatable)", collectFindingStatus, [])
|
|
803
|
+
.option("--disposition <disposition>", "Filter by classification disposition (repeatable)", collectFindingDisposition, [])
|
|
804
|
+
.option("--project <name>", "Filter by project (repeatable)", collectProjectNames, [])
|
|
805
|
+
.option("--rule <ruleId>", "Filter by rule id (repeatable)", collectRuleNames, [])
|
|
806
|
+
.option("--profile <id>", "Filter by accessibility profile (repeatable)", collectProfileNames, [])
|
|
807
|
+
.option("--flow <id>", "Filter by flow id (repeatable)", collectFlowNames, [])
|
|
808
|
+
.option("--checkpoint <id>", "Filter by checkpoint id (repeatable)", collectCheckpointNames, [])
|
|
809
|
+
.action(async (opts) => {
|
|
810
|
+
try {
|
|
811
|
+
const result = await runFindings({
|
|
812
|
+
cwd: resolveCwd(opts.cwd),
|
|
813
|
+
configPath: opts.config,
|
|
814
|
+
from: opts.from,
|
|
815
|
+
filters: {
|
|
816
|
+
status: opts.status,
|
|
817
|
+
disposition: opts.disposition,
|
|
818
|
+
project: opts.project,
|
|
819
|
+
rule: opts.rule,
|
|
820
|
+
profile: opts.profile,
|
|
821
|
+
flow: opts.flow,
|
|
822
|
+
checkpoint: opts.checkpoint,
|
|
823
|
+
},
|
|
824
|
+
});
|
|
825
|
+
if (opts.json) {
|
|
826
|
+
writeJson(formatFindingsJson(result));
|
|
827
|
+
}
|
|
828
|
+
else {
|
|
829
|
+
writeStdout(formatFindingsHuman(result));
|
|
830
|
+
}
|
|
831
|
+
process.exitCode = 0;
|
|
832
|
+
}
|
|
833
|
+
catch (error) {
|
|
834
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
835
|
+
if (opts.json) {
|
|
836
|
+
writeJson({ status: "error", message }, process.stdout);
|
|
837
|
+
}
|
|
838
|
+
writeStderr(message);
|
|
839
|
+
process.exitCode = 1;
|
|
840
|
+
}
|
|
841
|
+
});
|
|
842
|
+
program
|
|
843
|
+
.command("classify <finding-id>")
|
|
844
|
+
.description("Classify a finding in the baseline")
|
|
845
|
+
.requiredOption("--disposition <disposition>", "Classification disposition")
|
|
846
|
+
.requiredOption("--reason <text>", "Classification reason")
|
|
847
|
+
.option("--owner <name>", "Classification owner")
|
|
848
|
+
.option("--ticket <id>", "Tracking ticket id")
|
|
849
|
+
.option("--expires <date>", "Expiry date (YYYY-MM-DD)")
|
|
850
|
+
.option("--review <date>", "Review date (YYYY-MM-DD)")
|
|
851
|
+
.option("--notes <text>", "Additional notes")
|
|
852
|
+
.option("--from <path>", "Results JSON path (defaults to latest results)")
|
|
853
|
+
.option("--yes", "Apply without interactive confirmation", false)
|
|
854
|
+
.option("--json", "Emit machine-readable JSON on stdout", false)
|
|
855
|
+
.option("--config <path>", "Path to a configuration file")
|
|
856
|
+
.option("--cwd <path>", "Directory to load configuration from")
|
|
857
|
+
.action(async (findingId, opts) => {
|
|
858
|
+
try {
|
|
859
|
+
const result = await runClassify({
|
|
860
|
+
cwd: resolveCwd(opts.cwd),
|
|
861
|
+
configPath: opts.config,
|
|
862
|
+
findingId,
|
|
863
|
+
from: opts.from,
|
|
864
|
+
disposition: parseFindingsDisposition(opts.disposition),
|
|
865
|
+
reason: opts.reason,
|
|
866
|
+
owner: opts.owner,
|
|
867
|
+
ticket: opts.ticket,
|
|
868
|
+
expires: opts.expires,
|
|
869
|
+
review: opts.review,
|
|
870
|
+
notes: opts.notes,
|
|
871
|
+
yes: opts.yes,
|
|
872
|
+
});
|
|
873
|
+
if (opts.json) {
|
|
874
|
+
writeJson(formatClassifyJson(result));
|
|
875
|
+
}
|
|
876
|
+
else {
|
|
877
|
+
writeStdout(formatClassifyHuman(result));
|
|
878
|
+
}
|
|
879
|
+
process.exitCode = 0;
|
|
880
|
+
}
|
|
881
|
+
catch (error) {
|
|
882
|
+
const preview = formatClassifyPreviewFromError(error);
|
|
883
|
+
if (preview) {
|
|
884
|
+
if (opts.json) {
|
|
885
|
+
writeJson(formatClassifyJson(preview));
|
|
886
|
+
}
|
|
887
|
+
else {
|
|
888
|
+
writeStdout(formatClassifyHuman(preview));
|
|
889
|
+
}
|
|
890
|
+
writeStderr(error instanceof Error ? error.message : String(error));
|
|
891
|
+
process.exitCode = 2;
|
|
892
|
+
return;
|
|
893
|
+
}
|
|
894
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
895
|
+
if (opts.json) {
|
|
896
|
+
writeJson({ status: "error", message }, process.stdout);
|
|
897
|
+
}
|
|
898
|
+
writeStderr(message);
|
|
899
|
+
process.exitCode = 1;
|
|
900
|
+
}
|
|
901
|
+
});
|
|
902
|
+
program
|
|
903
|
+
.command("unclassify <finding-id>")
|
|
904
|
+
.description("Remove a finding classification from the baseline")
|
|
905
|
+
.option("--from <path>", "Results JSON path (defaults to latest results)")
|
|
906
|
+
.option("--yes", "Apply without interactive confirmation", false)
|
|
907
|
+
.option("--json", "Emit machine-readable JSON on stdout", false)
|
|
908
|
+
.option("--config <path>", "Path to a configuration file")
|
|
909
|
+
.option("--cwd <path>", "Directory to load configuration from")
|
|
910
|
+
.action(async (findingId, opts) => {
|
|
911
|
+
try {
|
|
912
|
+
const result = await runUnclassify({
|
|
913
|
+
cwd: resolveCwd(opts.cwd),
|
|
914
|
+
configPath: opts.config,
|
|
915
|
+
findingId,
|
|
916
|
+
from: opts.from,
|
|
917
|
+
yes: opts.yes,
|
|
918
|
+
});
|
|
919
|
+
if (opts.json) {
|
|
920
|
+
writeJson(formatUnclassifyJson(result));
|
|
921
|
+
}
|
|
922
|
+
else {
|
|
923
|
+
writeStdout(formatUnclassifyHuman(result));
|
|
924
|
+
}
|
|
925
|
+
process.exitCode = 0;
|
|
926
|
+
}
|
|
927
|
+
catch (error) {
|
|
928
|
+
const preview = formatUnclassifyPreviewFromError(error);
|
|
929
|
+
if (preview) {
|
|
930
|
+
if (opts.json) {
|
|
931
|
+
writeJson(formatUnclassifyJson(preview));
|
|
932
|
+
}
|
|
933
|
+
else {
|
|
934
|
+
writeStdout(formatUnclassifyHuman(preview));
|
|
935
|
+
}
|
|
936
|
+
writeStderr(error instanceof Error ? error.message : String(error));
|
|
937
|
+
process.exitCode = 2;
|
|
938
|
+
return;
|
|
939
|
+
}
|
|
940
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
941
|
+
if (opts.json) {
|
|
942
|
+
writeJson({ status: "error", message }, process.stdout);
|
|
943
|
+
}
|
|
944
|
+
writeStderr(message);
|
|
945
|
+
process.exitCode = 1;
|
|
946
|
+
}
|
|
947
|
+
});
|
|
948
|
+
return program;
|
|
949
|
+
}
|
|
950
|
+
//# sourceMappingURL=command-program.js.map
|