@vertaaux/cli 0.2.3 → 0.3.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 (59) 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/package.json +9 -5
@@ -1,101 +1,54 @@
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 ora from "ora";
8
- import chalk from "chalk";
9
- import { isTTY, shouldUseColor } from "../utils/detect-env.js";
10
- // Spinner style - classic dots for clean look
11
- const SPINNER_STYLE = {
12
- frames: ["\u2807", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"],
13
- interval: 80,
14
- };
15
- // Brand color for spinner
16
- const BRAND_COLOR = "#78FFB4";
7
+ import { createSpinner as tuiCreateSpinner, } from "@vertaaux/tui";
17
8
  /**
18
9
  * Create a new spinner instance.
19
10
  *
20
11
  * Spinner only displays in TTY mode. In non-TTY environments,
21
12
  * returns a no-op spinner that still tracks state.
22
- *
23
- * @param text - Initial text to display next to spinner
24
- * @returns An ora spinner instance
25
13
  */
26
14
  export function createSpinner(text) {
27
- const useColor = shouldUseColor();
28
- const spinner = ora({
29
- text,
30
- spinner: SPINNER_STYLE,
31
- color: "cyan",
32
- isEnabled: isTTY(),
33
- stream: process.stderr, // Write to stderr to avoid mixing with command output
34
- });
35
- // Apply brand color if colors are enabled
36
- if (useColor) {
37
- spinner.color = "cyan";
38
- }
39
- return spinner;
15
+ return tuiCreateSpinner(text);
40
16
  }
41
17
  /**
42
18
  * Update spinner text with optional progress indicator.
43
- *
44
- * @param spinner - The spinner instance to update
45
- * @param text - New text to display
46
- * @param current - Current progress count (optional)
47
- * @param total - Total items count (optional)
48
19
  */
49
20
  export function updateSpinner(spinner, text, current, total) {
50
21
  if (current !== undefined && total !== undefined) {
51
- const progress = shouldUseColor()
52
- ? chalk.dim(`(${current}/${total})`)
53
- : `(${current}/${total})`;
54
- spinner.text = `${text} ${progress}`;
22
+ spinner.setText(`${text} (${current}/${total})`);
55
23
  }
56
24
  else if (current !== undefined) {
57
- const progress = shouldUseColor()
58
- ? chalk.dim(`(${current})`)
59
- : `(${current})`;
60
- spinner.text = `${text} ${progress}`;
25
+ spinner.setText(`${text} (${current})`);
61
26
  }
62
27
  else {
63
- spinner.text = text;
28
+ spinner.setText(text);
64
29
  }
65
30
  }
66
31
  /**
67
32
  * Complete spinner with success state.
68
- *
69
- * @param spinner - The spinner instance
70
- * @param text - Success message to display
71
33
  */
72
34
  export function succeedSpinner(spinner, text) {
73
- spinner.succeed(shouldUseColor() ? chalk.green(text) : text);
35
+ spinner.succeed(text);
74
36
  }
75
37
  /**
76
38
  * Complete spinner with failure state.
77
- *
78
- * @param spinner - The spinner instance
79
- * @param text - Failure message to display
80
39
  */
81
40
  export function failSpinner(spinner, text) {
82
- spinner.fail(shouldUseColor() ? chalk.red(text) : text);
41
+ spinner.fail(text);
83
42
  }
84
43
  /**
85
44
  * Complete spinner with warning state.
86
- *
87
- * @param spinner - The spinner instance
88
- * @param text - Warning message to display
89
45
  */
90
46
  export function warnSpinner(spinner, text) {
91
- spinner.warn(shouldUseColor() ? chalk.yellow(text) : text);
47
+ spinner.warn(text);
92
48
  }
93
49
  /**
94
50
  * Complete spinner with info state.
95
- *
96
- * @param spinner - The spinner instance
97
- * @param text - Info message to display
98
51
  */
99
52
  export function infoSpinner(spinner, text) {
100
- spinner.info(shouldUseColor() ? chalk.cyan(text) : text);
53
+ spinner.info(text);
101
54
  }
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Table formatting utilities for CLI output.
3
3
  *
4
- * Uses cli-table3 for clean table rendering with severity-based coloring.
4
+ * Delegates to @vertaaux/tui for consistent rendering with severity-based coloring.
5
5
  * Respects terminal width and provides truncation for long content.
6
6
  */
7
7
  export interface Issue {
@@ -33,31 +33,14 @@ export declare function groupBySeverity(issues: Issue[]): Map<string, Issue[]>;
33
33
  export declare function groupByCategory(issues: Issue[]): Map<string, Issue[]>;
34
34
  /**
35
35
  * Format issues into a table string.
36
- *
37
- * Columns: Severity | ID | Description | Selector (optional)
38
- * Issues are sorted by severity (critical -> info).
39
- *
40
- * @param issues - Array of issues to format
41
- * @param options - Formatting options
42
- * @returns Formatted table string
43
36
  */
44
37
  export declare function formatIssuesTable(issues: Issue[], options?: FormatTableOptions): string;
45
- export interface Score {
46
- category: string;
47
- score: number;
48
- }
49
38
  /**
50
39
  * Format scores into a table string.
51
- *
52
- * @param scores - Object mapping category names to scores
53
- * @returns Formatted table string
54
40
  */
55
41
  export declare function formatScoresTable(scores: Record<string, number | unknown>): string;
56
42
  /**
57
43
  * Format a summary line showing issue counts by severity.
58
- *
59
- * @param issues - Array of issues
60
- * @returns Summary string like "3 critical, 5 serious, 12 minor issues"
61
44
  */
62
45
  export declare function formatIssueSummary(issues: Issue[]): string;
63
46
  //# sourceMappingURL=table.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"table.d.ts","sourceRoot":"","sources":["../../src/ui/table.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAuCH,MAAM,WAAW,KAAK;IACpB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,kBAAkB;IACjC,2CAA2C;IAC3C,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,wCAAwC;IACxC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,0CAA0C;IAC1C,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,0CAA0C;IAC1C,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAqCD;;GAEG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,KAAK,EAAE,GAAG,GAAG,CAAC,MAAM,EAAE,KAAK,EAAE,CAAC,CAWrE;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,KAAK,EAAE,GAAG,GAAG,CAAC,MAAM,EAAE,KAAK,EAAE,CAAC,CAWrE;AAED;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,KAAK,EAAE,EACf,OAAO,GAAE,kBAAuB,GAC/B,MAAM,CAoER;AAED,MAAM,WAAW,KAAK;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,MAAM,CAyClF;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,CAkC1D"}
1
+ {"version":3,"file":"table.d.ts","sourceRoot":"","sources":["../../src/ui/table.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAeH,MAAM,WAAW,KAAK;IACpB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,kBAAkB;IACjC,2CAA2C;IAC3C,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,wCAAwC;IACxC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,0CAA0C;IAC1C,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,0CAA0C;IAC1C,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAaD;;GAEG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,KAAK,EAAE,GAAG,GAAG,CAAC,MAAM,EAAE,KAAK,EAAE,CAAC,CASrE;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,KAAK,EAAE,GAAG,GAAG,CAAC,MAAM,EAAE,KAAK,EAAE,CAAC,CASrE;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,KAAK,EAAE,EACf,OAAO,GAAE,kBAAuB,GAC/B,MAAM,CAuCR;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,MAAM,CA2BlF;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,CA4B1D"}
package/dist/ui/table.js CHANGED
@@ -1,71 +1,18 @@
1
1
  /**
2
2
  * Table formatting utilities for CLI output.
3
3
  *
4
- * Uses cli-table3 for clean table rendering with severity-based coloring.
4
+ * Delegates to @vertaaux/tui for consistent rendering with severity-based coloring.
5
5
  * Respects terminal width and provides truncation for long content.
6
6
  */
7
- import Table from "cli-table3";
8
- import chalk from "chalk";
9
- import { shouldUseColor, getTerminalWidth } from "../utils/detect-env.js";
10
- // Severity colors matching brand palette
11
- const SEVERITY_COLORS = {
12
- critical: "#FF6B6B", // Red
13
- error: "#FF6B6B", // Red (alias)
14
- serious: "#FFD93D", // Yellow
15
- warning: "#FFD93D", // Yellow (alias)
16
- minor: "#6BCEFF", // Cyan
17
- moderate: "#6BCEFF", // Cyan (alias)
18
- info: "#888888", // Dim gray
19
- };
20
- // Severity display labels
21
- const SEVERITY_LABELS = {
22
- critical: "CRITICAL",
23
- error: "ERROR",
24
- serious: "SERIOUS",
25
- warning: "WARNING",
26
- minor: "MINOR",
27
- moderate: "MODERATE",
28
- info: "INFO",
29
- };
30
- // Severity sort order (highest first)
31
- const SEVERITY_ORDER = {
32
- critical: 0,
33
- error: 1,
34
- serious: 2,
35
- warning: 3,
36
- minor: 4,
37
- moderate: 5,
38
- info: 6,
39
- };
40
- /**
41
- * Truncate text to fit within max width, adding ellipsis if truncated.
42
- */
43
- function truncate(text, maxWidth) {
44
- if (!text)
45
- return "";
46
- if (text.length <= maxWidth)
47
- return text;
48
- return text.slice(0, maxWidth - 3) + "...";
49
- }
50
- /**
51
- * Get colored severity label.
52
- */
53
- function colorSeverity(severity) {
54
- const normalized = severity.toLowerCase();
55
- const label = SEVERITY_LABELS[normalized] || severity.toUpperCase();
56
- if (!shouldUseColor()) {
57
- return label;
58
- }
59
- const color = SEVERITY_COLORS[normalized] || "#888888";
60
- return chalk.hex(color).bold(label);
61
- }
7
+ import { renderTable, text, } from "@vertaaux/tui";
8
+ import { severityOrder, isColorEnabled, colorize, severity as severityPalette, tokens, } from "@vertaaux/tui";
62
9
  /**
63
10
  * Sort issues by severity (highest first).
64
11
  */
65
12
  function sortBySeverity(issues) {
66
13
  return [...issues].sort((a, b) => {
67
- const orderA = SEVERITY_ORDER[a.severity?.toLowerCase() || "info"] ?? 6;
68
- const orderB = SEVERITY_ORDER[b.severity?.toLowerCase() || "info"] ?? 6;
14
+ const orderA = severityOrder[a.severity?.toLowerCase() || "info"] ?? 6;
15
+ const orderB = severityOrder[b.severity?.toLowerCase() || "info"] ?? 6;
69
16
  return orderA - orderB;
70
17
  });
71
18
  }
@@ -75,10 +22,10 @@ function sortBySeverity(issues) {
75
22
  export function groupBySeverity(issues) {
76
23
  const groups = new Map();
77
24
  for (const issue of issues) {
78
- const severity = issue.severity?.toLowerCase() || "info";
79
- const group = groups.get(severity) || [];
25
+ const sev = issue.severity?.toLowerCase() || "info";
26
+ const group = groups.get(sev) || [];
80
27
  group.push(issue);
81
- groups.set(severity, group);
28
+ groups.set(sev, group);
82
29
  }
83
30
  return groups;
84
31
  }
@@ -88,149 +35,95 @@ export function groupBySeverity(issues) {
88
35
  export function groupByCategory(issues) {
89
36
  const groups = new Map();
90
37
  for (const issue of issues) {
91
- const category = issue.category || "Uncategorized";
92
- const group = groups.get(category) || [];
38
+ const cat = issue.category || "Uncategorized";
39
+ const group = groups.get(cat) || [];
93
40
  group.push(issue);
94
- groups.set(category, group);
41
+ groups.set(cat, group);
95
42
  }
96
43
  return groups;
97
44
  }
98
45
  /**
99
46
  * Format issues into a table string.
100
- *
101
- * Columns: Severity | ID | Description | Selector (optional)
102
- * Issues are sorted by severity (critical -> info).
103
- *
104
- * @param issues - Array of issues to format
105
- * @param options - Formatting options
106
- * @returns Formatted table string
107
47
  */
108
48
  export function formatIssuesTable(issues, options = {}) {
109
49
  if (!issues.length) {
110
- return shouldUseColor()
111
- ? chalk.green("No issues found.")
50
+ return isColorEnabled()
51
+ ? text.success("No issues found.")
112
52
  : "No issues found.";
113
53
  }
114
- const termWidth = getTerminalWidth();
115
- const { maxDescriptionWidth = Math.min(50, Math.floor(termWidth * 0.4)), maxSelectorWidth = Math.min(30, Math.floor(termWidth * 0.2)), showSelector = true, showCategory = false, } = options;
116
- // Calculate column widths
117
- const severityWidth = 10;
118
- const idWidth = 20;
119
- // Build header
120
- const header = ["Severity", "ID", "Description"];
121
- const colWidths = [severityWidth, idWidth, maxDescriptionWidth];
54
+ const { maxDescriptionWidth = 50, maxSelectorWidth = 30, showSelector = true, showCategory = false, } = options;
55
+ const sorted = sortBySeverity(issues);
56
+ const columns = [
57
+ { key: "severity", label: "Severity", width: 10, color: (val) => text.severityBadge(val) },
58
+ { key: "id", label: "ID", width: 20 },
59
+ { key: "description", label: "Description", width: maxDescriptionWidth },
60
+ ];
122
61
  if (showCategory) {
123
- header.push("Category");
124
- colWidths.push(15);
62
+ columns.push({ key: "category", label: "Category", width: 15 });
125
63
  }
126
64
  if (showSelector) {
127
- header.push("Selector");
128
- colWidths.push(maxSelectorWidth);
65
+ columns.push({ key: "selector", label: "Selector", width: maxSelectorWidth });
129
66
  }
130
- const useColor = shouldUseColor();
131
- const table = new Table({
132
- head: useColor
133
- ? header.map((h) => chalk.bold(h))
134
- : header,
135
- colWidths,
136
- style: {
137
- head: useColor ? ["cyan"] : [],
138
- border: useColor ? ["dim"] : [],
139
- },
140
- wordWrap: true,
141
- });
142
- // Sort and add rows
143
- const sortedIssues = sortBySeverity(issues);
144
- for (const issue of sortedIssues) {
145
- const row = [
146
- colorSeverity(issue.severity || "info"),
147
- truncate(issue.id || "-", idWidth - 2),
148
- truncate(issue.description || issue.title || "-", maxDescriptionWidth - 2),
149
- ];
150
- if (showCategory) {
151
- row.push(truncate(issue.category || "-", 13));
152
- }
153
- if (showSelector) {
154
- row.push(truncate(issue.selector || "-", maxSelectorWidth - 2));
155
- }
156
- table.push(row);
157
- }
158
- return table.toString();
67
+ const rows = sorted.map((issue) => ({
68
+ severity: issue.severity?.toLowerCase() || "info",
69
+ id: issue.id || "-",
70
+ description: issue.description || issue.title || "-",
71
+ category: issue.category || "-",
72
+ selector: issue.selector || "-",
73
+ }));
74
+ return renderTable(rows, columns);
159
75
  }
160
76
  /**
161
77
  * Format scores into a table string.
162
- *
163
- * @param scores - Object mapping category names to scores
164
- * @returns Formatted table string
165
78
  */
166
79
  export function formatScoresTable(scores) {
167
80
  const entries = Object.entries(scores).filter(([, value]) => typeof value === "number");
168
81
  if (!entries.length) {
169
- return shouldUseColor()
170
- ? chalk.dim("No scores available.")
82
+ return isColorEnabled()
83
+ ? text.dim("No scores available.")
171
84
  : "No scores available.";
172
85
  }
173
- const useColor = shouldUseColor();
174
- const table = new Table({
175
- head: useColor
176
- ? [chalk.bold("Category"), chalk.bold("Score")]
177
- : ["Category", "Score"],
178
- colWidths: [20, 10],
179
- style: {
180
- head: useColor ? ["cyan"] : [],
181
- border: useColor ? ["dim"] : [],
86
+ const columns = [
87
+ { key: "category", label: "Category", width: 20 },
88
+ {
89
+ key: "score",
90
+ label: "Score",
91
+ width: 10,
92
+ color: (val) => text.scoreBadge(Number(val)),
182
93
  },
183
- });
184
- for (const [category, score] of entries) {
185
- const numScore = score;
186
- let scoreStr = String(numScore);
187
- if (useColor) {
188
- if (numScore >= 90) {
189
- scoreStr = chalk.green.bold(scoreStr);
190
- }
191
- else if (numScore >= 70) {
192
- scoreStr = chalk.yellow(scoreStr);
193
- }
194
- else {
195
- scoreStr = chalk.red(scoreStr);
196
- }
197
- }
198
- table.push([category, scoreStr]);
199
- }
200
- return table.toString();
94
+ ];
95
+ const rows = entries.map(([category, score]) => ({
96
+ category,
97
+ score: String(score),
98
+ }));
99
+ return renderTable(rows, columns);
201
100
  }
202
101
  /**
203
102
  * Format a summary line showing issue counts by severity.
204
- *
205
- * @param issues - Array of issues
206
- * @returns Summary string like "3 critical, 5 serious, 12 minor issues"
207
103
  */
208
104
  export function formatIssueSummary(issues) {
209
105
  const counts = {};
210
106
  for (const issue of issues) {
211
- const severity = issue.severity?.toLowerCase() || "info";
212
- counts[severity] = (counts[severity] || 0) + 1;
107
+ const sev = issue.severity?.toLowerCase() || "info";
108
+ counts[sev] = (counts[sev] || 0) + 1;
213
109
  }
214
- const useColor = shouldUseColor();
215
- const parts = [];
216
- // Order: critical, serious, minor, info
217
110
  const order = ["critical", "error", "serious", "warning", "minor", "moderate", "info"];
218
- for (const severity of order) {
219
- const count = counts[severity];
111
+ const parts = [];
112
+ for (const sev of order) {
113
+ const count = counts[sev];
220
114
  if (count) {
221
- const label = count === 1 ? severity : severity;
222
- const text = `${count} ${label}`;
223
- if (useColor) {
224
- const color = SEVERITY_COLORS[severity] || "#888888";
225
- parts.push(chalk.hex(color)(text));
115
+ const label = `${count} ${sev}`;
116
+ if (isColorEnabled()) {
117
+ const hex = severityPalette[sev] || tokens.muted;
118
+ parts.push(colorize(label, hex));
226
119
  }
227
120
  else {
228
- parts.push(text);
121
+ parts.push(label);
229
122
  }
230
123
  }
231
124
  }
232
125
  if (!parts.length) {
233
- return useColor ? chalk.green("No issues") : "No issues";
126
+ return isColorEnabled() ? text.success("No issues") : "No issues";
234
127
  }
235
128
  return parts.join(", ") + " issues";
236
129
  }
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Centralized AI error classification and formatting for CLI commands.
3
+ *
4
+ * Provides consistent error handling across all 8 AI-powered commands.
5
+ * Classifies errors into distinct types (timeout, unreachable, auth, etc.)
6
+ * and formats user-friendly messages with actionable suggestions.
7
+ */
8
+ import { type SpinnerInstance } from "../ui/spinner.js";
9
+ /** Default timeout for LLM API requests (30 seconds). */
10
+ export declare const AI_TIMEOUT_MS = 30000;
11
+ /**
12
+ * Classification of AI/LLM errors.
13
+ *
14
+ * Each kind maps to a distinct user-facing message with tailored suggestions.
15
+ */
16
+ export type AiErrorKind = "timeout" | "unreachable" | "auth" | "invalid_response" | "rate_limit" | "server_error" | "unknown";
17
+ /**
18
+ * Classify an unknown error into a specific AI error kind.
19
+ *
20
+ * Inspects error properties (name, code, message, status) to determine
21
+ * the most specific classification. Falls back to `"unknown"` when no
22
+ * pattern matches.
23
+ *
24
+ * @param error - The caught error (any type)
25
+ * @returns The classified error kind
26
+ */
27
+ export declare function classifyAiError(error: unknown): AiErrorKind;
28
+ /**
29
+ * Format an AI error kind into a user-friendly message with suggestions.
30
+ *
31
+ * @param kind - The classified error kind
32
+ * @param command - The CLI command name (e.g., "explain", "triage")
33
+ * @returns Multi-line string with error description and actionable suggestions
34
+ */
35
+ export declare function formatAiError(kind: AiErrorKind, command: string): string;
36
+ /**
37
+ * Handle an AI command error: classify, format, display, and exit.
38
+ *
39
+ * This is the primary entry point for error handling in AI commands.
40
+ * It classifies the error, fails the spinner with a summary, writes
41
+ * the full formatted message to stderr, and exits with ExitCode.ERROR.
42
+ *
43
+ * @param error - The caught error
44
+ * @param command - The CLI command name (e.g., "explain", "triage")
45
+ * @param spinner - Active spinner instance to fail
46
+ */
47
+ export declare function handleAiCommandError(error: unknown, command: string, spinner: SpinnerInstance): never;
48
+ //# sourceMappingURL=ai-error.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ai-error.d.ts","sourceRoot":"","sources":["../../src/utils/ai-error.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,EAAe,KAAK,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAMrE,yDAAyD;AACzD,eAAO,MAAM,aAAa,QAAS,CAAC;AAMpC;;;;GAIG;AACH,MAAM,MAAM,WAAW,GACnB,SAAS,GACT,aAAa,GACb,MAAM,GACN,kBAAkB,GAClB,YAAY,GACZ,cAAc,GACd,SAAS,CAAC;AAMd;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,WAAW,CAoF3D;AAeD;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CA2DxE;AAMD;;;;;;;;;;GAUG;AACH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,OAAO,EACd,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,eAAe,GACvB,KAAK,CAQP"}