@vertaaux/cli 0.3.3 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (227) hide show
  1. package/CHANGELOG.md +97 -0
  2. package/MIGRATION.md +239 -0
  3. package/README.md +34 -16
  4. package/dist/app/interactive-app.d.ts +101 -0
  5. package/dist/app/interactive-app.d.ts.map +1 -0
  6. package/dist/app/interactive-app.js +309 -0
  7. package/dist/app/layout/canvas.d.ts +23 -0
  8. package/dist/app/layout/canvas.d.ts.map +1 -0
  9. package/dist/app/layout/canvas.js +36 -0
  10. package/dist/app/layout/footer.d.ts +31 -0
  11. package/dist/app/layout/footer.d.ts.map +1 -0
  12. package/dist/app/layout/footer.js +41 -0
  13. package/dist/app/layout/header.d.ts +20 -0
  14. package/dist/app/layout/header.d.ts.map +1 -0
  15. package/dist/app/layout/header.js +27 -0
  16. package/dist/app/menu/categories.d.ts +20 -0
  17. package/dist/app/menu/categories.d.ts.map +1 -0
  18. package/dist/app/menu/categories.js +181 -0
  19. package/dist/app/menu/filter.d.ts +17 -0
  20. package/dist/app/menu/filter.d.ts.map +1 -0
  21. package/dist/app/menu/filter.js +33 -0
  22. package/dist/app/menu/menu-view.d.ts +35 -0
  23. package/dist/app/menu/menu-view.d.ts.map +1 -0
  24. package/dist/app/menu/menu-view.js +230 -0
  25. package/dist/app/menu/recent.d.ts +24 -0
  26. package/dist/app/menu/recent.d.ts.map +1 -0
  27. package/dist/app/menu/recent.js +49 -0
  28. package/dist/app/types.d.ts +43 -0
  29. package/dist/app/types.d.ts.map +1 -0
  30. package/dist/app/types.js +7 -0
  31. package/dist/app/views/command-runner.d.ts +36 -0
  32. package/dist/app/views/command-runner.d.ts.map +1 -0
  33. package/dist/app/views/command-runner.js +372 -0
  34. package/dist/app/views/help-overlay.d.ts +21 -0
  35. package/dist/app/views/help-overlay.d.ts.map +1 -0
  36. package/dist/app/views/help-overlay.js +45 -0
  37. package/dist/auth/ci-token.d.ts +14 -2
  38. package/dist/auth/ci-token.d.ts.map +1 -1
  39. package/dist/auth/ci-token.js +15 -30
  40. package/dist/auth/device-flow.d.ts +2 -1
  41. package/dist/auth/device-flow.d.ts.map +1 -1
  42. package/dist/auth/device-flow.js +13 -10
  43. package/dist/auth/token-store.d.ts.map +1 -1
  44. package/dist/auth/token-store.js +12 -2
  45. package/dist/baseline/diff.d.ts +2 -2
  46. package/dist/baseline/diff.d.ts.map +1 -1
  47. package/dist/baseline/diff.js +15 -34
  48. package/dist/commands/a11y.d.ts +9 -0
  49. package/dist/commands/a11y.d.ts.map +1 -0
  50. package/dist/commands/a11y.js +76 -0
  51. package/dist/commands/audit/artifacts.d.ts +27 -0
  52. package/dist/commands/audit/artifacts.d.ts.map +1 -0
  53. package/dist/commands/audit/artifacts.js +158 -0
  54. package/dist/commands/audit/ci-detection.d.ts +18 -0
  55. package/dist/commands/audit/ci-detection.d.ts.map +1 -0
  56. package/dist/commands/audit/ci-detection.js +71 -0
  57. package/dist/commands/audit/explain.d.ts +11 -0
  58. package/dist/commands/audit/explain.d.ts.map +1 -0
  59. package/dist/commands/audit/explain.js +45 -0
  60. package/dist/commands/audit/filters.d.ts +17 -0
  61. package/dist/commands/audit/filters.d.ts.map +1 -0
  62. package/dist/commands/audit/filters.js +40 -0
  63. package/dist/commands/audit/index.d.ts +18 -0
  64. package/dist/commands/audit/index.d.ts.map +1 -0
  65. package/dist/commands/audit/index.js +564 -0
  66. package/dist/commands/audit/output.d.ts +32 -0
  67. package/dist/commands/audit/output.d.ts.map +1 -0
  68. package/dist/commands/audit/output.js +130 -0
  69. package/dist/commands/audit/policy.d.ts +19 -0
  70. package/dist/commands/audit/policy.d.ts.map +1 -0
  71. package/dist/commands/audit/policy.js +102 -0
  72. package/dist/commands/audit/scoring.d.ts +23 -0
  73. package/dist/commands/audit/scoring.d.ts.map +1 -0
  74. package/dist/commands/audit/scoring.js +70 -0
  75. package/dist/commands/audit/types.d.ts +88 -0
  76. package/dist/commands/audit/types.d.ts.map +1 -0
  77. package/dist/commands/audit/types.js +8 -0
  78. package/dist/commands/audit.d.ts +2 -60
  79. package/dist/commands/audit.d.ts.map +1 -1
  80. package/dist/commands/audit.js +2 -1038
  81. package/dist/commands/baseline.d.ts +1 -0
  82. package/dist/commands/baseline.d.ts.map +1 -1
  83. package/dist/commands/baseline.js +205 -121
  84. package/dist/commands/comment.d.ts +22 -0
  85. package/dist/commands/comment.d.ts.map +1 -1
  86. package/dist/commands/comment.js +122 -58
  87. package/dist/commands/compare.d.ts +17 -0
  88. package/dist/commands/compare.d.ts.map +1 -1
  89. package/dist/commands/compare.js +287 -180
  90. package/dist/commands/diff.d.ts +5 -0
  91. package/dist/commands/diff.d.ts.map +1 -1
  92. package/dist/commands/diff.js +168 -141
  93. package/dist/commands/doc.d.ts +10 -0
  94. package/dist/commands/doc.d.ts.map +1 -1
  95. package/dist/commands/doc.js +134 -76
  96. package/dist/commands/doctor.d.ts +2 -0
  97. package/dist/commands/doctor.d.ts.map +1 -1
  98. package/dist/commands/doctor.js +164 -17
  99. package/dist/commands/download.d.ts +10 -0
  100. package/dist/commands/download.d.ts.map +1 -1
  101. package/dist/commands/download.js +169 -112
  102. package/dist/commands/explain.d.ts +5 -0
  103. package/dist/commands/explain.d.ts.map +1 -1
  104. package/dist/commands/explain.js +241 -155
  105. package/dist/commands/fix-all.d.ts +25 -0
  106. package/dist/commands/fix-all.d.ts.map +1 -0
  107. package/dist/commands/fix-all.js +206 -0
  108. package/dist/commands/fix-plan.d.ts +9 -0
  109. package/dist/commands/fix-plan.d.ts.map +1 -1
  110. package/dist/commands/fix-plan.js +152 -89
  111. package/dist/commands/fix.d.ts +17 -0
  112. package/dist/commands/fix.d.ts.map +1 -0
  113. package/dist/commands/fix.js +111 -0
  114. package/dist/commands/init.d.ts +11 -0
  115. package/dist/commands/init.d.ts.map +1 -1
  116. package/dist/commands/init.js +94 -42
  117. package/dist/commands/login.d.ts +18 -0
  118. package/dist/commands/login.d.ts.map +1 -1
  119. package/dist/commands/login.js +268 -95
  120. package/dist/commands/patch-review.d.ts +11 -0
  121. package/dist/commands/patch-review.d.ts.map +1 -1
  122. package/dist/commands/patch-review.js +159 -97
  123. package/dist/commands/policy.d.ts +31 -0
  124. package/dist/commands/policy.d.ts.map +1 -1
  125. package/dist/commands/policy.js +269 -124
  126. package/dist/commands/release-notes.d.ts +10 -0
  127. package/dist/commands/release-notes.d.ts.map +1 -1
  128. package/dist/commands/release-notes.js +127 -73
  129. package/dist/commands/scan.d.ts +13 -0
  130. package/dist/commands/scan.d.ts.map +1 -0
  131. package/dist/commands/scan.js +133 -0
  132. package/dist/commands/status.d.ts +9 -0
  133. package/dist/commands/status.d.ts.map +1 -0
  134. package/dist/commands/status.js +81 -0
  135. package/dist/commands/suggest.d.ts +10 -0
  136. package/dist/commands/suggest.d.ts.map +1 -1
  137. package/dist/commands/suggest.js +153 -82
  138. package/dist/commands/triage.d.ts +35 -0
  139. package/dist/commands/triage.d.ts.map +1 -1
  140. package/dist/commands/triage.js +206 -81
  141. package/dist/commands/upload.d.ts +9 -0
  142. package/dist/commands/upload.d.ts.map +1 -1
  143. package/dist/commands/upload.js +140 -101
  144. package/dist/commands/verify.d.ts +13 -0
  145. package/dist/commands/verify.d.ts.map +1 -0
  146. package/dist/commands/verify.js +118 -0
  147. package/dist/index.d.ts +3 -2
  148. package/dist/index.d.ts.map +1 -1
  149. package/dist/index.js +125 -990
  150. package/dist/interactive/fix-wizard.d.ts +3 -0
  151. package/dist/interactive/fix-wizard.d.ts.map +1 -1
  152. package/dist/interactive/fix-wizard.js +130 -112
  153. package/dist/interactive/init-wizard.d.ts +3 -1
  154. package/dist/interactive/init-wizard.d.ts.map +1 -1
  155. package/dist/interactive/init-wizard.js +207 -138
  156. package/dist/interactive/prompts.d.ts +7 -3
  157. package/dist/interactive/prompts.d.ts.map +1 -1
  158. package/dist/interactive/prompts.js +44 -23
  159. package/dist/output/envelope.d.ts +2 -0
  160. package/dist/output/envelope.d.ts.map +1 -1
  161. package/dist/output/envelope.js +18 -2
  162. package/dist/output/factory.d.ts +9 -1
  163. package/dist/output/factory.d.ts.map +1 -1
  164. package/dist/output/html.d.ts +2 -1
  165. package/dist/output/html.d.ts.map +1 -1
  166. package/dist/output/html.js +3 -2
  167. package/dist/output/human.d.ts +9 -1
  168. package/dist/output/human.d.ts.map +1 -1
  169. package/dist/output/human.js +17 -2
  170. package/dist/output/json.d.ts +2 -1
  171. package/dist/output/json.d.ts.map +1 -1
  172. package/dist/output/junit.d.ts +2 -1
  173. package/dist/output/junit.d.ts.map +1 -1
  174. package/dist/output/sarif.d.ts +2 -1
  175. package/dist/output/sarif.d.ts.map +1 -1
  176. package/dist/types.d.ts +74 -0
  177. package/dist/types.d.ts.map +1 -0
  178. package/dist/types.js +5 -0
  179. package/dist/ui/banner.d.ts +34 -0
  180. package/dist/ui/banner.d.ts.map +1 -1
  181. package/dist/ui/banner.js +97 -5
  182. package/dist/ui/diagnostics.d.ts +9 -4
  183. package/dist/ui/diagnostics.d.ts.map +1 -1
  184. package/dist/ui/diagnostics.js +32 -82
  185. package/dist/ui/strings.d.ts +373 -0
  186. package/dist/ui/strings.d.ts.map +1 -0
  187. package/dist/ui/strings.js +499 -0
  188. package/dist/ui/table.d.ts +0 -2
  189. package/dist/ui/table.d.ts.map +1 -1
  190. package/dist/ui/table.js +3 -4
  191. package/dist/utils/api-client.d.ts +46 -0
  192. package/dist/utils/api-client.d.ts.map +1 -0
  193. package/dist/utils/api-client.js +170 -0
  194. package/dist/utils/client.d.ts +29 -18
  195. package/dist/utils/client.d.ts.map +1 -1
  196. package/dist/utils/client.js +102 -12
  197. package/dist/utils/formatters.d.ts +38 -0
  198. package/dist/utils/formatters.d.ts.map +1 -0
  199. package/dist/utils/formatters.js +277 -0
  200. package/dist/utils/local-capture.d.ts +25 -0
  201. package/dist/utils/local-capture.d.ts.map +1 -0
  202. package/dist/utils/local-capture.js +57 -0
  203. package/dist/utils/url-classify.d.ts +18 -0
  204. package/dist/utils/url-classify.d.ts.map +1 -0
  205. package/dist/utils/url-classify.js +106 -0
  206. package/node_modules/@vertaaux/tui/dist/index.cjs +713 -20
  207. package/node_modules/@vertaaux/tui/dist/index.cjs.map +1 -1
  208. package/node_modules/@vertaaux/tui/dist/index.d.cts +361 -4
  209. package/node_modules/@vertaaux/tui/dist/index.d.ts +361 -4
  210. package/node_modules/@vertaaux/tui/dist/index.js +689 -21
  211. package/node_modules/@vertaaux/tui/dist/index.js.map +1 -1
  212. package/package.json +13 -5
  213. package/dist/commands/client.d.ts +0 -14
  214. package/dist/commands/client.d.ts.map +0 -1
  215. package/dist/commands/client.js +0 -362
  216. package/dist/commands/drift.d.ts +0 -15
  217. package/dist/commands/drift.d.ts.map +0 -1
  218. package/dist/commands/drift.js +0 -309
  219. package/dist/commands/protect.d.ts +0 -16
  220. package/dist/commands/protect.d.ts.map +0 -1
  221. package/dist/commands/protect.js +0 -323
  222. package/dist/commands/report.d.ts +0 -15
  223. package/dist/commands/report.d.ts.map +0 -1
  224. package/dist/commands/report.js +0 -214
  225. package/dist/policy/sync.d.ts +0 -67
  226. package/dist/policy/sync.d.ts.map +0 -1
  227. package/dist/policy/sync.js +0 -147
