@vertaaux/cli 0.5.0 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/README.md +28 -1
  3. package/dist/app/interactive-app.d.ts +2 -0
  4. package/dist/app/interactive-app.d.ts.map +1 -1
  5. package/dist/app/interactive-app.js +26 -7
  6. package/dist/app/menu/categories.d.ts +2 -2
  7. package/dist/app/menu/categories.d.ts.map +1 -1
  8. package/dist/app/menu/categories.js +3 -18
  9. package/dist/app/views/command-runner.d.ts.map +1 -1
  10. package/dist/app/views/command-runner.js +52 -9
  11. package/dist/app/views/help-overlay.d.ts +1 -1
  12. package/dist/app/views/help-overlay.d.ts.map +1 -1
  13. package/dist/app/views/help-overlay.js +5 -4
  14. package/dist/commands/a11y.d.ts +4 -2
  15. package/dist/commands/a11y.d.ts.map +1 -1
  16. package/dist/commands/a11y.js +82 -9
  17. package/dist/commands/audit/index.d.ts.map +1 -1
  18. package/dist/commands/audit/index.js +33 -8
  19. package/dist/commands/audit/output.d.ts.map +1 -1
  20. package/dist/commands/audit/output.js +3 -4
  21. package/dist/commands/audit/policy.d.ts +10 -2
  22. package/dist/commands/audit/policy.d.ts.map +1 -1
  23. package/dist/commands/audit/policy.js +47 -2
  24. package/dist/commands/audit/types.d.ts +1 -0
  25. package/dist/commands/audit/types.d.ts.map +1 -1
  26. package/dist/commands/baseline.d.ts +1 -0
  27. package/dist/commands/baseline.d.ts.map +1 -1
  28. package/dist/commands/baseline.js +26 -12
  29. package/dist/commands/comment.d.ts.map +1 -1
  30. package/dist/commands/comment.js +8 -7
  31. package/dist/commands/compare.js +3 -3
  32. package/dist/commands/diff.d.ts +2 -0
  33. package/dist/commands/diff.d.ts.map +1 -1
  34. package/dist/commands/diff.js +23 -12
  35. package/dist/commands/doc.js +2 -2
  36. package/dist/commands/doctor.js +2 -2
  37. package/dist/commands/explain.js +3 -3
  38. package/dist/commands/fix-all.js +2 -2
  39. package/dist/commands/fix-plan.d.ts.map +1 -1
  40. package/dist/commands/fix-plan.js +4 -3
  41. package/dist/commands/fix.js +5 -5
  42. package/dist/commands/patch-review.js +2 -2
  43. package/dist/commands/policy.js +2 -2
  44. package/dist/commands/release-notes.js +3 -3
  45. package/dist/commands/suggest.d.ts.map +1 -1
  46. package/dist/commands/suggest.js +28 -2
  47. package/dist/commands/triage.js +3 -3
  48. package/dist/commands/verify.js +3 -3
  49. package/dist/config/schema.d.ts +4 -0
  50. package/dist/config/schema.d.ts.map +1 -1
  51. package/dist/index.js +3 -2
  52. package/dist/output/envelope.d.ts +8 -1
  53. package/dist/output/envelope.d.ts.map +1 -1
  54. package/dist/output/envelope.js +24 -6
  55. package/dist/policy/schema.d.ts +137 -0
  56. package/dist/policy/schema.d.ts.map +1 -1
  57. package/dist/policy/schema.js +107 -0
  58. package/dist/prompts/command-catalog.js +9 -9
  59. package/dist/utils/client.d.ts.map +1 -1
  60. package/dist/utils/client.js +30 -28
  61. package/dist/utils/root-args.d.ts +12 -0
  62. package/dist/utils/root-args.d.ts.map +1 -0
  63. package/dist/utils/root-args.js +44 -0
  64. package/dist/utils/stdin.d.ts +7 -0
  65. package/dist/utils/stdin.d.ts.map +1 -1
  66. package/dist/utils/stdin.js +32 -2
  67. package/node_modules/@vertaaux/tui/dist/index.cjs +505 -9
  68. package/node_modules/@vertaaux/tui/dist/index.cjs.map +1 -1
  69. package/node_modules/@vertaaux/tui/dist/index.js +502 -8
  70. package/node_modules/@vertaaux/tui/dist/index.js.map +1 -1
  71. package/node_modules/@vertaaux/tui/package.json +2 -3
  72. package/node_modules/chalk/license +9 -0
  73. package/node_modules/chalk/package.json +83 -0
  74. package/node_modules/chalk/readme.md +297 -0
  75. package/node_modules/chalk/source/index.d.ts +325 -0
  76. package/node_modules/chalk/source/index.js +225 -0
  77. package/node_modules/chalk/source/utilities.js +33 -0
  78. package/node_modules/chalk/source/vendor/ansi-styles/index.d.ts +236 -0
  79. package/node_modules/chalk/source/vendor/ansi-styles/index.js +223 -0
  80. package/node_modules/chalk/source/vendor/supports-color/browser.d.ts +1 -0
  81. package/node_modules/chalk/source/vendor/supports-color/browser.js +34 -0
  82. package/node_modules/chalk/source/vendor/supports-color/index.d.ts +55 -0
  83. package/node_modules/chalk/source/vendor/supports-color/index.js +190 -0
  84. package/package.json +9 -2
