@vertaaux/cli 0.4.0 → 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 (223) 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 +8 -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 -1097
  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 +263 -92
  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 +2 -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 +2 -1
  168. package/dist/output/human.d.ts.map +1 -1
  169. package/dist/output/human.js +3 -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/url-classify.d.ts.map +1 -1
  201. package/dist/utils/url-classify.js +24 -3
  202. package/node_modules/@vertaaux/tui/dist/index.cjs +713 -20
  203. package/node_modules/@vertaaux/tui/dist/index.cjs.map +1 -1
  204. package/node_modules/@vertaaux/tui/dist/index.d.cts +361 -4
  205. package/node_modules/@vertaaux/tui/dist/index.d.ts +361 -4
  206. package/node_modules/@vertaaux/tui/dist/index.js +689 -21
  207. package/node_modules/@vertaaux/tui/dist/index.js.map +1 -1
  208. package/package.json +13 -5
  209. package/dist/commands/client.d.ts +0 -14
  210. package/dist/commands/client.d.ts.map +0 -1
  211. package/dist/commands/client.js +0 -362
  212. package/dist/commands/drift.d.ts +0 -15
  213. package/dist/commands/drift.d.ts.map +0 -1
  214. package/dist/commands/drift.js +0 -309
  215. package/dist/commands/protect.d.ts +0 -16
  216. package/dist/commands/protect.d.ts.map +0 -1
  217. package/dist/commands/protect.js +0 -323
  218. package/dist/commands/report.d.ts +0 -15
  219. package/dist/commands/report.d.ts.map +0 -1
  220. package/dist/commands/report.js +0 -214
  221. package/dist/policy/sync.d.ts +0 -67
  222. package/dist/policy/sync.d.ts.map +0 -1
  223. package/dist/policy/sync.js +0 -147