@@ -12,52 +12,18 @@
12
12
  import { readFile } from "fs/promises";
13
13
  import { existsSync } from "fs";
14
14
  import { ExitCode } from "../utils/exit-codes.js";
15
+ import { createClient } from "../utils/client.js";
15
16
  import { loadBaseline, DEFAULT_BASELINE_PATH } from "../baseline/manager.js";
16
17
  import { computeDiff, formatDiffHuman, formatDiffJson } from "../baseline/diff.js";
17
18
  import { writeJsonOutput, writeOutput } from "../output/envelope.js";
18
19
  import { resolveCommandFormat } from "../output/formats.js";
19
- const DEFAULT_API_BASE = "https://vertaaux.ai/v1";
20
+ import { strings } from "../ui/strings.js";
21
+ import { runSteps, createRenderer, renderWarning, renderError, isCI, isTTY } from "@vertaaux/tui";
20
22
  /**
21
- * Get API base URL from environment.
22
- */
23
- function getApiBase() {
24
- return (process.env.VERTAAUX_API_BASE || DEFAULT_API_BASE).replace(/\/$/, "");
25
- }
26
- /**
27
- * Get API key from environment.
28
- */
29
- function getApiKey() {
30
- const key = process.env.VERTAAUX_API_KEY;
31
- if (!key) {
32
- throw new Error("VERTAAUX_API_KEY is required");
33
- }
34
- return key;
35
- }
36
- /**
37
- * Fetch audit results from API.
23
+ * Fetch audit results via SDK client.
38
24
  */