package/CHANGELOG.md CHANGED
@@ -5,6 +5,25 @@ All notable changes to `@vertaaux/cli` will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [Unreleased]
9
+
10
+ ## [0.5.1] - 2026-04-07
11
+
12
+ ### Added
13
+
14
+ - **`--profile <name>` flag on `vertaa audit`** — Phase 1.5 of audit profiles is now live on npm. `vertaa audit <url> --profile wcag-aa` (and any profile with a `categories` subset) skips auditors in the cloud worker rather than running all 7 and filtering post-run. Built-in profiles: `wcag-aa`, `conversion-focus`, `quick-ux`, `ci-gate`, `compliance`. The CLI passes `profile.categories` in the POST body via `apiRequest` when a category subset is resolved; the SDK path is used when no subset applies. See `cli/tests/commands/audit-handler.test.ts` for coverage.
15
+ - **`a11y` command upgrade** — `vertaa a11y <url>` now calls the dedicated `/v1/a11y/audit` endpoint with multi-engine analysis (axe-core + AccessLint + custom analyzers). Added `--mode`, `--fail-on-score`, `--min-impact`, and `--fail-on-findings` flags.
16
+ - **Source tracking** — All CLI-triggered audits now include `source: "cli"` metadata for dashboard visibility.
17
+
18
+ ### Fixed
19
+
20
+ - **Profile-filtered categories now report `null` instead of a false `100`** — Excluded categories in `scores` are `null` (not a fabricated "perfect 100" from `calculateScore([])`). `metadata.filtered_categories` lists profile exclusions; `metadata.skipped_checks` is reserved for runtime failures. `metadata.partial` stays `false` for profile-filtered audits. Server-side fix shipped in PR #375; this release gets it onto the npm-published CLI output path.
21
+
22
+ ### Changed
23
+
24
+ - **SDK upgraded to `@vertaaux/sdk@2.0.0`** — Updated to match v2 API surface with snake_case parameter conventions.
25
+ - **Machine-mode hardening** — Payload output routes consistently through the data writer; enveloped JSON input is unwrapped uniformly across `baseline`, `comment`, and `diff` commands.
26
+
8
27
  ## [0.5.0] - 2026-03-19
9
28
 
10
29
  ### Added
package/README.md CHANGED
@@ -119,11 +119,27 @@ All AI commands require authentication (`vertaa login` or `VERTAAUX_API_KEY`). T
119
119
  | `upload <file>` | Upload audit results to cloud storage |
120
120
  | `download <id>` | Download audit results from cloud storage |
121
121
 
122
+ ### Accessibility
123
+
124
+ | Command | Description |
125
+ |---------|-------------|
126
+ | `a11y <url>` | Multi-engine accessibility audit (axe-core + AccessLint + custom analyzers) |
127
+
128
+ The `a11y` command uses a dedicated API endpoint (`/v1/a11y/audit`) and returns WCAG-mapped findings with structured fix suggestions and fixability ratings.
129
+
130
+ | Option | Description |
131
+ |--------|-------------|
132
+ | `--mode <basic\|standard\|deep>` | Audit depth (default: `basic`) |
133
+ | `--min-impact <minor\|moderate\|serious\|critical>` | Minimum impact level to report |
134
+ | `--fail-on-score <0-100>` | Exit 1 if accessibility score below this value |
135
+ | `--fail-on-findings <n>` | Exit 1 if critical+serious findings exceed n |
136
+ | `--format <json\|md>` | Output format (default: `json`) |
137
+ | `--timeout <ms>` | Wait timeout (default: 60000) |
138
+
122
139
  ### Aliases
123
140
 
124
141
  | Command | Alias For |
125
142
  |---------|-----------|
126
- | `a11y <url>` | Accessibility-focused audit (filters for a11y issues) |
127
143
  | `scan <url>` | UX scan (alias for audit) |
128
144
  | `compare <urlA> <urlB>` | Compare audits of two URLs (also supports `--before`/`--after` for LLM-powered comparison) |
129
145
 
@@ -134,6 +150,7 @@ Formats are **per-command**, not global. Each command supports a different set o
134
150
  | Command | Formats | Default |
135
151
  |---------|---------|---------|
136
152
  | `audit` | `human`, `json`, `sarif`, `junit`, `html` | `human` |
153
+ | `a11y` | `json`, `md` | `json` |
137
154
  | `comment` | `json`, `markdown` | `markdown` |
138
155
  | `explain` | `human`, `json` | `human` |
139
156
  | `policy show` | `json`, `yaml` | `yaml` |
@@ -419,6 +436,16 @@ vertaa error: invalid value for --mode
419
436
  │ valid: basic, standard, deep
