@vertaaux/cli 0.2.3 → 0.3.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 (66) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +58 -2
  3. package/dist/auth/device-flow.js +6 -8
  4. package/dist/commands/audit.d.ts +2 -0
  5. package/dist/commands/audit.d.ts.map +1 -1
  6. package/dist/commands/audit.js +165 -6
  7. package/dist/commands/compare.d.ts +20 -0
  8. package/dist/commands/compare.d.ts.map +1 -0
  9. package/dist/commands/compare.js +335 -0
  10. package/dist/commands/doc.d.ts +18 -0
  11. package/dist/commands/doc.d.ts.map +1 -0
  12. package/dist/commands/doc.js +161 -0
  13. package/dist/commands/download.d.ts.map +1 -1
  14. package/dist/commands/download.js +9 -8
  15. package/dist/commands/explain.d.ts +14 -33
  16. package/dist/commands/explain.d.ts.map +1 -1
  17. package/dist/commands/explain.js +277 -179
  18. package/dist/commands/fix-plan.d.ts +15 -0
  19. package/dist/commands/fix-plan.d.ts.map +1 -0
  20. package/dist/commands/fix-plan.js +182 -0
  21. package/dist/commands/patch-review.d.ts +14 -0
  22. package/dist/commands/patch-review.d.ts.map +1 -0
  23. package/dist/commands/patch-review.js +200 -0
  24. package/dist/commands/release-notes.d.ts +17 -0
  25. package/dist/commands/release-notes.d.ts.map +1 -0
  26. package/dist/commands/release-notes.js +145 -0
  27. package/dist/commands/suggest.d.ts +18 -0
  28. package/dist/commands/suggest.d.ts.map +1 -0
  29. package/dist/commands/suggest.js +152 -0
  30. package/dist/commands/triage.d.ts +17 -0
  31. package/dist/commands/triage.d.ts.map +1 -0
  32. package/dist/commands/triage.js +205 -0
  33. package/dist/commands/upload.d.ts.map +1 -1
  34. package/dist/commands/upload.js +8 -7
  35. package/dist/index.js +62 -25
  36. package/dist/output/formats.d.ts.map +1 -1
  37. package/dist/output/formats.js +14 -0
  38. package/dist/output/human.d.ts +1 -10
  39. package/dist/output/human.d.ts.map +1 -1
  40. package/dist/output/human.js +26 -98
  41. package/dist/prompts/command-catalog.d.ts +46 -0
  42. package/dist/prompts/command-catalog.d.ts.map +1 -0
  43. package/dist/prompts/command-catalog.js +187 -0
  44. package/dist/ui/spinner.d.ts +10 -35
  45. package/dist/ui/spinner.d.ts.map +1 -1
  46. package/dist/ui/spinner.js +11 -58
  47. package/dist/ui/table.d.ts +1 -18
  48. package/dist/ui/table.d.ts.map +1 -1
  49. package/dist/ui/table.js +56 -163
  50. package/dist/utils/ai-error.d.ts +48 -0
  51. package/dist/utils/ai-error.d.ts.map +1 -0
  52. package/dist/utils/ai-error.js +190 -0
  53. package/dist/utils/detect-env.d.ts +6 -8
  54. package/dist/utils/detect-env.d.ts.map +1 -1
  55. package/dist/utils/detect-env.js +6 -25
  56. package/dist/utils/stdin.d.ts +50 -0
  57. package/dist/utils/stdin.d.ts.map +1 -0
  58. package/dist/utils/stdin.js +93 -0
  59. package/node_modules/@vertaaux/tui/dist/index.cjs +1157 -0
  60. package/node_modules/@vertaaux/tui/dist/index.cjs.map +1 -0
  61. package/node_modules/@vertaaux/tui/dist/index.d.cts +609 -0
  62. package/node_modules/@vertaaux/tui/dist/index.d.ts +609 -0
  63. package/node_modules/@vertaaux/tui/dist/index.js +1100 -0
  64. package/node_modules/@vertaaux/tui/dist/index.js.map +1 -0
  65. package/node_modules/@vertaaux/tui/package.json +64 -0
  66. package/package.json +12 -5
@@ -2,7 +2,7 @@
2
2
  * Human-readable output formatter for CLI.
3
3
  *
4
4
  * Outputs audit results as colorful, formatted text for terminal display.