39
25
  async function fetchAudit(jobId) {
40
- const base = getApiBase();
41
- const url = `${base}/audit/${jobId}`;
42
- const res = await fetch(url, {
43
- method: "GET",
44
- headers: {
45
- "Content-Type": "application/json",
46
- "X-API-Key": getApiKey(),
47
- },
48
- });
49
- if (!res.ok) {
50
- let detail = res.statusText;
51
- try {
52
- const data = (await res.json());
53
- detail = data.error || data.message || detail;
54
- }
55
- catch {
56
- // ignore
57
- }
58
- throw new Error(`HTTP ${res.status}: ${detail}`);
59
- }
60
- return (await res.json());
26
+ return createClient().audits.retrieve(jobId);
61
27
  }
62
28
  /**
63
29
  * Normalize issues from various API response formats.
@@ -71,6 +37,157 @@ function normalizeIssues(issues) {
71
37
  }
72
38
  return [];
73
39
  }
40
+ // ---------------------------------------------------------------------------
41
+ // Handler — exported for CommandRunnerView
42
+ // ---------------------------------------------------------------------------
43
+ export async function handleDiff(opts) {
44
+ const baselinePath = opts.baseline || DEFAULT_BASELINE_PATH;
45
+ const format = resolveCommandFormat("diff", opts.format, opts.machine || false);
46
+ const verbose = opts.verbose || false;
47
+ const jobIdArg = opts.jobIdArg;
48
+ // Resolve fail-fast mode: --strict > --continue-on-error > auto-detect
49
+ let failFast;
50
+ if (opts.strict && opts.continueOnError) {
51
+ writeOutput(renderWarning({ message: "--strict and --continue-on-error both set — --strict takes precedence" }));
52
+ failFast = true;
53
+ }
54
+ else if (opts.strict) {
55
+ failFast = true;
56
+ }
57
+ else if (opts.continueOnError) {
58
+ failFast = false;
59
+ }
60
+ else {
61
+ failFast = isCI() || !isTTY();
62
+ }
63
+ const ctx = { diffOutput: null };
64
+ const renderer = createRenderer("auto");
65
+ const baseState = {
66
+ phase: "diff",
67
+ phaseIndex: 1,
68
+ phaseTotal: 1,
69
+ url: "",
70
+ mode: "diff",
71
+ progress: {},
72
+ totals: {},
73
+ issueCount: 0,
74
+ scorePreview: null,
75
+ verbose: false,
76
+ elapsed: 0,
77
+ };
78
+ const steps = [
79
+ {
80
+ id: "load",
81
+ actionText: strings.diff.run.action,
82
+ summaryText: strings.diff.run.done(0, 0),
83
+ run: async () => {
84
+ // Get current audit issues
85
+ let currentIssues;
86
+ let currentUrl;
87
+ if (opts.fromFile) {
88
+ const filePath = opts.fromFile;
89
+ if (!existsSync(filePath)) {
90
+ throw new Error(`File not found: ${filePath}`);
91
+ }
92
+ const content = await readFile(filePath, "utf-8");
93
+ const data = JSON.parse(content);
94
+ currentIssues = normalizeIssues(data.issues);
95
+ currentUrl = data.url || "unknown";
96
+ }
97
+ else if (jobIdArg) {
98
+ const audit = await fetchAudit(jobIdArg);
99
+ if (audit.status !== "completed") {
100
+ throw new Error(strings.fixAll.auditIncomplete(jobIdArg || "unknown", audit.status || "unknown"));
101
+ }
102
+ currentIssues = normalizeIssues(audit.issues);
103
+ currentUrl = audit.url || "unknown";
104
+ }
105
+ else {
106
+ throw new Error("Provide job ID or use --from-file");
107
+ }
108
+ // Handle --compare: compare two audits
109
+ if (opts.compare) {
110
+ const compareAudit = await fetchAudit(opts.compare);
111
+ if (compareAudit.status !== "completed") {
112
+ throw new Error(strings.fixAll.auditIncomplete(opts.compare || "unknown", compareAudit.status || "unknown"));
113
+ }
114
+ const compareIssues = normalizeIssues(compareAudit.issues);
115
+ const tempBaseline = {
116
+ version: 1,
117
+ created: new Date().toISOString(),
118
+ updated: new Date().toISOString(),
119
+ url: compareAudit.url || "unknown",
120
+ issues: compareIssues.map((issue) => ({
121
+ fingerprint: "",
122
+ ruleId: issue.ruleId || issue.rule_id || issue.id || "unknown",
123
+ severity: issue.severity || "warning",
124
+ category: issue.category || "general",
125
+ description: issue.description || "",
126
+ selector: issue.selector,
127
+ baselinedAt: new Date().toISOString(),
128
+ })),
129
+ };
130
+ const { generateFingerprint } = await import("../baseline/hash.js");
131
+ for (let i = 0; i < tempBaseline.issues.length; i++) {
132
+ tempBaseline.issues[i].fingerprint = generateFingerprint(compareIssues[i]);
133
+ }
134
+ const diff = computeDiff(currentIssues, tempBaseline);
135
+ const diffData = format === "json" ? JSON.parse(formatDiffJson(diff)) : null;
136
+ ctx.diffOutput = { diffData, newCount: diff.summary.newCount, currentUrl };
137
+ if (format !== "json") {
138
+ writeOutput(`Comparing ${currentUrl} (current) vs ${tempBaseline.url} (previous)\n`);
139
+ writeOutput(formatDiffHuman(diff, verbose));
140
+ }
141
+ return;
142
+ }
143
+ // Load baseline
144
+ const baseline = await loadBaseline(baselinePath);
145
+ if (!baseline) {
146
+ throw new Error(strings.diff.errors.noBaseline);
147
+ }
148
+ const diff = computeDiff(currentIssues, baseline);
149
+ const diffData = format === "json" ? JSON.parse(formatDiffJson(diff)) : null;
150
+ ctx.diffOutput = { diffData, newCount: diff.summary.newCount, currentUrl };
151
+ if (format !== "json") {
152
+ writeOutput(`Comparing ${currentUrl} against baseline\n`);
153
+ writeOutput(formatDiffHuman(diff, verbose));
154
+ }
155
+ },
156
+ },
157
+ ];
158
+ const { success, states } = await runSteps(steps, {
159
+ failFast,
160
+ onStateChange: (stepStates) => {
161
+ renderer.update({ ...baseState, stepStates });
162
+ },
163
+ });
164
+ renderer.finish({
165
+ url: "",
166
+ mode: "diff",
167
+ overallScore: success ? 100 : 0,
168
+ scores: {},
169
+ issueCount: 0,
170
+ passed: success,
171
+ elapsed: 0,
172
+ });
173
+ if (!success) {
174
+ const failedStep = states.find((s) => s.status === "failed");
175
+ process.stderr.write(renderError({
176
+ message: strings.diff.errors.noBaseline,
177
+ context: failedStep?.failReason,
178
+ suggestion: "vertaa audit <url>",
179
+ }) + "\n");
180
+ process.exit(ExitCode.ERROR);
181
+ }
182
+ // Output JSON if requested
183
+ if (format === "json" && ctx.diffOutput) {
184
+ writeJsonOutput(ctx.diffOutput.diffData, "diff");
185
+ }
186
+ // Exit code: 1 if new issues
187
+ if (ctx.diffOutput && ctx.diffOutput.newCount > 0) {
188
+ process.exit(1);
189
+ }
190
+ }
74
191
  /**
75
192
  * Register the diff command with the Commander program.
76
193
  */