420
437
  ```
421
438
 
439
+ ## AI Agent Skill
440
+
441
+ Teach your AI coding agent (Claude Code, Cursor, Codex, Copilot, Gemini CLI, etc.) how to use the VertaaUX CLI:
442
+
443
+ ```bash
444
+ npx skills add VertaaUX/agent-skills
445
+ ```
446
+
447
+ Covers all CLI commands, CI/CD setup, SDK integration, and 10 use-case playbooks. Published on [skills.sh](https://skills.sh/VertaaUX/agent-skills).
448
+
422
449
  ## Related
423
450
 
424
451
  - [Migration Guide](./MIGRATION.md) -- Breaking changes in this release
@@ -37,6 +37,8 @@ export declare class InteractiveApp {
37
37
  private frameIndex;
38
38
  private version;
39
39
  private resolveRun;
40
+ /** Saved menu view so we can restore it after help overlay */
41
+ private savedMenuView;
40
42
  constructor(output?: NodeJS.WritableStream);
41
43
  /**
42
44
  * Start the interactive app.
@@ -1 +1 @@
1
- {"version":3,"file":"interactive-app.d.ts","sourceRoot":"","sources":["../../src/app/interactive-app.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAUH,OAAO,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAa,MAAM,YAAY,CAAC;AAWnE;;;;;;;GAOG;AACH,qBAAa,cAAc;IACzB,yDAAyD;IACzD,MAAM,EAAE,QAAQ,CAId;IAEF;;;OAGG;IACH,OAAO,CAAC,OAAO,CAAS;IAExB,sCAAsC;IACtC,UAAU,UAAS;IAEnB,OAAO,CAAC,MAAM,CAAwB;IACtC,OAAO,CAAC,SAAS,CAA+C;IAChE,OAAO,CAAC,WAAW,CAA8C;IACjE,OAAO,CAAC,UAAU,CAAK;IACvB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,UAAU,CAA6B;gBAEnC,MAAM,GAAE,MAAM,CAAC,cAA+B;IAQ1D;;;;;;OAMG;IACG,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;IAuB1B;;;;OAIG;IACH,UAAU,IAAI,MAAM;IA6BpB;;;;;;OAMG;IACH,MAAM,IAAI,IAAI;IAMd;;;;;;;;OAQG;IACG,OAAO,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAY/C;;OAEG;IACG,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;IAUhC;;;;;OAKG;IACH,OAAO,IAAI,IAAI;IAsBf;;;;OAIG;IACH,MAAM,IAAI,IAAI;IAoBd;;OAEG;IACH,OAAO,IAAI,IAAI;IAgCf,8EAA8E;IAC9E,aAAa,IAAI,MAAM;IAMvB,OAAO,CAAC,SAAS;IAUjB,OAAO,CAAC,QAAQ;IAOhB,OAAO,CAAC,QAAQ,CAWd;IAEF,OAAO,CAAC,SAAS,CAmCf;CACH"}
1
+ {"version":3,"file":"interactive-app.d.ts","sourceRoot":"","sources":["../../src/app/interactive-app.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAUH,OAAO,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAa,MAAM,YAAY,CAAC;AAYnE;;;;;;;GAOG;AACH,qBAAa,cAAc;IACzB,yDAAyD;IACzD,MAAM,EAAE,QAAQ,CAId;IAEF;;;OAGG;IACH,OAAO,CAAC,OAAO,CAAS;IAExB,sCAAsC;IACtC,UAAU,UAAS;IAEnB,OAAO,CAAC,MAAM,CAAwB;IACtC,OAAO,CAAC,SAAS,CAA+C;IAChE,OAAO,CAAC,WAAW,CAA8C;IACjE,OAAO,CAAC,UAAU,CAAK;IACvB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,UAAU,CAA6B;IAC/C,8DAA8D;IAC9D,OAAO,CAAC,aAAa,CAA4B;gBAErC,MAAM,GAAE,MAAM,CAAC,cAA+B;IAQ1D;;;;;;OAMG;IACG,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;IAuB1B;;;;OAIG;IACH,UAAU,IAAI,MAAM;IA6BpB;;;;;;OAMG;IACH,MAAM,IAAI,IAAI;IAMd;;;;;;;;OAQG;IACG,OAAO,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAY/C;;OAEG;IACG,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;IAUhC;;;;;OAKG;IACH,OAAO,IAAI,IAAI;IAsBf;;;;OAIG;IACH,MAAM,IAAI,IAAI;IAoBd;;OAEG;IACH,OAAO,IAAI,IAAI;IAgCf,8EAA8E;IAC9E,aAAa,IAAI,MAAM;IAMvB,OAAO,CAAC,SAAS;IAUjB,OAAO,CAAC,QAAQ;IAOhB,OAAO,CAAC,QAAQ,CAWd;IAEF,OAAO,CAAC,SAAS,CAmDf;CACH"}
@@ -17,6 +17,7 @@ import { renderHeader } from "./layout/header.js";
17
17
  import { renderFooter } from "./layout/footer.js";
18
18
  import { renderCanvas } from "./layout/canvas.js";
19
19
  import { getVersion } from "../ui/banner.js";
20
+ import { HelpOverlayView } from "./views/help-overlay.js";
20
21
  // Default keyboard shortcuts shown in the footer when no view is active
21
22
  const DEFAULT_SHORTCUTS = ["↑↓ navigate", "↵ select", "F1 help", "^C quit"];
22
23
  // Shortcuts shown when a command view is active
@@ -49,6 +50,8 @@ export class InteractiveApp {
49
50
  frameIndex = 0;
50
51
  version;
51
52
  resolveRun = null;
53
+ /** Saved menu view so we can restore it after help overlay */
54
+ savedMenuView = null;
52
55
  constructor(output = process.stderr) {
53
56
  this.output = output;
54
57
  this.version = getVersion();
@@ -273,7 +276,29 @@ export class InteractiveApp {
273
276
  const key = data.toString();
274
277
  const ctrl = key.length === 1 && data[0] < 32 && data[0] !== 27;
275
278
  const meta = key.startsWith("\x1b") && key.length > 1;
276
- // Delegate to active view first
279
+ // F1 is always handled at app level (before view delegation)
280
+ if (key === "\x1bOP" || key === "\x1b[11~") {
281
+ if (this._state.screen === "help") {
282
+ // Restore saved view
283
+ this._state.activeView = this.savedMenuView;
284
+ this.savedMenuView = null;
285
+ this._state.screen = "menu";
286
+ }
287
+ else {
288
+ // Save current view and show help overlay
289
+ this.savedMenuView = this._state.activeView;
290
+ this._state.activeView = new HelpOverlayView(() => {
291
+ this._state.activeView = this.savedMenuView;
292
+ this.savedMenuView = null;
293
+ this._state.screen = "menu";
294
+ this.redraw();
295
+ });
296
+ this._state.screen = "help";
297
+ }
298
+ this.redraw();
299
+ return;
300
+ }
301
+ // Delegate to active view
277
302
  if (this._state.activeView?.handleKey) {
278
303
  const consumed = this._state.activeView.handleKey(key, ctrl, meta);
279
304
  if (consumed)
@@ -293,12 +318,6 @@ export class InteractiveApp {
293
318
  process.exit(0);
294
319
  }
295
320
  }
296
- else if (key === "\x1bOP" || key === "\x1b[11~") {
297
- // F1 — toggle help
298
- const next = this._state.screen === "help" ? "menu" : "help";
299
- this._state.screen = next;
300
- this.redraw();
301
- }
302
321
  else if (key === "\x1b" || key === "\x1b[") {
303
322
  // Escape or Escape sequence
304
323
  if (this._state.screen === "command" || this._state.screen === "help") {
@@ -1,12 +1,12 @@
1
1
  /**
2
2
  * Command category definitions for the interactive menu.
3
3
  *
4
- * 6 groups of 24 commands matching Commander.js registrations in index.ts.
4
+ * 6 groups of 23 interactive-safe commands matching the TUI runner.
5
5
  * Each MenuItem.value must exactly match the Commander command name.
6
6
  */
7
7
  import type { CommandCategory, MenuItem } from "../types.js";
8
8
  /**
9
- * All 24 commands organized into 6 semantic groups.
9
+ * All interactive-safe commands organized into 6 semantic groups.
10
10
  *
11
11
  * Groups follow a natural user workflow:
12
12
  * Audit → Results → Fixes → Reports → Project → Account
@@ -1 +1 @@
1
- {"version":3,"file":"categories.d.ts","sourceRoot":"","sources":["../../../src/app/menu/categories.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAE7D;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,EAAE,eAAe,EAiK/C,CAAC;AAEF;;;GAGG;AACH,wBAAgB,YAAY,IAAI,QAAQ,EAAE,CAEzC"}
1
+ {"version":3,"file":"categories.d.ts","sourceRoot":"","sources":["../../../src/app/menu/categories.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAE7D;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,EAAE,eAAe,EAkJ/C,CAAC;AAEF;;;GAGG;AACH,wBAAgB,YAAY,IAAI,QAAQ,EAAE,CAEzC"}
@@ -1,11 +1,11 @@
1
1
  /**
2
2
  * Command category definitions for the interactive menu.
3
3
  *
4
- * 6 groups of 24 commands matching Commander.js registrations in index.ts.
4
+ * 6 groups of 23 interactive-safe commands matching the TUI runner.
5
5
  * Each MenuItem.value must exactly match the Commander command name.
6
6
  */
7
7
  /**
8
- * All 24 commands organized into 6 semantic groups.
8
+ * All interactive-safe commands organized into 6 semantic groups.
9
9
  *
10
10
  * Groups follow a natural user workflow:
11
11
  * Audit → Results → Fixes → Reports → Project → Account
@@ -84,11 +84,6 @@ export const COMMAND_CATEGORIES = [
84
84
  value: "fix-plan",
85
85
  description: "Generate a structured remediation plan",
86
86
  },
87
- {
88
- name: "Review Patch",
89
- value: "patch-review",
90
- description: "Review a generated patch before applying",
91
- },
92
87
  ],
93
88
  },
94
89
  {
@@ -104,11 +99,6 @@ export const COMMAND_CATEGORIES = [
104
99
  value: "comment",
105
100
  description: "Add audit annotations to source code",
106
101
  },
107
- {
108
- name: "Release Notes",
109
- value: "release-notes",
110
- description: "Generate release notes from audit improvements",
111
- },
112
102
  ],
113
103
  },
114
104
  {
@@ -134,11 +124,6 @@ export const COMMAND_CATEGORIES = [
134
124
  value: "download",
135
125
  description: "Download audit results from VertaaUX cloud",
136
126
  },
137
- {
138
- name: "Verify Quality Gate",
139
- value: "verify",
140
- description: "Check if results meet quality gate thresholds",
141
- },
142
127
  ],
143
128
  },
144
129
  {
@@ -160,7 +145,7 @@ export const COMMAND_CATEGORIES = [
160
145
  description: "Show currently authenticated account",
161
146
  },
162
147
  {
163
- name: "Health Check",
148
+ name: "Health Check (doctor)",
164
149
  value: "doctor",
165
150
  description: "Run diagnostics on CLI configuration and connectivity",
166
151
  },
@@ -1 +1 @@
1
- {"version":3,"file":"command-runner.d.ts","sourceRoot":"","sources":["../../../src/app/views/command-runner.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAWH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAwF5D,qBAAa,iBAAkB,YAAW,WAAW;IACnD,OAAO,CAAC,MAAM,CAAgB;IAC9B,OAAO,CAAC,UAAU,CAAM;IACxB,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,GAAG,CAAiB;IAC5B,OAAO,CAAC,QAAQ,CAAa;IAG7B,OAAO,CAAC,OAAO,CAAW;IAC1B,OAAO,CAAC,aAAa,CAA8B;IACnD,OAAO,CAAC,eAAe,CAAK;IAC5B,OAAO,CAAC,WAAW,CAAM;IAGzB,OAAO,CAAC,oBAAoB,CAA6B;IAGzD,OAAO,CAAC,cAAc,CAAwB;IAG9C,OAAO,CAAC,YAAY,CAAgB;IAGpC,OAAO,CAAC,YAAY,CAAK;gBAGvB,YAAY,EAAE,MAAM,EACpB,GAAG,EAAE,cAAc,EACnB,QAAQ,EAAE,MAAM,IAAI;IAStB,MAAM,IAAI,MAAM;IA0EhB,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO;IAsFxD,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;YA+BhB,cAAc;CA0F7B"}
1
+ {"version":3,"file":"command-runner.d.ts","sourceRoot":"","sources":["../../../src/app/views/command-runner.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAaH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAsH5D,qBAAa,iBAAkB,YAAW,WAAW;IACnD,OAAO,CAAC,MAAM,CAAgB;IAC9B,OAAO,CAAC,UAAU,CAAM;IACxB,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,GAAG,CAAiB;IAC5B,OAAO,CAAC,QAAQ,CAAa;IAG7B,OAAO,CAAC,OAAO,CAAW;IAC1B,OAAO,CAAC,aAAa,CAA8B;IACnD,OAAO,CAAC,eAAe,CAAK;IAC5B,OAAO,CAAC,WAAW,CAAM;IAGzB,OAAO,CAAC,oBAAoB,CAA6B;IAGzD,OAAO,CAAC,cAAc,CAAwB;IAG9C,OAAO,CAAC,YAAY,CAAgB;IAGpC,OAAO,CAAC,YAAY,CAAK;gBAGvB,YAAY,EAAE,MAAM,EACpB,GAAG,EAAE,cAAc,EACnB,QAAQ,EAAE,MAAM,IAAI;IAStB,MAAM,IAAI,MAAM;IA0EhB,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO;IAsFxD,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;YA+BhB,cAAc;CA4G7B"}
@@ -11,6 +11,8 @@
11
11
  *
12
12
  * Each command's handler is called directly (no program.parse()).
13
13
  */
14
+ import fs from "fs";
15
+ import path from "path";
14
16
  import { dim, bold, colorize, brand, renderError, setRendererOverride, setKeyboardOverrideActive, renderStepList, getTerminalWidth, getTerminalHeight, } from "@vertaaux/tui";
15
17
  import { setOutputBuffer } from "../../output/envelope.js";
16
18
  // Command handler imports
@@ -38,6 +40,17 @@ import { handleVerify } from "../../commands/verify.js";
38
40
  import { handleLogin, handleLogout, handleWhoami } from "../../commands/login.js";
39
41
  import { handleDoctor } from "../../commands/doctor.js";
40
42
  import { handlePolicy } from "../../commands/policy.js";
43
+ function resolveJobOrFile(value) {
44
+ const trimmed = value.trim();
45
+ const resolved = path.resolve(process.cwd(), trimmed);
46
+ if (trimmed.toLowerCase().endsWith(".json") ||
47
+ trimmed.includes("/") ||
48
+ trimmed.includes("\\") ||
49
+ fs.existsSync(resolved)) {
50
+ return { file: trimmed };
51
+ }
52
+ return { job: trimmed };
53
+ }
41
54
  /**
42
55
  * Get the argument definitions for a command.
43
56
  */
@@ -49,10 +62,24 @@ function getArgDefs(cmd) {
49
62
  return [{ label: "Target URL", key: "url" }];
50
63
  case "status":
51
64
  return [{ label: "Job ID", key: "jobId" }];
65
+ case "diff":
66
+ case "triage":
67
+ case "explain":
68
+ case "fix-plan":
69
+ case "doc":
70
+ case "baseline":
71
+ return [{ label: "Audit job ID or JSON file", key: "auditSource" }];
72
+ case "compare":
73
+ return [
74
+ { label: "URL A", key: "urlA" },
75
+ { label: "URL B", key: "urlB" },
76
+ ];
52
77
  case "fix":
53
78
  return [{ label: "Job ID to fix", key: "jobId" }];
54
79
  case "suggest":
55
80
  return [{ label: "Describe the fix you need", key: "intent" }];
81
+ case "comment":
82
+ return [{ label: "Audit JSON file", key: "input" }];
56
83
  case "fix-all":
57
84
  return [
58
85
  { label: "Job ID", key: "jobId" },
@@ -261,7 +288,7 @@ export class CommandRunnerView {
261
288
  });
262
289
  }
263
290
  // Set overrides so command handlers use our canvas renderer,
264
- // don't steal stdin, and buffer their output instead of writing to stderr.
291
+ // don't steal stdin, and buffer their output instead of writing to stdout.
265
292
  this.status = "running";
266
293
  setRendererOverride(this.canvasRenderer);
267
294
  setKeyboardOverrideActive(true);
@@ -312,28 +339,39 @@ export class CommandRunnerView {
312
339
  });
313
340
  break;
314
341
  case "diff":
315
- await handleDiff({});
342
+ await handleDiff(args.auditSource
343
+ ? (() => {
344
+ const source = resolveJobOrFile(args.auditSource);
345
+ return source.file
346
+ ? { fromFile: source.file }
347
+ : { jobIdArg: source.job };
348
+ })()
349
+ : {});
316
350
  break;
317
351
  case "compare":
318
- await handleCompare({});
352
+ await handleCompare({
353
+ urlA: args.urlA,
354
+ urlB: args.urlB,
355
+ wait: true,
356
+ });
319
357
  break;
320
358
  case "triage":
321
- await handleTriage({});
359
+ await handleTriage(resolveJobOrFile(args.auditSource));
322
360
  break;
323
361
  case "explain":
324
- await handleExplain({});
362
+ await handleExplain(resolveJobOrFile(args.auditSource));
325
363
  break;
326
364
  case "fix-plan":
327
- await handleFixPlan({});
365
+ await handleFixPlan(resolveJobOrFile(args.auditSource));
328
366
  break;
329
367
  case "patch-review":
330
368
  await handlePatchReview({});
331
369
  break;
332
370
  case "doc":
333
- await handleDoc({});
371
+ await handleDoc(resolveJobOrFile(args.auditSource));
334
372
  break;
335
373
  case "comment":
336
- await handleComment({});
374
+ await handleComment({ input: args.input });
337
375
  break;
338
376
  case "release-notes":
339
377
  await handleReleaseNotes({});
@@ -342,7 +380,12 @@ export class CommandRunnerView {
342
380
  await handleInit({});
343
381
  break;
344
382
  case "baseline":
345
- await handleBaseline(undefined, {});
383
+ await (() => {
384
+ const source = resolveJobOrFile(args.auditSource);
385
+ return source.file
386
+ ? handleBaseline(undefined, { fromFile: source.file })
387
+ : handleBaseline(source.job, {});
388
+ })();
346
389
  break;
347
390
  case "upload":
348
391
  await handleUpload(undefined, {});
@@ -15,7 +15,7 @@ export declare class HelpOverlayView implements CommandView {
15
15
  constructor(onDismiss: () => void);
16
16
  /** Render the help overlay as a box */
17
17
  render(): string;
18
- /** Handle H and Esc to dismiss the overlay */
18
+ /** Handle Esc to dismiss the overlay (F1 toggle handled at app level) */
19
19
  handleKey(key: string, _ctrl: boolean, _meta: boolean): boolean;
20
20
  }
21
21
  //# sourceMappingURL=help-overlay.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"help-overlay.d.ts","sourceRoot":"","sources":["../../../src/app/views/help-overlay.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAY/C;;;;GAIG;AACH,qBAAa,eAAgB,YAAW,WAAW;IACjD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAa;gBAE3B,SAAS,EAAE,MAAM,IAAI;IAIjC,uCAAuC;IACvC,MAAM,IAAI,MAAM;IAehB,8CAA8C;IAC9C,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO;CAOhE"}
1
+ {"version":3,"file":"help-overlay.d.ts","sourceRoot":"","sources":["../../../src/app/views/help-overlay.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAY/C;;;;GAIG;AACH,qBAAa,eAAgB,YAAW,WAAW;IACjD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAa;gBAE3B,SAAS,EAAE,MAAM,IAAI;IAIjC,uCAAuC;IACvC,MAAM,IAAI,MAAM;IAehB,yEAAyE;IACzE,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO;CAQhE"}
@@ -11,7 +11,7 @@ const DEFAULT_SHORTCUTS = [
11
11
  ["Enter", "Select / confirm"],
12
12
  ["/", "Start search (or type any character)"],
13
13
  ["Esc", "Clear search / go back"],
14
- ["H", "Toggle this help overlay"],
14
+ ["F1", "Toggle this help overlay"],
15
15
  ["Ctrl+C", "Exit vertaa"],
16
16
  ];
17
17
  /**
@@ -34,12 +34,13 @@ export class HelpOverlayView {
34
34
  ].join("\n");
35
35
  return box(content, { padding: 1 });
36
36
  }
37
- /** Handle H and Esc to dismiss the overlay */
37
+ /** Handle Esc to dismiss the overlay (F1 toggle handled at app level) */
38
38
  handleKey(key, _ctrl, _meta) {
39
- if (key === "h" || key === "H" || key === "\x1b") {
39
+ if (key === "\x1b") {
40
40
  this.onDismiss();
41
41
  return true;
42
42
  }
43
- return false;
43
+ // Consume all other keys while overlay is visible
44
+ return true;
44
45
  }
45
46
  }
@@ -1,9 +1,11 @@
1
1
  /**
2
- * Legacy a11y command handler.
3
- * Runs accessibility-focused audit (alias for audit with a11y filter).
2
+ * A11y command handler.
3
+ * Runs a dedicated multi-engine accessibility audit via POST /v1/a11y/audit.
4
+ * Uses axe-core (101 WCAG rules) + @accesslint/core (structured fixes) + custom analyzers.
4
5
  */
5
6
  import type { Command } from "commander";
6
7
  import type { Flags } from "../types.js";
8
+ export declare function runA11yAudit(base: string, url: string, flags: Flags): Promise<void>;
7
9
  export declare function handleA11y(rawUrl: string, cmdOptions: Flags): Promise<void>;
8
10
  export declare function registerA11yCommand(program: Command): void;
9
11
  //# sourceMappingURL=a11y.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"a11y.d.ts","sourceRoot":"","sources":["../../src/commands/a11y.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAKzC,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAGzC,wBAAsB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CAgDjF;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAuB1D"}
1
+ {"version":3,"file":"a11y.d.ts","sourceRoot":"","sources":["../../src/commands/a11y.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAYzC,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAqEzC,wBAAsB,YAAY,CAChC,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,KAAK,GACX,OAAO,CAAC,IAAI,CAAC,CAkDf;AAED,wBAAsB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CAgDjF;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA0B1D"}
@@ -1,12 +1,82 @@
1
1
  /**
2
- * Legacy a11y command handler.
3
- * Runs accessibility-focused audit (alias for audit with a11y filter).
2
+ * A11y command handler.
3
+ * Runs a dedicated multi-engine accessibility audit via POST /v1/a11y/audit.
4
+ * Uses axe-core (101 WCAG rules) + @accesslint/core (structured fixes) + custom analyzers.
4
5
  */
5
6
  import { renderError, runSteps, createRenderer } from "@vertaaux/tui";
6
7
  import { ExitCode } from "../utils/exit-codes.js";
7
8
  import { parseMode, parseTimeout, parseInterval, parseScore } from "../utils/validators.js";
8
- import { resolveApiBase } from "../utils/api-client.js";
9
- import { runAuditCommand } from "./scan.js";
9
+ import { resolveApiBase, getString, getNumber, resolveFormat, } from "../utils/api-client.js";
10
+ import { apiRequest, getApiKey } from "../utils/client.js";
11
+ import { printOutput } from "../utils/formatters.js";
12
+ function formatA11yMarkdown(result) {
13
+ const lines = [
14
+ `## Accessibility Audit: ${result.url}`,
15
+ "",
16
+ `**Total findings:** ${result.total_findings}`,
17
+ `**Severity:** ${result.severity_counts.critical || 0} critical, ${result.severity_counts.serious || 0} serious, ${result.severity_counts.moderate || 0} moderate, ${result.severity_counts.minor || 0} minor`,
18
+ ];
19
+ if (result.tier_shaping) {
20
+ lines.push("", `> ${result.tier_shaping.gated_findings} additional findings available on a paid plan.`, `> Upgrade: ${result.tier_shaping.upgrade_url}`);
21
+ }
22
+ if (result.findings.length > 0) {
23
+ lines.push("", "### Findings", "");
24
+ for (const f of result.findings.slice(0, 20)) {
25
+ const wcag = f.wcagCriteria?.length ? ` [WCAG ${f.wcagCriteria.join(", ")}]` : "";
26
+ lines.push(`- **[${f.impact.toUpperCase()}]** \`${f.engineId}/${f.ruleId}\`${wcag}`);
27
+ lines.push(` ${f.message}`);
28
+ lines.push(` Element: \`${f.selector}\``);
29
+ if (f.fix?.suggestion) {
30
+ lines.push(` Fix: ${f.fix.suggestion}`);
31
+ }
32
+ else if (f.fix?.attribute) {
33
+ lines.push(` Fix: ${f.fix.type} ${f.fix.attribute}="${f.fix.value || ""}"`);
34
+ }
35
+ if (f.fixability) {
36
+ lines.push(` Fixability: ${f.fixability}`);
37
+ }
38
+ lines.push("");
39
+ }
40
+ if (result.findings.length > 20) {
41
+ lines.push(`... and ${result.findings.length - 20} more findings.`);
42
+ }
43
+ }
44
+ return lines.join("\n");
45
+ }
46
+ export async function runA11yAudit(base, url, flags) {
47
+ const minImpact = getString(flags, "min-impact");
48
+ const mode = getString(flags, "mode");
49
+ const format = resolveFormat(flags);
50
+ const apiKey = getApiKey();
51
+ const body = { url, source: "cli" };
52
+ if (minImpact)
53
+ body.min_impact = minImpact;
54
+ if (mode)
55
+ body.mode = mode;
56
+ const result = await apiRequest(base, "/a11y/audit", { method: "POST", body }, apiKey);
57
+ // Compute an accessibility score from findings for --fail-on-score
58
+ // Starts at 100, subtracts 15 per critical, 10 per serious, 5 per moderate, 2 per minor
59
+ const counts = result.severity_counts;
60
+ const a11yScore = Math.max(0, 100 -
61
+ (counts.critical || 0) * 15 -
62
+ (counts.serious || 0) * 10 -
63
+ (counts.moderate || 0) * 5 -
64
+ (counts.minor || 0) * 2);
65
+ printOutput(format, { ...result, accessibility_score: a11yScore }, formatA11yMarkdown(result));
66
+ // Exit non-zero if --fail-on-score and computed score is below threshold
67
+ const failOnScore = getNumber(flags, "fail-on-score");
68
+ if (failOnScore !== undefined && a11yScore < failOnScore) {
69
+ process.exitCode = 1;
70
+ }
71
+ // Exit non-zero if --fail-on-findings and critical/serious findings exceed threshold
72
+ const failOnFindings = getNumber(flags, "fail-on-findings");
73
+ if (failOnFindings !== undefined) {
74
+ const criticalAndSerious = (counts.critical || 0) + (counts.serious || 0);
75
+ if (criticalAndSerious > failOnFindings) {
76
+ process.exitCode = 1;
77
+ }
78
+ }
79
+ }
10
80
  export async function handleA11y(rawUrl, cmdOptions) {
11
81
  const url = /^https?:\/\//i.test(rawUrl) ? rawUrl : `https://${rawUrl}`;
12
82
  const renderer = createRenderer("auto");
@@ -27,11 +97,11 @@ export async function handleA11y(rawUrl, cmdOptions) {
27
97
  const steps = [
28
98
  {
29
99
  id: "a11y",
30
- actionText: "Running accessibility audit...",
100
+ actionText: "Running multi-engine accessibility audit...",
31
101
  summaryText: "Accessibility audit complete",
32
102
  run: async () => {
33
103
  const base = resolveApiBase(cmdOptions);
34
- await runAuditCommand(base, url, { ...cmdOptions, wait: cmdOptions.wait ?? true }, "a11y");
104
+ await runA11yAudit(base, url, cmdOptions);
35
105
  },
36
106
  },
37
107
  ];
@@ -54,12 +124,15 @@ export async function handleA11y(rawUrl, cmdOptions) {
54
124
  export function registerA11yCommand(program) {
55
125
  program
56
126
  .command("a11y <url>")
57
- .description("Run accessibility-focused audit (alias for audit with a11y filter)")
127
+ .description("Run multi-engine accessibility audit (axe-core + accesslint + custom analyzers)")
58
128
  .option("--mode <mode>", "Audit depth: basic|standard|deep", parseMode, "basic")
59
- .option("--wait", "Wait for audit completion")
129
+ .option("--min-impact <level>", "Minimum impact level: minor|moderate|serious|critical")
130
+ .option("--fail-on-score <n>", "Exit non-zero if accessibility score below n (0-100)", parseScore)
131
+ .option("--fail-on-findings <n>", "Exit non-zero if critical+serious findings exceed n")
132
+ .option("--wait", "Wait for audit completion (default: true)")
60
133
  .option("--timeout <ms>", "Wait timeout in milliseconds (1-300000)", parseTimeout, 60000)
61
134
  .option("--interval <ms>", "Poll interval in milliseconds (1-300000)", parseInterval, 5000)
62
- .option("--fail-on-score <n>", "Exit non-zero if score below n (0-100)", parseScore)
135
+ .option("--format <fmt>", "Output format: json|md", "json")
63
136
  .action(async (url, cmdOptions) => {
64
137
  try {
65
138
  await handleA11y(url, cmdOptions);
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/audit/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AA+D7D,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAuFtD;;GAEG;AACH,wBAAsB,WAAW,CAC/B,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,mBAAmB,EAC5B,MAAM,EAAE,cAAc,GACrB,OAAO,CAAC,IAAI,CAAC,CA6Sf;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAyP3D"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/audit/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AA+D7D,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAwFtD;;GAEG;AACH,wBAAsB,WAAW,CAC/B,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,mBAAmB,EAC5B,MAAM,EAAE,cAAc,GACrB,OAAO,CAAC,IAAI,CAAC,CA+Uf;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA0P3D"}
@@ -29,13 +29,13 @@ import { normalizeIssues, filterBySeverity, filterByCategory } from "./filters.j
29
29
  import { mapStatusToPhase, getOverallScoreFromResult, extractNumericScores, countTotalIssues } from "./scoring.js";
30
30
  import { saveReproArtifacts, saveArtifactBundle } from "./artifacts.js";
31
31
  import { outputFireAndForget, outputFormattedResults, outputQualityGateResult } from "./output.js";
32
- import { loadAndResolvePolicy, buildQualityGateConfig } from "./policy.js";
32
+ import { loadAndResolvePolicy, buildQualityGateConfig, resolveAuditProfile } from "./policy.js";
33
33
  import { runInlineExplanation } from "./explain.js";
34
34
  /**
35
35
  * Apply filters, evaluate quality gate, and output results after audit completes.
36
36
  */
37
37
  async function runPostAuditAnalysis(params) {
38
- const { result, createdJobId, targetUrl, format, formatter, groupBy, options, config, interactive, quiet } = params;
38
+ const { result, createdJobId, targetUrl, format, formatter, groupBy, options, config, interactive, quiet, profile } = params;
39
39
  // Save repro artifacts if requested
40
40
  if (createdJobId) {
41
41
  saveReproArtifacts(createdJobId, result, options, quiet);
@@ -49,8 +49,8 @@ async function runPostAuditAnalysis(params) {
49
49
  const filteredResult = { ...result, issues };
50
50
  // Load and apply policy (CICD-17)
51
51
  await loadAndResolvePolicy(options, quiet);
52
- // Build quality gate config and evaluate
53
- const gateConfig = buildQualityGateConfig(config, options);
52
+ // Build quality gate config and evaluate (profile applied between config and CLI flags)
53
+ const gateConfig = buildQualityGateConfig(config, options, profile);
54
54
  const baselinePath = options.baseline || config.baseline?.path;
55
55
  const baseline = baselinePath ? await loadBaseline(baselinePath) : null;
56
56
  const prLabels = detectPRLabels();
@@ -114,8 +114,10 @@ export async function handleAudit(rawUrl, options, config) {
114
114
  process.exitCode = ExitCode.ERROR;
115
115
  return;
116
116
  }
117
- // Resolve options with precedence: flags > env > config > defaults
118
- const mode = options.mode || config.mode || "basic";
117
+ // Resolve audit profile (bundles categories, weights, thresholds, mode)
118
+ const profile = resolveAuditProfile(options.profile || config.profile, config.profiles);
119
+ // Resolve options with precedence: flags > config > profile > defaults
120
+ const mode = options.mode || config.mode || profile?.mode || "basic";
119
121
  const timeout = options.timeout || config.timeout || 60000;
120
122
  const interval = options.interval || config.interval || 5000;
121
123
  const wait = options.wait ?? true; // Default to waiting for completion
@@ -219,8 +221,29 @@ export async function handleAudit(rawUrl, options, config) {
219
221
  createdJobId = created.job_id;
220
222
  }
221
223
  else {
222
- // Public URL — send to cloud API
223
- const created = await sdkClient.audits.create({ url: targetUrl, mode: mode });
224
+ // Public URL — send to cloud API.
225
+ //
226
+ // When the resolved profile declares a `categories` subset, we bypass
227
+ // `sdkClient.audits.create` and POST directly via `apiRequest`
228
+ // because the published @vertaaux/sdk typings don't yet include the
229
+ // `categories` field (Phase 1.5 wire format). The wire format is
230
+ // identical to the SDK's — same path, same auth — so this is a
231
+ // targeted escape hatch, not a fork. Once the SDK typings are bumped
232
+ // to include `categories?`, both branches collapse back to the SDK.
233
+ const profileCategories = profile?.categories;
234
+ const created = profileCategories && profileCategories.length > 0
235
+ ? await apiRequest(base, "/audit", {
236
+ method: "POST",
237
+ body: {
238
+ url: targetUrl,
239
+ mode: mode,
240
+ categories: profileCategories,
241
+ },
242
+ }, apiKey)
243
+ : await sdkClient.audits.create({
244
+ url: targetUrl,
245
+ mode: mode,
246
+ });
224
247
  auditResult = created;
225
248
  createdJobId = created.job_id;
226
249
  // If not waiting, just output the job info and stop processing.
@@ -352,6 +375,7 @@ export async function handleAudit(rawUrl, options, config) {
352
375
  config,
353
376
  interactive,
354
377
  quiet,
378
+ profile,
355
379
  });
356
380
  }
357
381
  }
@@ -415,6 +439,7 @@ export function registerAuditCommand(program) {
415
439
  .option("--json-logs", "Output structured JSON logs for CI")
416
440
  // Policy options (CICD-17)
417
441
  .option("--policy <file>", "Path to policy file (default: auto-detect vertaa.policy.yml)")
442
+ .option("--profile <name>", "Audit profile: wcag-aa|conversion-focus|quick-ux|ci-gate|compliance")
418
443
  .option("--explain", "Append AI explanation to audit results")
419
444
  .option("--strict", "Fail immediately on first step error")
420
445
  .option("--continue-on-error", "Continue on step errors even in CI")