5
- * Uses chalk for colors and cli-table3 for table formatting.
5
+ * Delegates to @vertaaux/tui for consistent rendering.
6
6
  */
7
7
  import { type FormatTableOptions } from "../ui/table.js";
8
8
  export interface AuditResult {
@@ -19,23 +19,14 @@ export interface AuditResult {
19
19
  error?: string;
20
20
  }
21
21
  export interface FormatHumanOptions {
22
- /** How to group issues: severity (default), category, route */
23
22
  groupBy?: "severity" | "category" | "route";
24
- /** Whether to show detailed scores table */
25
23
  showScores?: boolean;
26
- /** Whether to show the summary at the end */
27
24
  showSummary?: boolean;
28
- /** Maximum issues to display per group */
29
25
  maxIssuesPerGroup?: number;
30
- /** Table formatting options */
31
26
  tableOptions?: FormatTableOptions;
32
27
  }
33
28
  /**
34
29
  * Format audit result as human-readable string.
35
- *
36
- * @param result - Audit result object
37
- * @param options - Formatting options
38
- * @returns Formatted string for terminal display
39
30
  */
40
31
  export declare function formatAuditHuman(result: AuditResult, options?: FormatHumanOptions): string;
41
32
  //# sourceMappingURL=human.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"human.d.ts","sourceRoot":"","sources":["../../src/output/human.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EAOL,KAAK,kBAAkB,EACxB,MAAM,gBAAgB,CAAC;AAGxB,MAAM,WAAW,WAAW;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,kBAAkB;IACjC,+DAA+D;IAC/D,OAAO,CAAC,EAAE,UAAU,GAAG,UAAU,GAAG,OAAO,CAAC;IAC5C,4CAA4C;IAC5C,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,6CAA6C;IAC7C,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,0CAA0C;IAC1C,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,+BAA+B;IAC/B,YAAY,CAAC,EAAE,kBAAkB,CAAC;CACnC;AAmRD;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,WAAW,EACnB,OAAO,GAAE,kBAAuB,GAC/B,MAAM,CAkDR"}
1
+ {"version":3,"file":"human.d.ts","sourceRoot":"","sources":["../../src/output/human.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AASH,OAAO,EAOL,KAAK,kBAAkB,EACxB,MAAM,gBAAgB,CAAC;AAExB,MAAM,WAAW,WAAW;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,CAAC,EAAE,UAAU,GAAG,UAAU,GAAG,OAAO,CAAC;IAC5C,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,YAAY,CAAC,EAAE,kBAAkB,CAAC;CACnC;AAuND;;GAEG;AACH,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,WAAW,EACnB,OAAO,GAAE,kBAAuB,GAC/B,MAAM,CAuCR"}
@@ -2,11 +2,10 @@
2
2
  * Human-readable output formatter for CLI.
3
3
  *
4
4
  * Outputs audit results as colorful, formatted text for terminal display.
5
- * Uses chalk for colors and cli-table3 for table formatting.
5
+ * Delegates to @vertaaux/tui for consistent rendering.
6
6
  */
7
- import chalk from "chalk";
7
+ import { text, getTerminalWidth, scoreColor, boldColor, } from "@vertaaux/tui";
8
8
  import { formatIssuesTable, formatScoresTable, formatIssueSummary, groupBySeverity, groupByCategory, } from "../ui/table.js";
9
- import { shouldUseColor, getTerminalWidth } from "../utils/detect-env.js";
10
9
  /**
11
10
  * Normalize issues from various API response formats.
12
11
  */
@@ -34,41 +33,22 @@ function getOverallScore(scores) {
34
33
  return null;
35
34
  return Math.round(numeric.reduce((a, b) => a + b, 0) / numeric.length);
36
35
  }
37
- /**
38
- * Format a horizontal rule.
39
- */
40
- function hr() {
41
- const width = Math.min(getTerminalWidth(), 80);
42
- return shouldUseColor()
43
- ? chalk.dim("-".repeat(width))
44
- : "-".repeat(width);
45
- }
46
36
  /**
47
37
  * Format the header section.
48
38
  */
49
39
  function formatHeader(result) {
50
- const useColor = shouldUseColor();
51
40
  const lines = [];
52
- // Title
53
- const title = useColor
54
- ? chalk.bold.cyan("Audit Complete")
55
- : "AUDIT COMPLETE";
56
- lines.push(title);
57
- // URL
41
+ lines.push(text.heading("Audit Complete"));
58
42
  if (result.url) {
59
- const urlLine = useColor
60
- ? chalk.dim("URL: ") + chalk.white(result.url)
61
- : `URL: ${result.url}`;
62
- lines.push(urlLine);
43
+ lines.push(`${text.dim("URL:")} ${text.link(result.url)}`);
63
44
  }
64
- // Mode and Job ID
65
45
  const meta = [];
66
46
  if (result.mode)
67
47
  meta.push(`Mode: ${result.mode}`);
68
48
  if (result.job_id)
69
49
  meta.push(`Job: ${result.job_id}`);
70
50
  if (meta.length) {
71
- lines.push(useColor ? chalk.dim(meta.join(" | ")) : meta.join(" | "));
51
+ lines.push(text.dim(meta.join(" | ")));
72
52
  }
73
53
  return lines.join("\n");
74
54
  }
@@ -76,34 +56,20 @@ function formatHeader(result) {
76
56
  * Format the scores section.
77
57
  */
78
58
  function formatScoresSection(scores) {
79
- const useColor = shouldUseColor();
80
59
  const lines = [];
81
60
  lines.push("");
82
- lines.push(useColor ? chalk.bold("Scores") : "SCORES");
83
- lines.push(hr());
61
+ lines.push(text.bold("Scores"));
62
+ lines.push(text.hr(Math.min(getTerminalWidth(), 80)));
84
63
  if (!scores || Object.keys(scores).length === 0) {
85
- lines.push(useColor ? chalk.dim("No scores available.") : "No scores available.");
64
+ lines.push(text.dim("No scores available."));
86
65
  return lines.join("\n");
87
66
  }
88
- // Overall score with color coding
89
67
  const overall = getOverallScore(scores);
90
68
  if (overall !== null) {
91
- let scoreDisplay = String(overall);
92
- if (useColor) {
93
- if (overall >= 90) {
94
- scoreDisplay = chalk.green.bold(scoreDisplay);
95
- }
96
- else if (overall >= 70) {
97
- scoreDisplay = chalk.yellow.bold(scoreDisplay);
98
- }
99
- else {
100
- scoreDisplay = chalk.red.bold(scoreDisplay);
101
- }
102
- }
103
- lines.push(`Overall: ${scoreDisplay}/100`);
69
+ const scoreStr = boldColor(String(overall), scoreColor(overall));
70
+ lines.push(`Overall: ${scoreStr}/100`);
104
71
  lines.push("");
105
72
  }
106
- // Detailed scores table
107
73
  const numericScores = {};
108
74
  for (const [key, value] of Object.entries(scores)) {
109
75
  if (typeof value === "number" && key !== "overall") {
@@ -119,40 +85,29 @@ function formatScoresSection(scores) {
119
85
  * Format issues section grouped by severity.
120
86
  */
121
87
  function formatIssuesBySeverity(issues, maxPerGroup, tableOptions) {
122
- const useColor = shouldUseColor();
123
88
  const lines = [];
124
89
  lines.push("");
125
- lines.push(useColor ? chalk.bold("Issues") : "ISSUES");
126
- lines.push(hr());
90
+ lines.push(text.bold("Issues"));
91
+ lines.push(text.hr(Math.min(getTerminalWidth(), 80)));
127
92
  if (issues.length === 0) {
128
- lines.push(useColor ? chalk.green("No issues found!") : "No issues found!");
93
+ lines.push(text.success("No issues found!"));
129
94
  return lines.join("\n");
130
95
  }
131
- // Group by severity
132
96
  const groups = groupBySeverity(issues);
133
- const severityOrder = ["critical", "error", "serious", "warning", "minor", "moderate", "info"];
134
- for (const severity of severityOrder) {
135
- const groupIssues = groups.get(severity);
97
+ const order = ["critical", "error", "serious", "warning", "minor", "moderate", "info"];
98
+ for (const sev of order) {
99
+ const groupIssues = groups.get(sev);
136
100
  if (!groupIssues || groupIssues.length === 0)
137
101
  continue;
138
102
  const displayIssues = maxPerGroup
139
103
  ? groupIssues.slice(0, maxPerGroup)
140
104
  : groupIssues;
141
- // Section header
142
- const label = severity.toUpperCase();
143
- const count = groupIssues.length;
144
- const header = useColor
145
- ? chalk.bold(`${label} (${count})`)
146
- : `${label} (${count})`;
147
105
  lines.push("");
148
- lines.push(header);
149
- // Issues table for this severity
106
+ lines.push(text.bold(`${text.severityBadge(sev)} (${groupIssues.length})`));
150
107
  lines.push(formatIssuesTable(displayIssues, tableOptions));
151
- // Show truncation message if needed
152
108
  if (maxPerGroup && groupIssues.length > maxPerGroup) {
153
109
  const remaining = groupIssues.length - maxPerGroup;
154
- const msg = `...and ${remaining} more ${severity} issues`;
155
- lines.push(useColor ? chalk.dim(msg) : msg);
110
+ lines.push(text.dim(`...and ${remaining} more ${sev} issues`));
156
111
  }
157
112
  }
158
113
  return lines.join("\n");
@@ -161,34 +116,25 @@ function formatIssuesBySeverity(issues, maxPerGroup, tableOptions) {
161
116
  * Format issues section grouped by category.
162
117
  */
163
118
  function formatIssuesByCategory(issues, maxPerGroup, tableOptions) {
164
- const useColor = shouldUseColor();
165
119
  const lines = [];
166
120
  lines.push("");
167
- lines.push(useColor ? chalk.bold("Issues by Category") : "ISSUES BY CATEGORY");
168
- lines.push(hr());
121
+ lines.push(text.bold("Issues by Category"));
122
+ lines.push(text.hr(Math.min(getTerminalWidth(), 80)));
169
123
  if (issues.length === 0) {
170
- lines.push(useColor ? chalk.green("No issues found!") : "No issues found!");
124
+ lines.push(text.success("No issues found!"));
171
125
  return lines.join("\n");
172
126
  }
173
- // Group by category
174
127
  const groups = groupByCategory(issues);
175
128
  for (const [category, groupIssues] of groups) {
176
129
  const displayIssues = maxPerGroup
177
130
  ? groupIssues.slice(0, maxPerGroup)
178
131
  : groupIssues;
179
- // Section header
180
- const header = useColor
181
- ? chalk.bold(`${category} (${groupIssues.length})`)
182
- : `${category} (${groupIssues.length})`;
183
132
  lines.push("");
184
- lines.push(header);
185
- // Issues table for this category
133
+ lines.push(text.bold(`${category} (${groupIssues.length})`));
186
134
  lines.push(formatIssuesTable(displayIssues, tableOptions));
187
- // Show truncation message if needed
188
135
  if (maxPerGroup && groupIssues.length > maxPerGroup) {
189
136
  const remaining = groupIssues.length - maxPerGroup;
190
- const msg = `...and ${remaining} more issues`;
191
- lines.push(useColor ? chalk.dim(msg) : msg);
137
+ lines.push(text.dim(`...and ${remaining} more issues`));
192
138
  }
193
139
  }
194
140
  return lines.join("\n");
@@ -197,14 +143,11 @@ function formatIssuesByCategory(issues, maxPerGroup, tableOptions) {
197
143
  * Format the summary section.
198
144
  */
199
145
  function formatSummary(issues, scores, threshold) {
200
- const useColor = shouldUseColor();
201
146
  const lines = [];
202
147
  lines.push("");
203
- lines.push(hr());
204
- // Summary line
148
+ lines.push(text.hr(Math.min(getTerminalWidth(), 80)));
205
149
  const summary = formatIssueSummary(issues);
206
150
  lines.push(summary);
207
- // Pass/fail status
208
151
  const overall = getOverallScore(scores);
209
152
  const hasErrors = issues.some((i) => i.severity?.toLowerCase() === "critical" || i.severity?.toLowerCase() === "error");
210
153
  let status;
@@ -223,50 +166,35 @@ function formatSummary(issues, scores, threshold) {
223
166
  passed = !hasErrors;
224
167
  status = passed ? "PASSED" : "FAILED";
225
168
  }
226
- if (useColor) {
227
- status = passed ? chalk.green.bold(status) : chalk.red.bold(status);
228
- }
169
+ status = passed ? text.success(status) : text.error(status);
229
170
  lines.push(status);
230
171
  return lines.join("\n");
231
172
  }
232
173
  /**
233
174
  * Format audit result as human-readable string.
234
- *
235
- * @param result - Audit result object
236
- * @param options - Formatting options
237
- * @returns Formatted string for terminal display
238
175
  */
239
176
  export function formatAuditHuman(result, options = {}) {
240
177
  const { groupBy = "severity", showScores = true, showSummary = true, maxIssuesPerGroup = 10, tableOptions, } = options;
241
178
  const sections = [];
242
- // Header
243
179
  sections.push(formatHeader(result));
244
- // Handle non-completed audits
245
180
  if (result.status !== "completed") {
246
- const useColor = shouldUseColor();
247
181
  sections.push("");
248
- sections.push(useColor
249
- ? chalk.yellow(`Status: ${result.status || "unknown"}`)
250
- : `Status: ${result.status || "unknown"}`);
182
+ sections.push(text.warning(`Status: ${result.status || "unknown"}`));
251
183
  if (typeof result.progress === "number") {
252
184
  sections.push(`Progress: ${result.progress}%`);
253
185
  }
254
186
  return sections.join("\n");
255
187
  }
256
- // Scores section
257
188
  if (showScores) {
258
189
  sections.push(formatScoresSection(result.scores));
259
190
  }
260
- // Issues section
261
191
  const issues = normalizeIssues(result.issues);
262
192
  if (groupBy === "category") {
263
193
  sections.push(formatIssuesByCategory(issues, maxIssuesPerGroup, tableOptions));
264
194
  }
265
195
  else {
266
- // Default: group by severity
267
196
  sections.push(formatIssuesBySeverity(issues, maxIssuesPerGroup, tableOptions));
268
197
  }
269
- // Summary
270
198
  if (showSummary) {
271
199
  sections.push(formatSummary(issues, result.scores));
272
200
  }
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Command catalog for the `suggest` command.
3
+ *
4
+ * Maps natural-language intents to exact CLI commands.
5
+ * Used for local fuzzy matching before falling back to LLM API.
6
+ */
7
+ export interface CatalogEntry {
8
+ /** Exact CLI command to run */
9
+ command: string;
10
+ /** Short explanation of what the command does */
11
+ description: string;
12
+ /** Keywords that trigger this entry */
13
+ keywords: string[];
14
+ }
15
+ /**
16
+ * Full command catalog for VertaaUX CLI.
17
+ *
18
+ * Each entry has keywords that are matched against user intent
19
+ * using simple substring/word-overlap scoring.
20
+ */
21
+ export declare const COMMAND_CATALOG: CatalogEntry[];
22
+ /**
23
+ * Score a catalog entry against user intent.
24
+ *
25
+ * Uses a combined scoring approach:
26
+ * - Each exact keyword match earns 1 point (multi-word keywords earn 2)
27
+ * - Partial word overlap earns fractional points
28
+ * - Final score = matchPoints / max(totalWeight, 3) to avoid penalizing
29
+ * entries with many keywords when only a few match.
30
+ *
31
+ * Returns a number between 0 and 1 where higher = better match.
32
+ */
33
+ export declare function scoreMatch(intent: string, entry: CatalogEntry): number;
34
+ /**
35
+ * Find the best matching command(s) for a user intent.
36
+ *
37
+ * @param intent - Natural language intent from the user
38
+ * @param threshold - Minimum score to include (0-1)
39
+ * @param maxResults - Maximum number of results to return
40
+ * @returns Sorted matches with scores
41
+ */
42
+ export declare function findMatches(intent: string, threshold?: number, maxResults?: number): Array<{
43
+ entry: CatalogEntry;
44
+ score: number;
45
+ }>;
46
+ //# sourceMappingURL=command-catalog.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"command-catalog.d.ts","sourceRoot":"","sources":["../../src/prompts/command-catalog.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,WAAW,YAAY;IAC3B,+BAA+B;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,iDAAiD;IACjD,WAAW,EAAE,MAAM,CAAC;IACpB,uCAAuC;IACvC,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED;;;;;GAKG;AACH,eAAO,MAAM,eAAe,EAAE,YAAY,EAwIzC,CAAC;AAEF;;;;;;;;;;GAUG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,GAAG,MAAM,CAuBtE;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CACzB,MAAM,EAAE,MAAM,EACd,SAAS,SAAM,EACf,UAAU,SAAI,GACb,KAAK,CAAC;IAAE,KAAK,EAAE,YAAY,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAU/C"}
@@ -0,0 +1,187 @@
1
+ /**
2
+ * Command catalog for the `suggest` command.
3
+ *
4
+ * Maps natural-language intents to exact CLI commands.
5
+ * Used for local fuzzy matching before falling back to LLM API.
6
+ */
7
+ /**
8
+ * Full command catalog for VertaaUX CLI.
9
+ *
10
+ * Each entry has keywords that are matched against user intent
11
+ * using simple substring/word-overlap scoring.
12
+ */
13
+ export const COMMAND_CATALOG = [
14
+ // --- Audit Commands ---
15
+ {
16
+ command: "vertaa audit <url> --wait",
17
+ description: "Run a UX audit on a URL and wait for results",
18
+ keywords: ["audit", "scan", "check", "test", "run", "url", "website", "page", "site"],
19
+ },
20
+ {
21
+ command: "vertaa audit <url> --mode standard --wait",
22
+ description: "Run a standard-depth audit (more thorough than basic)",
23
+ keywords: ["standard", "thorough", "detailed", "full", "deep scan"],
24
+ },
25
+ {
26
+ command: "vertaa audit <url> --mode deep --wait",
27
+ description: "Run a deep audit with maximum coverage",
28
+ keywords: ["deep", "comprehensive", "maximum", "everything", "all checks"],
29
+ },
30
+ {
31
+ command: "vertaa audit <url> --wait --json",
32
+ description: "Run an audit and output JSON for piping to other commands",
33
+ keywords: ["json", "pipe", "machine", "ci", "scripting", "output json", "structured"],
34
+ },
35
+ {
36
+ command: "vertaa audit <url> --wait --fail-on-score 80",
37
+ description: "Run audit and fail CI if score is below 80",
38
+ keywords: ["ci", "gate", "fail", "threshold", "score", "pipeline", "quality gate"],
39
+ },
40
+ // --- Accessibility ---
41
+ {
42
+ command: "vertaa a11y <url> --wait",
43
+ description: "Run accessibility-focused audit",
44
+ keywords: ["accessibility", "a11y", "wcag", "aria", "screen reader", "accessible"],
45
+ },
46
+ {
47
+ command: "vertaa audit <url> --wait --json | vertaa explain",
48
+ description: "Audit a site and get an AI explanation of findings",
49
+ keywords: ["explain", "understand", "what", "why", "summary", "meaning"],
50
+ },
51
+ // --- Contrast & Color ---
52
+ {
53
+ command: "vertaa audit <url> --wait --json | vertaa triage",
54
+ description: "Audit and prioritize findings by severity",
55
+ keywords: ["contrast", "color", "triage", "prioritize", "priority", "important", "critical", "p0"],
56
+ },
57
+ // --- Compare ---
58
+ {
59
+ command: "vertaa compare <urlA> <urlB> --wait",
60
+ description: "Compare UX audits of two URLs",
61
+ keywords: ["compare", "diff", "versus", "vs", "difference", "before after", "regression"],
62
+ },
63
+ // --- Explain ---
64
+ {
65
+ command: "vertaa explain <finding-id> --job <job-id>",
66
+ description: "Show detailed evidence for a specific finding",
67
+ keywords: ["explain", "finding", "evidence", "detail", "issue", "specific"],
68
+ },
69
+ // --- Triage ---
70
+ {
71
+ command: "vertaa audit <url> --wait --json | vertaa triage",
72
+ description: "Prioritize audit findings into P0/P1/P2 buckets with effort estimates",
73
+ keywords: ["triage", "prioritize", "priority", "bucket", "effort", "plan", "what first"],
74
+ },
75
+ // --- Fix ---
76
+ {
77
+ command: "vertaa fix <jobId> --issue <id> --file-content \"...\"",
78
+ description: "Generate an auto-fix patch for a specific issue",
79
+ keywords: ["fix", "patch", "auto-fix", "repair", "resolve", "remediate"],
80
+ },
81
+ {
82
+ command: "vertaa fix-all <jobId> --file-content \"...\"",
83
+ description: "Generate fix patches for all issues in an audit",
84
+ keywords: ["fix all", "batch", "all issues", "bulk fix"],
85
+ },
86
+ // --- Diff ---
87
+ {
88
+ command: "vertaa diff --job-a <id> --job-b <id>",
89
+ description: "Compare two audit jobs to see what changed",
90
+ keywords: ["diff", "delta", "change", "compare jobs", "what changed"],
91
+ },
92
+ // --- Baseline ---
93
+ {
94
+ command: "vertaa baseline save",
95
+ description: "Save current audit results as a baseline for future comparison",
96
+ keywords: ["baseline", "save", "snapshot", "reference", "lock"],
97
+ },
98
+ // --- Policy ---
99
+ {
100
+ command: "vertaa policy show",
101
+ description: "Show the current audit policy configuration",
102
+ keywords: ["policy", "rules", "config", "configuration", "show policy"],
103
+ },
104
+ // --- Init & Setup ---
105
+ {
106
+ command: "vertaa init",
107
+ description: "Initialize a new VertaaUX project with configuration file",
108
+ keywords: ["init", "setup", "start", "new", "configure", "initialize", "create config"],
109
+ },
110
+ {
111
+ command: "vertaa login",
112
+ description: "Authenticate with VertaaUX API",
113
+ keywords: ["login", "auth", "authenticate", "sign in", "connect", "api key"],
114
+ },
115
+ {
116
+ command: "vertaa doctor",
117
+ description: "Run diagnostics on CLI configuration and connectivity",
118
+ keywords: ["doctor", "diagnostic", "health", "check", "debug", "troubleshoot", "problem"],
119
+ },
120
+ // --- Comment ---
121
+ {
122
+ command: "vertaa comment --job <id> --github-pr <number>",
123
+ description: "Post audit results as a GitHub PR comment",
124
+ keywords: ["comment", "pr", "pull request", "github", "gitlab", "post"],
125
+ },
126
+ // --- Upload/Download ---
127
+ {
128
+ command: "vertaa upload --file baseline.json",
129
+ description: "Upload a baseline file to the VertaaUX API",
130
+ keywords: ["upload", "push", "send"],
131
+ },
132
+ {
133
+ command: "vertaa download --job <id> --output results.json",
134
+ description: "Download audit results to a local file",
135
+ keywords: ["download", "pull", "fetch", "get", "save results"],
136
+ },
137
+ ];
138
+ /**
139
+ * Score a catalog entry against user intent.
140
+ *
141
+ * Uses a combined scoring approach:
142
+ * - Each exact keyword match earns 1 point (multi-word keywords earn 2)
143
+ * - Partial word overlap earns fractional points
144
+ * - Final score = matchPoints / max(totalWeight, 3) to avoid penalizing
145
+ * entries with many keywords when only a few match.
146
+ *
147
+ * Returns a number between 0 and 1 where higher = better match.
148
+ */
149
+ export function scoreMatch(intent, entry) {
150
+ const lower = intent.toLowerCase();
151
+ const words = lower.split(/\s+/).filter(Boolean);
152
+ let matchPoints = 0;
153
+ for (const keyword of entry.keywords) {
154
+ const kw = keyword.toLowerCase();
155
+ const weight = kw.includes(" ") ? 2 : 1;
156
+ if (lower.includes(kw)) {
157
+ matchPoints += weight;
158
+ }
159
+ else {
160
+ // Partial word overlap
161
+ const kwWords = kw.split(/\s+/);
162
+ const overlap = kwWords.filter((w) => words.includes(w)).length / kwWords.length;
163
+ matchPoints += weight * overlap * 0.5;
164
+ }
165
+ }
166
+ // Normalize: divide by a minimum of 3 so that 1 exact match = 0.33 (above threshold)
167
+ const normalizer = Math.max(entry.keywords.length, 3);
168
+ return normalizer > 0 ? Math.min(matchPoints / normalizer, 1) : 0;
169
+ }
170
+ /**
171
+ * Find the best matching command(s) for a user intent.
172
+ *
173
+ * @param intent - Natural language intent from the user
174
+ * @param threshold - Minimum score to include (0-1)
175
+ * @param maxResults - Maximum number of results to return
176
+ * @returns Sorted matches with scores
177
+ */
178
+ export function findMatches(intent, threshold = 0.1, maxResults = 3) {
179
+ const scored = COMMAND_CATALOG.map((entry) => ({
180
+ entry,
181
+ score: scoreMatch(intent, entry),
182
+ }));
183
+ return scored
184
+ .filter((m) => m.score >= threshold)
185
+ .sort((a, b) => b.score - a.score)
186
+ .slice(0, maxResults);
187
+ }
@@ -1,61 +1,36 @@
1
1
  /**
2
2
  * Progress spinner wrapper for CLI.
3
3
  *
4
- * Provides a consistent interface for showing progress during long-running operations.
5
- * Uses ora for elegant terminal spinners with TTY detection.
4
+ * Delegates to @vertaaux/tui for consistent terminal rendering.
5
+ * Writes ONLY to stderr never pollutes stdout.
6
6
  */
7
- import { type Ora } from "ora";
8
- export interface SpinnerOptions {
9
- /** Initial text to display */
10
- text: string;
11
- /** Color for the spinner (default: brand lime) */
12
- color?: string;
13
- }
7
+ import { type SpinnerInstance } from "@vertaaux/tui";
8
+ export type { SpinnerInstance };
14
9
  /**
15
10
  * Create a new spinner instance.
16
11
  *
17
12
  * Spinner only displays in TTY mode. In non-TTY environments,
18
13
  * returns a no-op spinner that still tracks state.
19
- *
20
- * @param text - Initial text to display next to spinner
21
- * @returns An ora spinner instance
22
14
  */
23
- export declare function createSpinner(text: string): Ora;
15
+ export declare function createSpinner(text: string): SpinnerInstance;
24
16
  /**
25
17
  * Update spinner text with optional progress indicator.
26
- *
27
- * @param spinner - The spinner instance to update
28
- * @param text - New text to display
29
- * @param current - Current progress count (optional)
30
- * @param total - Total items count (optional)
31
18
  */
32
- export declare function updateSpinner(spinner: Ora, text: string, current?: number, total?: number): void;
19
+ export declare function updateSpinner(spinner: SpinnerInstance, text: string, current?: number, total?: number): void;
33
20
  /**
34
21
  * Complete spinner with success state.
35
- *
36
- * @param spinner - The spinner instance
37
- * @param text - Success message to display
38
22
  */
39
- export declare function succeedSpinner(spinner: Ora, text: string): void;
23
+ export declare function succeedSpinner(spinner: SpinnerInstance, text: string): void;
40
24
  /**
41
25
  * Complete spinner with failure state.
42
- *
43
- * @param spinner - The spinner instance
44
- * @param text - Failure message to display
45
26
  */
46
- export declare function failSpinner(spinner: Ora, text: string): void;
27
+ export declare function failSpinner(spinner: SpinnerInstance, text: string): void;
47
28
  /**
48
29
  * Complete spinner with warning state.
49
- *
50
- * @param spinner - The spinner instance
51
- * @param text - Warning message to display
52
30
  */
53
- export declare function warnSpinner(spinner: Ora, text: string): void;
31
+ export declare function warnSpinner(spinner: SpinnerInstance, text: string): void;
54
32
  /**
55
33
  * Complete spinner with info state.
56
- *
57
- * @param spinner - The spinner instance
58
- * @param text - Info message to display
59
34
  */
60
- export declare function infoSpinner(spinner: Ora, text: string): void;
35
+ export declare function infoSpinner(spinner: SpinnerInstance, text: string): void;
61
36
  //# sourceMappingURL=spinner.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"spinner.d.ts","sourceRoot":"","sources":["../../src/ui/spinner.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAY,EAAE,KAAK,GAAG,EAAE,MAAM,KAAK,CAAC;AAapC,MAAM,WAAW,cAAc;IAC7B,8BAA8B;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,kDAAkD;IAClD,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,GAAG,CAiB/C;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,GAAG,EACZ,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,MAAM,EAChB,KAAK,CAAC,EAAE,MAAM,GACb,IAAI,CAcN;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAE/D;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAE5D;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAE5D;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAE5D"}
1
+ {"version":3,"file":"spinner.d.ts","sourceRoot":"","sources":["../../src/ui/spinner.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAEL,KAAK,eAAe,EACrB,MAAM,eAAe,CAAC;AAEvB,YAAY,EAAE,eAAe,EAAE,CAAC;AAEhC;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,eAAe,CAE3D;AAED;;GAEG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,eAAe,EACxB,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,MAAM,EAChB,KAAK,CAAC,EAAE,MAAM,GACb,IAAI,CAQN;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,eAAe,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAE3E;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,eAAe,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAExE;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,eAAe,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAExE;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,eAAe,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAExE"}