@@ -83,113 +200,23 @@ export function registerDiffCommand(program) {
83
200
  .option("--format <format>", "Output format: json | human (default: auto)")
84
201
  .option("--verbose", "Show all present issues")
85
202
  .option("--from-file <file>", "Read current audit from JSON file")
203
+ .option("--strict", "Fail immediately on first step error")
204
+ .option("--continue-on-error", "Continue on step errors even in CI")
86
205
  .action(async (jobIdArg, cmdOptions, cmd) => {
87
206
  try {
88
- const baselinePath = cmdOptions.baseline || DEFAULT_BASELINE_PATH;
89
207
  const machineMode = cmd.optsWithGlobals?.().machine || false;
90
- const format = resolveCommandFormat("diff", cmdOptions.format, machineMode);
91
- const verbose = cmdOptions.verbose || false;
92
- // Get current audit issues
93
- let currentIssues;
94
- let currentUrl;
95
- if (cmdOptions.fromFile) {
96
- // Load from file
97
- const filePath = cmdOptions.fromFile;
98
- if (!existsSync(filePath)) {
99
- console.error(`File not found: ${filePath}`);
100
- process.exit(ExitCode.ERROR);
101
- }
102
- const content = await readFile(filePath, "utf-8");
103
- const data = JSON.parse(content);
104
- currentIssues = normalizeIssues(data.issues);
105
- currentUrl = data.url || "unknown";
106
- }
107
- else if (jobIdArg) {
108
- // Fetch from API
109
- const audit = await fetchAudit(jobIdArg);
110
- if (audit.status !== "completed") {
111
- console.error(`Audit ${jobIdArg} is not completed (status: ${audit.status})`);
112
- process.exit(ExitCode.ERROR);
113
- }
114
- currentIssues = normalizeIssues(audit.issues);
115
- currentUrl = audit.url || "unknown";
116
- }
117
- else {
118
- console.error("Provide job ID or use --from-file");
119
- console.error("Usage: vertaa diff <job-id>");
120
- console.error(" vertaa diff --from-file <audit.json>");
121
- process.exit(ExitCode.ERROR);
122
- }
123
- // Handle --compare: compare two audits
124
- if (cmdOptions.compare) {
125
- const compareAudit = await fetchAudit(cmdOptions.compare);
126
- if (compareAudit.status !== "completed") {
127
- console.error(`Audit ${cmdOptions.compare} is not completed (status: ${compareAudit.status})`);
128
- process.exit(ExitCode.ERROR);
129
- }
130
- // Create temporary baseline from compare job
131
- const compareIssues = normalizeIssues(compareAudit.issues);
132
- const tempBaseline = {
133
- version: 1,
134
- created: new Date().toISOString(),
135
- updated: new Date().toISOString(),
136
- url: compareAudit.url || "unknown",
137
- issues: compareIssues.map((issue) => ({
138
- fingerprint: "",
139
- ruleId: issue.ruleId || issue.rule_id || issue.id || "unknown",
140
- severity: issue.severity || "warning",
141
- category: issue.category || "general",
142
- description: issue.description || "",
143
- selector: issue.selector,
144
- baselinedAt: new Date().toISOString(),
145
- })),
146
- };
147
- // Re-generate fingerprints using the proper function
148
- const { generateFingerprint } = await import("../baseline/hash.js");
149
- for (let i = 0; i < tempBaseline.issues.length; i++) {
150
- tempBaseline.issues[i].fingerprint = generateFingerprint(compareIssues[i]);
151
- }
152
- const diff = computeDiff(currentIssues, tempBaseline);
153
- // Output
154
- if (format === "json") {
155
- const diffData = JSON.parse(formatDiffJson(diff));
156
- writeJsonOutput(diffData, "diff");
157
- }
158
- else {
159
- console.error(`Comparing ${currentUrl} (current) vs ${tempBaseline.url} (previous)\n`);
160
- writeOutput(formatDiffHuman(diff, verbose));
161
- }
162
- // Exit code: 1 if new issues
163
- if (diff.summary.newCount > 0) {
164
- process.exit(1);
165
- }
166
- return;
167
- }
168
- // Load baseline
169
- const baseline = await loadBaseline(baselinePath);
170
- if (!baseline) {
171
- console.error(`No baseline found at: ${baselinePath}`);
172
- console.error("Create one with: vertaa baseline <job-id>");
173
- process.exit(ExitCode.ERROR);
174
- }
175
- // Compute diff
176
- const diff = computeDiff(currentIssues, baseline);
177
- // Output
178
- if (format === "json") {
179
- const diffData = JSON.parse(formatDiffJson(diff));
180
- writeJsonOutput(diffData, "diff");
181
- }
182
- else {
183
- console.error(`Comparing ${currentUrl} against baseline\n`);
184
- writeOutput(formatDiffHuman(diff, verbose));
185
- }
186
- // Exit code: 1 if new issues
187
- if (diff.summary.newCount > 0) {
188
- process.exit(1);
189
- }
208
+ await handleDiff({
209
+ ...cmdOptions,
210
+ machine: machineMode,
211
+ jobIdArg,
212
+ });
190
213
  }
191
214
  catch (error) {
192
- console.error("Error:", error instanceof Error ? error.message : String(error));
215
+ process.stderr.write(renderError({
216
+ message: error instanceof Error ? error.message : String(error),
217
+ suggestion: "vertaa audit <url>",
218
+ exitCode: ExitCode.ERROR,
219
+ }) + "\n");
193
220
  process.exit(ExitCode.ERROR);
194
221
  }
195
222
  });
@@ -14,5 +14,15 @@
14
14
  * vertaa doc --file audit.json --team "Frontend Team"
15
15
  */
16
16
  import { Command } from "commander";
17
+ export interface DocCommandOptions {
18
+ job?: string;
19
+ file?: string;
20
+ team?: string;
21
+ format?: string;
22
+ base?: string;
23
+ machine?: boolean;
24
+ apiKey?: string;
25
+ }
26
+ export declare function handleDoc(opts: DocCommandOptions): Promise<void>;
17
27
  export declare function registerDocCommand(program: Command): void;
18
28
  //# sourceMappingURL=doc.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"doc.d.ts","sourceRoot":"","sources":["../../src/commands/doc.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAqFpC,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAoHzD"}
1
+ {"version":3,"file":"doc.d.ts","sourceRoot":"","sources":["../../src/commands/doc.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAqFpC,MAAM,WAAW,iBAAiB;IAChC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,wBAAsB,SAAS,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAyItE;AAMD,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA2CzD"}
@@ -13,15 +13,15 @@
13
13
  * vertaa doc --job abc123
14
14
  * vertaa doc --file audit.json --team "Frontend Team"
15
15
  */
16
- import chalk from "chalk";
16
+ import { bold, dim, renderError, createRenderer, runSteps } from "@vertaaux/tui";
17
17
  import { ExitCode } from "../utils/exit-codes.js";
18
- import { resolveApiBase, getApiKey, apiRequest } from "../utils/client.js";
18
+ import { resolveApiBase, getApiKey, apiRequest, createClient } from "../utils/client.js";
19
19
  import { resolveConfig } from "../config/loader.js";
20
20
  import { writeJsonOutput, writeOutput } from "../output/envelope.js";
21
21
  import { resolveCommandFormat } from "../output/formats.js";
22
- import { createSpinner, succeedSpinner } from "../ui/spinner.js";
23
22
  import { readJsonInput } from "../utils/stdin.js";
24
- import { handleAiCommandError, AI_TIMEOUT_MS } from "../utils/ai-error.js";
23
+ import { AI_TIMEOUT_MS } from "../utils/ai-error.js";
24
+ import { strings } from "../ui/strings.js";
25
25
  // ---------------------------------------------------------------------------
26
26
  // Helpers
27
27
  // ---------------------------------------------------------------------------
@@ -55,13 +55,126 @@ function normalizeIssues(issues) {
55
55
  // ---------------------------------------------------------------------------
56
56
  function formatDocHuman(data) {
57
57
  const lines = [];
58
- lines.push(chalk.bold(data.title));
59
- lines.push(chalk.dim("─".repeat(40)));
60
- lines.push(chalk.dim(`Sections: ${data.sections.join(", ")}`));
58
+ lines.push(bold(data.title));
59
+ lines.push(dim("─".repeat(40)));
60
+ lines.push(dim(`Sections: ${data.sections.join(", ")}`));
61
61
  lines.push("");
62
62
  lines.push(data.content);
63
63
  return lines.join("\n");
64
64
  }
65
+ export async function handleDoc(opts) {
66
+ const config = { apiKey: opts.apiKey };
67
+ const format = resolveCommandFormat("doc", opts.format, opts.machine || false);
68
+ const renderer = createRenderer("auto");
69
+ const baseState = {
70
+ phase: "doc",
71
+ phaseIndex: 1,
72
+ phaseTotal: 2,
73
+ url: "",
74
+ mode: "doc",
75
+ progress: {},
76
+ totals: {},
77
+ issueCount: 0,
78
+ scorePreview: null,
79
+ verbose: false,
80
+ elapsed: 0,
81
+ };
82
+ const startTime = Date.now();
83
+ let responseData = null;
84
+ const steps = [
85
+ {
86
+ id: "analyze",
87
+ actionText: strings.doc.generate.action,
88
+ summaryText: "Project analyzed",
89
+ run: async () => {
90
+ // Resolve audit data
91
+ let auditPayload;
92
+ if (opts.job) {
93
+ const sdkClient = createClient({ base: opts.base, apiKey: config.apiKey });
94
+ const result = await sdkClient.audits.retrieve(opts.job);
95
+ const issues = normalizeIssues(result.issues);
96
+ auditPayload = {
97
+ job_id: result.job_id || opts.job,
98
+ url: result.url || null,
99
+ scores: result.scores || null,
100
+ issues,
101
+ };
102
+ }
103
+ else {
104
+ const input = await readJsonInput(opts.file);
105
+ if (!input) {
106
+ throw new Error("No audit data provided.\nUsage:\n vertaa audit https://example.com --json | vertaa doc\n vertaa doc --job <job-id>\n vertaa doc --file audit.json");
107
+ }
108
+ const data = input;
109
+ const innerData = (data.data && typeof data.data === "object" ? data.data : data);
110
+ const issues = normalizeIssues(innerData.issues);
111
+ auditPayload = {
112
+ job_id: innerData.job_id || null,
113
+ url: innerData.url || null,
114
+ scores: innerData.scores || null,
115
+ issues,
116
+ };
117
+ }
118
+ if (!Array.isArray(auditPayload.issues) ||
119
+ auditPayload.issues.length === 0) {
120
+ throw new Error("No issues found in audit data.");
121
+ }
122
+ const base = resolveApiBase(opts.base);
123
+ const apiKey = getApiKey(config.apiKey);
124
+ const response = await Promise.race([
125
+ apiRequest(base, "/cli/ai/doc", {
126
+ method: "POST",
127
+ body: {
128
+ audit: auditPayload,
129
+ teamName: opts.team || null,
130
+ },
131
+ }, apiKey),
132
+ new Promise((_, reject) => setTimeout(() => reject(new Error("LLM request timed out")), AI_TIMEOUT_MS)),
133
+ ]);
134
+ responseData = response.data;
135
+ },
136
+ },
137
+ {
138
+ id: "generate",
139
+ actionText: "Generating documentation...",
140
+ summaryText: responseData ? strings.doc.generate.done(responseData.sections.length) : "Documentation generated",
141
+ run: async () => {
142
+ // Document generation happened in the analyze step via API call
143
+ },
144
+ },
145
+ ];
146
+ const { success, states } = await runSteps(steps, {
147
+ onStateChange: (stepStates) => {
148
+ renderer.update({ ...baseState, stepStates, elapsed: Date.now() - startTime });
149
+ },
150
+ });
151
+ renderer.finish({
152
+ url: "",
153
+ mode: "doc",
154
+ overallScore: success ? 100 : 0,
155
+ scores: {},
156
+ issueCount: 0,
157
+ passed: success,
158
+ elapsed: Date.now() - startTime,
159
+ });
160
+ if (!success) {
161
+ const failed = states.find(s => s.status === "failed");
162
+ process.stderr.write(renderError({
163
+ message: failed?.failReason || "Command failed",
164
+ suggestion: "vertaa doctor",
165
+ }) + "\n");
166
+ process.exitCode = ExitCode.ERROR;
167
+ }
168
+ if (success && responseData) {
169
+ const data = responseData;
170
+ if (format === "json") {
171
+ writeJsonOutput(data, "doc");
172
+ }
173
+ else {
174
+ writeOutput(format === "markdown" ? data.content : formatDocHuman(data));
175
+ }
176
+ }
177
+ }
65
178
  // ---------------------------------------------------------------------------
66
179
  // Command Registration
67
180
  // ---------------------------------------------------------------------------
@@ -84,77 +197,22 @@ Examples:
84
197
  const globalOpts = command.optsWithGlobals();
85
198
  const config = await resolveConfig(globalOpts.config);
86
199
  const machineMode = globalOpts.machine || false;
87
- const format = resolveCommandFormat("doc", options.format, machineMode);
88
- // Resolve audit data
89
- let auditPayload;
90
- if (options.job) {
91
- const base = resolveApiBase(globalOpts.base);
92
- const apiKey = getApiKey(config.apiKey);
93
- const result = await apiRequest(base, `/audit/${options.job}`, { method: "GET" }, apiKey);
94
- const issues = normalizeIssues(result.issues);
95
- auditPayload = {
96
- job_id: result.job_id || options.job,
97
- url: result.url || null,
98
- scores: result.scores || null,
99
- issues,
100
- };
101
- }
102
- else {
103
- const input = await readJsonInput(options.file);
104
- if (!input) {
105
- console.error("Error: No audit data provided.");
106
- console.error("Usage:");
107
- console.error(" vertaa audit https://example.com --json | vertaa doc");
108
- console.error(" vertaa doc --job <job-id>");
109
- console.error(" vertaa doc --file audit.json");
110
- process.exit(ExitCode.ERROR);
111
- }
112
- const data = input;
113
- const innerData = (data.data && typeof data.data === "object" ? data.data : data);
114
- const issues = normalizeIssues(innerData.issues);
115
- auditPayload = {
116
- job_id: innerData.job_id || null,
117
- url: innerData.url || null,
118
- scores: innerData.scores || null,
119
- issues,
120
- };
121
- }
122
- if (!Array.isArray(auditPayload.issues) ||
123
- auditPayload.issues.length === 0) {
124
- console.error("Error: No issues found in audit data.");
125
- process.exit(ExitCode.ERROR);
126
- }
127
- // Auth check
128
- const base = resolveApiBase(globalOpts.base);
129
- const apiKey = getApiKey(config.apiKey);
130
- // Call LLM doc API
131
- const spinner = createSpinner("Generating team playbook...");
132
- try {
133
- const response = await Promise.race([
134
- apiRequest(base, "/cli/ai/doc", {
135
- method: "POST",
136
- body: {
137
- audit: auditPayload,
138
- teamName: options.team || null,
139
- },
140
- }, apiKey),
141
- new Promise((_, reject) => setTimeout(() => reject(new Error("LLM request timed out")), AI_TIMEOUT_MS)),
142
- ]);
143
- succeedSpinner(spinner, "Playbook ready");
144
- if (format === "json") {
145
- writeJsonOutput(response.data, "doc");
146
- }
147
- else {
148
- // Default markdown output — just emit the content directly
149
- writeOutput(format === "markdown" ? response.data.content : formatDocHuman(response.data));
150
- }
151
- }
152
- catch (error) {
153
- handleAiCommandError(error, "doc", spinner);
154
- }
200
+ await handleDoc({
201
+ job: options.job,
202
+ file: options.file,
203
+ team: options.team,
204
+ format: options.format,
205
+ base: globalOpts.base,
206
+ machine: machineMode,
207
+ apiKey: config.apiKey,
208
+ });
155
209
  }
156
210
  catch (error) {
157
- console.error("Error:", error instanceof Error ? error.message : String(error));
211
+ process.stderr.write(renderError({
212
+ message: error instanceof Error ? error.message : String(error),
213
+ suggestion: "vertaa audit <url>",
214
+ exitCode: ExitCode.ERROR,
215
+ }) + "\n");
158
216
  process.exit(ExitCode.ERROR);
159
217
  }
160
218
  });