@@ -0,0 +1,277 @@
1
+ /**
2
+ * Output formatting utilities for the VertaaUX CLI legacy commands.
3
+ * Provides markdown and JSON formatting functions for audit/fix/verify results.
4
+ */
5
+ import { scoreColor, boldColor } from "@vertaaux/tui";
6
+ import { writeOutput } from "../output/envelope.js";
7
+ // ============================================================================
8
+ // Data normalization
9
+ // ============================================================================
10
+ export function normalizeIssues(issues) {
11
+ if (Array.isArray(issues))
12
+ return issues;
13
+ if (issues && typeof issues === "object") {
14
+ const values = Object.values(issues);
15
+ return values.flatMap((value) => (Array.isArray(value) ? value : []));
16
+ }
17
+ return [];
18
+ }
19
+ export function toNumber(value) {
20
+ if (typeof value === "number" && Number.isFinite(value))
21
+ return value;
22
+ return null;
23
+ }
24
+ export function getOverallScore(scores) {
25
+ if (!scores)
26
+ return null;
27
+ const s = scores;
28
+ const direct = toNumber(s.overall ?? s.ux ?? s.total);
29
+ if (direct !== null)
30
+ return direct;
31
+ const numeric = Object.values(s)
32
+ .map((value) => toNumber(value))
33
+ .filter((value) => value !== null);
34
+ if (numeric.length === 0)
35
+ return null;
36
+ const avg = numeric.reduce((sum, value) => sum + value, 0) / numeric.length;
37
+ return Math.round(avg);
38
+ }
39
+ export function getCategoryScore(scores, key) {
40
+ if (!scores)
41
+ return null;
42
+ return toNumber(scores[key]);
43
+ }
44
+ // ============================================================================
45
+ // Markdown formatters
46
+ // ============================================================================
47
+ export function formatScoresTable(scores) {
48
+ if (!scores)
49
+ return "No scores available.";
50
+ const entries = Object.entries(scores).filter(([, value]) => typeof value === "number");
51
+ if (entries.length === 0)
52
+ return "No scores available.";
53
+ const lines = ["| Category | Score |", "| --- | --- |"];
54
+ for (const [key, value] of entries) {
55
+ lines.push(`| ${key} | ${value} |`);
56
+ }
57
+ return lines.join("\n");
58
+ }
59
+ export function formatIssuesList(issues, limit = 5) {
60
+ if (!issues.length)
61
+ return "No issues.";
62
+ const lines = issues.slice(0, limit).map((issue, index) => {
63
+ const label = issue.title || issue.id || "Issue";
64
+ const severity = issue.severity ? issue.severity.toUpperCase() : "INFO";
65
+ return `${index + 1}. [${severity}] ${label}`;
66
+ });
67
+ if (issues.length > limit) {
68
+ lines.push(`...and ${issues.length - limit} more`);
69
+ }
70
+ return lines.join("\n");
71
+ }
72
+ export function formatAuditMarkdown(result) {
73
+ const status = result.status || "unknown";
74
+ const lines = [`## Audit Results`, `- Status: ${status}`];
75
+ if (result.job_id)
76
+ lines.push(`- Job ID: ${result.job_id}`);
77
+ if (result.url)
78
+ lines.push(`- URL: ${result.url}`);
79
+ if (result.mode)
80
+ lines.push(`- Mode: ${result.mode}`);
81
+ if (typeof result.progress === "number" && status !== "completed") {
82
+ lines.push(`- Progress: ${result.progress}%`);
83
+ }
84
+ if (status !== "completed")
85
+ return lines.join("\n");
86
+ const overall = getOverallScore(result.scores);
87
+ if (overall !== null)
88
+ lines.push(`- Overall score: ${boldColor(String(overall), scoreColor(overall))}`);
89
+ lines.push("\n### Scores");
90
+ lines.push(formatScoresTable(result.scores));
91
+ const issues = normalizeIssues(result.issues);
92
+ lines.push("\n### Issues");
93
+ lines.push(`Total issues: ${issues.length}`);
94
+ lines.push(formatIssuesList(issues));
95
+ return lines.join("\n");
96
+ }
97
+ export function formatA11yMarkdown(result) {
98
+ const lines = [`## Accessibility Audit`, `- Status: ${result.status || "unknown"}`];
99
+ if (result.url)
100
+ lines.push(`- URL: ${result.url}`);
101
+ if (result.mode)
102
+ lines.push(`- Mode: ${result.mode}`);
103
+ if (result.status !== "completed")
104
+ return lines.join("\n");
105
+ const a11yScore = getCategoryScore(result.scores, "accessibility");
106
+ if (a11yScore !== null)
107
+ lines.push(`- Accessibility score: ${boldColor(String(a11yScore), scoreColor(a11yScore))}`);
108
+ const issues = normalizeIssues(result.issues).filter((issue) => {
109
+ const category = (issue.category || "").toLowerCase();
110
+ return (category.includes("accessibility") ||
111
+ category.includes("a11y") ||
112
+ category.includes("wcag"));
113
+ });
114
+ lines.push("\n### Accessibility Issues");
115
+ lines.push(`Total accessibility issues: ${issues.length}`);
116
+ lines.push(formatIssuesList(issues));
117
+ return lines.join("\n");
118
+ }
119
+ export function formatCompareMarkdown(compare) {
120
+ const lines = ["## Audit Comparison"];
121
+ lines.push(`- URL A: ${compare.urlA}`);
122
+ lines.push(`- URL B: ${compare.urlB}`);
123
+ lines.push(`- Job A: ${compare.jobA}`);
124
+ lines.push(`- Job B: ${compare.jobB}`);
125
+ lines.push("\n### Overall Scores");
126
+ lines.push("| Site | Score | Delta |");
127
+ lines.push("| --- | --- | --- |");
128
+ lines.push(`| A | ${compare.overallA !== null ? boldColor(String(compare.overallA), scoreColor(compare.overallA)) : "n/a"} | ${compare.delta ?? "n/a"} |`);
129
+ lines.push(`| B | ${compare.overallB !== null ? boldColor(String(compare.overallB), scoreColor(compare.overallB)) : "n/a"} | ${compare.delta ?? "n/a"} |`);
130
+ lines.push("\n### Category Scores");
131
+ lines.push("| Category | A | B | Delta |");
132
+ lines.push("| --- | --- | --- | --- |");
133
+ for (const [key, values] of Object.entries(compare.categoryDeltas)) {
134
+ lines.push(`| ${key} | ${values.a ?? "n/a"} | ${values.b ?? "n/a"} | ${values.delta ?? "n/a"} |`);
135
+ }
136
+ lines.push("\n### Issue Counts");
137
+ lines.push(`- A: ${compare.issuesA}`);
138
+ lines.push(`- B: ${compare.issuesB}`);
139
+ return lines.join("\n");
140
+ }
141
+ export function formatExplainMarkdown(issue) {
142
+ const lines = ["## Issue Explanation"];
143
+ if (issue.id)
144
+ lines.push(`- ID: ${issue.id}`);
145
+ if (issue.severity)
146
+ lines.push(`- Severity: ${issue.severity}`);
147
+ if (issue.category)
148
+ lines.push(`- Category: ${issue.category}`);
149
+ if (issue.selector)
150
+ lines.push(`- Selector: ${issue.selector}`);
151
+ if (issue.description) {
152
+ lines.push("\n### Description");
153
+ lines.push(issue.description);
154
+ }
155
+ const recommendation = issue.recommendation || issue.recommended_fix;
156
+ if (recommendation) {
157
+ lines.push("\n### Recommendation");
158
+ lines.push(recommendation);
159
+ }
160
+ if (issue.wcag_reference) {
161
+ lines.push("\n### WCAG Reference");
162
+ lines.push(issue.wcag_reference);
163
+ }
164
+ return lines.join("\n");
165
+ }
166
+ export function formatPatchMarkdown(patch) {
167
+ if (!patch)
168
+ return "No patch generated.";
169
+ const lines = [
170
+ "## Patch Generated",
171
+ "",
172
+ `**Confidence:** ${patch.confidence.label} (${patch.confidence.percentage}%)`,
173
+ `**Classification:** ${patch.classification}`,
174
+ "",
175
+ "### Explanation",
176
+ patch.explanation,
177
+ "",
178
+ "### Search",
179
+ "```",
180
+ patch.search,
181
+ "```",
182
+ "",
183
+ "### Replace",
184
+ "```",
185
+ patch.replace,
186
+ "```",
187
+ ];
188
+ return lines.join("\n");
189
+ }
190
+ export function formatVerifyMarkdown(result) {
191
+ if (!result.success || !result.verification) {
192
+ return `## Verification Failed\n\n${result.error?.message || "Unknown error"}`;
193
+ }
194
+ const v = result.verification;
195
+ const deltaSign = v.score_delta.delta >= 0 ? "+" : "";
196
+ const lines = [
197
+ "## Verification Result",
198
+ "",
199
+ `**Issue Fixed:** ${v.issue_fixed ? "Yes" : "No"}`,
200
+ `**Score Delta:** ${boldColor(String(v.score_delta.before), scoreColor(v.score_delta.before))} -> ${boldColor(String(v.score_delta.after), scoreColor(v.score_delta.after))} (${deltaSign}${v.score_delta.delta})`,
201
+ `**Component:** ${v.component_audited}`,
202
+ `**Duration:** ${v.duration_ms}ms`,
203
+ ];
204
+ if (v.regressions.length > 0) {
205
+ lines.push("", "### Regressions");
206
+ for (const r of v.regressions) {
207
+ lines.push(`- [${r.impact.toUpperCase()}] ${r.rule_id}: ${r.description}`);
208
+ }
209
+ }
210
+ else {
211
+ lines.push("", "### Regressions", "None");
212
+ }
213
+ if (v.error) {
214
+ lines.push("", `**Warning:** ${v.error}`);
215
+ }
216
+ return lines.join("\n");
217
+ }
218
+ // Auto-fixable issue types (from lib/patch/classifier.ts)
219
+ const AUTO_FIXABLE_TYPES = new Set([
220
+ "label",
221
+ "input-missing-label",
222
+ "form-field-label",
223
+ "button-name",
224
+ "missing-button-name",
225
+ "link-name",
226
+ "html-has-lang",
227
+ "missing-lang-attribute",
228
+ "html-lang-valid",
229
+ "document-title",
230
+ "bypass",
231
+ "region",
232
+ "landmark-one-main",
233
+ "color-contrast",
234
+ "autocomplete-valid",
235
+ "aria-required-attr",
236
+ "aria-valid-attr",
237
+ "aria-valid-attr-value",
238
+ ]);
239
+ export function isAutoFixable(issueType) {
240
+ const normalized = issueType.toLowerCase().replace(/^(axe|wcag)-/, "");
241
+ return AUTO_FIXABLE_TYPES.has(normalized);
242
+ }
243
+ export function formatBatchMarkdown(results) {
244
+ const lines = [
245
+ "## Batch Patch Results",
246
+ "",
247
+ `**Total Requested:** ${results.totalRequested}`,
248
+ `**Succeeded:** ${results.successes.length}`,
249
+ `**Failed:** ${results.failures.length}`,
250
+ `**Skipped:** ${results.totalSkipped}`,
251
+ ];
252
+ if (results.successes.length > 0) {
253
+ lines.push("", "### Successful Patches");
254
+ for (const s of results.successes) {
255
+ const conf = s.patch?.confidence;
256
+ const cls = s.patch?.classification || "unknown";
257
+ const confStr = conf ? `${conf.label} (${conf.percentage}%)` : "n/a";
258
+ lines.push(`- ${s.issueId}: ${confStr} [${cls}]`);
259
+ }
260
+ }
261
+ if (results.failures.length > 0) {
262
+ lines.push("", "### Failed");
263
+ for (const f of results.failures) {
264
+ const details = f.details ? ` - ${f.details}` : "";
265
+ lines.push(`- ${f.issueId}: ${f.reason}${details}`);
266
+ }
267
+ }
268
+ return lines.join("\n");
269
+ }
270
+ export function printOutput(format, data, markdown) {
271
+ if (format === "json") {
272
+ process.stdout.write(JSON.stringify(data, null, 2) + "\n");
273
+ return;
274
+ }
275
+ // Route through writeOutput so interactive mode can buffer the output
276
+ writeOutput(markdown || "");
277
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"url-classify.d.ts","sourceRoot":"","sources":["../../src/utils/url-classify.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,MAAM,MAAM,OAAO,GAAG,QAAQ,GAAG,WAAW,GAAG,SAAS,CAAC;AAEzD;;;GAGG;AACH,wBAAgB,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CA2BtD;AA4CD;;;GAGG;AACH,wBAAgB,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAGrD"}
1
+ {"version":3,"file":"url-classify.d.ts","sourceRoot":"","sources":["../../src/utils/url-classify.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,MAAM,MAAM,OAAO,GAAG,QAAQ,GAAG,WAAW,GAAG,SAAS,CAAC;AAEzD;;;GAGG;AACH,wBAAgB,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CA8BtD;AAgED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAGrD"}
@@ -25,9 +25,12 @@ export function classifyUrl(urlString) {
25
25
  hostname.endsWith(".localhost")) {
26
26
  return "localhost";
27
27
  }
28
- // IP literal check
29
- if (net.isIP(hostname)) {
30
- if (isPrivateIp(hostname))
28
+ // IP literal check — strip brackets from IPv6 literals
29
+ const ip = hostname.startsWith("[") && hostname.endsWith("]")
30
+ ? hostname.slice(1, -1)
31
+ : hostname;
32
+ if (net.isIP(ip)) {
33
+ if (isPrivateIp(ip))
31
34
  return "private";
32
35
  return "public";
33
36
  }
@@ -40,6 +43,24 @@ function isPrivateIp(address) {
40
43
  const version = net.isIP(address);
41
44
  if (version === 6) {
42
45
  const lower = address.toLowerCase();
46
+ // IPv4-mapped IPv6 — extract and check as IPv4
47
+ // Handles both dotted form (::ffff:127.0.0.1) and hex form (::ffff:7f00:1)
48
+ if (lower.startsWith("::ffff:")) {
49
+ const mapped = lower.slice(7); // strip "::ffff:"
50
+ if (net.isIPv4(mapped)) {
51
+ return isPrivateIp(mapped);
52
+ }
53
+ // Hex form: two 16-bit groups (e.g. "7f00:1" = 127.0.0.1)
54
+ const hexParts = mapped.split(":");
55
+ if (hexParts.length === 2) {
56
+ const hi = parseInt(hexParts[0], 16);
57
+ const lo = parseInt(hexParts[1], 16);
58
+ if (!isNaN(hi) && !isNaN(lo) && hi <= 0xffff && lo <= 0xffff) {
59
+ const ipv4 = `${(hi >> 8) & 0xff}.${hi & 0xff}.${(lo >> 8) & 0xff}.${lo & 0xff}`;
60
+ return isPrivateIp(ipv4);
61
+ }
62
+ }
63
+ }
43
64
  return (lower === "::" ||
44
65
  lower === "::1" ||
45
66
  lower.startsWith("fc") ||