@@ -50,6 +50,8 @@ export declare function handleDoctor(options: {
50
50
  online?: boolean;
51
51
  deep?: boolean;
52
52
  format?: string;
53
+ strict?: boolean;
54
+ continueOnError?: boolean;
53
55
  }): Promise<void>;
54
56
  /**
55
57
  * Register the doctor command with the Commander program.
@@ -1 +1 @@
1
- {"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAmBzC,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;IAC1C,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;CACrE;AAMD;;;GAGG;AACH,wBAAsB,WAAW,IAAI,OAAO,CAAC,WAAW,CAAC,CAkCxD;AAED;;;;GAIG;AACH,wBAAsB,SAAS,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC,WAAW,CAAC,CA4FrE;AAED;;;;GAIG;AACH,wBAAsB,YAAY,IAAI,OAAO,CAAC,WAAW,CAAC,CAsBzD;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,OAAO,GAAG,WAAW,CAuD/D;AAMD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,CAqC9D;AAMD,wBAAsB,YAAY,CAAC,OAAO,EAAE;IAC1C,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,GAAG,OAAO,CAAC,IAAI,CAAC,CAgChB;AAMD;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAwB5D"}
1
+ {"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAoBzC,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;IAC1C,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;CACrE;AAMD;;;GAGG;AACH,wBAAsB,WAAW,IAAI,OAAO,CAAC,WAAW,CAAC,CAkCxD;AAED;;;;GAIG;AACH,wBAAsB,SAAS,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC,WAAW,CAAC,CA4FrE;AAED;;;;GAIG;AACH,wBAAsB,YAAY,IAAI,OAAO,CAAC,WAAW,CAAC,CAsBzD;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,OAAO,GAAG,WAAW,CAuD/D;AAMD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,CAqC9D;AA2CD,wBAAsB,YAAY,CAAC,OAAO,EAAE;IAC1C,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B,GAAG,OAAO,CAAC,IAAI,CAAC,CA+IhB;AAMD;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA6B5D"}