baseguard 1.0.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 (244) hide show
  1. package/.eslintrc.json +25 -0
  2. package/.prettierrc +8 -0
  3. package/README.md +94 -0
  4. package/bin/base.js +494 -0
  5. package/dist/ai/fix-manager.d.ts +67 -0
  6. package/dist/ai/fix-manager.d.ts.map +1 -0
  7. package/dist/ai/fix-manager.js +326 -0
  8. package/dist/ai/fix-manager.js.map +1 -0
  9. package/dist/ai/gemini-analyzer.d.ts +116 -0
  10. package/dist/ai/gemini-analyzer.d.ts.map +1 -0
  11. package/dist/ai/gemini-analyzer.js +572 -0
  12. package/dist/ai/gemini-analyzer.js.map +1 -0
  13. package/dist/ai/index.d.ts +4 -0
  14. package/dist/ai/index.d.ts.map +1 -0
  15. package/dist/ai/index.js +5 -0
  16. package/dist/ai/index.js.map +1 -0
  17. package/dist/ai/jules-implementer.d.ts +115 -0
  18. package/dist/ai/jules-implementer.d.ts.map +1 -0
  19. package/dist/ai/jules-implementer.js +387 -0
  20. package/dist/ai/jules-implementer.js.map +1 -0
  21. package/dist/commands/automation.d.ts +5 -0
  22. package/dist/commands/automation.d.ts.map +1 -0
  23. package/dist/commands/automation.js +305 -0
  24. package/dist/commands/automation.js.map +1 -0
  25. package/dist/commands/check.d.ts +9 -0
  26. package/dist/commands/check.d.ts.map +1 -0
  27. package/dist/commands/check.js +113 -0
  28. package/dist/commands/check.js.map +1 -0
  29. package/dist/commands/config.d.ts +11 -0
  30. package/dist/commands/config.d.ts.map +1 -0
  31. package/dist/commands/config.js +324 -0
  32. package/dist/commands/config.js.map +1 -0
  33. package/dist/commands/fix.d.ts +9 -0
  34. package/dist/commands/fix.d.ts.map +1 -0
  35. package/dist/commands/fix.js +207 -0
  36. package/dist/commands/fix.js.map +1 -0
  37. package/dist/commands/index.d.ts +6 -0
  38. package/dist/commands/index.d.ts.map +1 -0
  39. package/dist/commands/index.js +7 -0
  40. package/dist/commands/index.js.map +1 -0
  41. package/dist/commands/init.d.ts +9 -0
  42. package/dist/commands/init.d.ts.map +1 -0
  43. package/dist/commands/init.js +125 -0
  44. package/dist/commands/init.js.map +1 -0
  45. package/dist/core/api-key-manager.d.ts +83 -0
  46. package/dist/core/api-key-manager.d.ts.map +1 -0
  47. package/dist/core/api-key-manager.js +244 -0
  48. package/dist/core/api-key-manager.js.map +1 -0
  49. package/dist/core/baseguard.d.ts +46 -0
  50. package/dist/core/baseguard.d.ts.map +1 -0
  51. package/dist/core/baseguard.js +132 -0
  52. package/dist/core/baseguard.js.map +1 -0
  53. package/dist/core/baseline-checker.d.ts +63 -0
  54. package/dist/core/baseline-checker.d.ts.map +1 -0
  55. package/dist/core/baseline-checker.js +502 -0
  56. package/dist/core/baseline-checker.js.map +1 -0
  57. package/dist/core/cache-manager.d.ts +88 -0
  58. package/dist/core/cache-manager.d.ts.map +1 -0
  59. package/dist/core/cache-manager.js +213 -0
  60. package/dist/core/cache-manager.js.map +1 -0
  61. package/dist/core/configuration.d.ts +140 -0
  62. package/dist/core/configuration.d.ts.map +1 -0
  63. package/dist/core/configuration.js +474 -0
  64. package/dist/core/configuration.js.map +1 -0
  65. package/dist/core/directory-filter.d.ts +90 -0
  66. package/dist/core/directory-filter.d.ts.map +1 -0
  67. package/dist/core/directory-filter.js +319 -0
  68. package/dist/core/directory-filter.js.map +1 -0
  69. package/dist/core/error-handler.d.ts +110 -0
  70. package/dist/core/error-handler.d.ts.map +1 -0
  71. package/dist/core/error-handler.js +392 -0
  72. package/dist/core/error-handler.js.map +1 -0
  73. package/dist/core/file-processor.d.ts +80 -0
  74. package/dist/core/file-processor.d.ts.map +1 -0
  75. package/dist/core/file-processor.js +259 -0
  76. package/dist/core/file-processor.js.map +1 -0
  77. package/dist/core/gitignore-manager.d.ts +44 -0
  78. package/dist/core/gitignore-manager.d.ts.map +1 -0
  79. package/dist/core/gitignore-manager.js +147 -0
  80. package/dist/core/gitignore-manager.js.map +1 -0
  81. package/dist/core/index.d.ts +13 -0
  82. package/dist/core/index.d.ts.map +1 -0
  83. package/dist/core/index.js +13 -0
  84. package/dist/core/index.js.map +1 -0
  85. package/dist/core/lazy-loader.d.ts +68 -0
  86. package/dist/core/lazy-loader.d.ts.map +1 -0
  87. package/dist/core/lazy-loader.js +260 -0
  88. package/dist/core/lazy-loader.js.map +1 -0
  89. package/dist/core/memory-manager.d.ts +1 -0
  90. package/dist/core/memory-manager.d.ts.map +1 -0
  91. package/dist/core/memory-manager.js +2 -0
  92. package/dist/core/memory-manager.js.map +1 -0
  93. package/dist/core/startup-optimizer.d.ts +45 -0
  94. package/dist/core/startup-optimizer.d.ts.map +1 -0
  95. package/dist/core/startup-optimizer.js +140 -0
  96. package/dist/core/startup-optimizer.js.map +1 -0
  97. package/dist/git/automation-engine.d.ts +58 -0
  98. package/dist/git/automation-engine.d.ts.map +1 -0
  99. package/dist/git/automation-engine.js +318 -0
  100. package/dist/git/automation-engine.js.map +1 -0
  101. package/dist/git/github-manager.d.ts +71 -0
  102. package/dist/git/github-manager.d.ts.map +1 -0
  103. package/dist/git/github-manager.js +226 -0
  104. package/dist/git/github-manager.js.map +1 -0
  105. package/dist/git/hook-manager.d.ts +43 -0
  106. package/dist/git/hook-manager.d.ts.map +1 -0
  107. package/dist/git/hook-manager.js +191 -0
  108. package/dist/git/hook-manager.js.map +1 -0
  109. package/dist/git/index.d.ts +4 -0
  110. package/dist/git/index.d.ts.map +1 -0
  111. package/dist/git/index.js +5 -0
  112. package/dist/git/index.js.map +1 -0
  113. package/dist/index.d.ts +8 -0
  114. package/dist/index.d.ts.map +1 -0
  115. package/dist/index.js +9 -0
  116. package/dist/index.js.map +1 -0
  117. package/dist/parsers/feature-validator.d.ts +60 -0
  118. package/dist/parsers/feature-validator.d.ts.map +1 -0
  119. package/dist/parsers/feature-validator.js +483 -0
  120. package/dist/parsers/feature-validator.js.map +1 -0
  121. package/dist/parsers/index.d.ts +8 -0
  122. package/dist/parsers/index.d.ts.map +1 -0
  123. package/dist/parsers/index.js +9 -0
  124. package/dist/parsers/index.js.map +1 -0
  125. package/dist/parsers/parser-manager.d.ts +103 -0
  126. package/dist/parsers/parser-manager.d.ts.map +1 -0
  127. package/dist/parsers/parser-manager.js +321 -0
  128. package/dist/parsers/parser-manager.js.map +1 -0
  129. package/dist/parsers/parser.d.ts +23 -0
  130. package/dist/parsers/parser.d.ts.map +1 -0
  131. package/dist/parsers/parser.js +6 -0
  132. package/dist/parsers/parser.js.map +1 -0
  133. package/dist/parsers/react-parser.d.ts +22 -0
  134. package/dist/parsers/react-parser.d.ts.map +1 -0
  135. package/dist/parsers/react-parser.js +307 -0
  136. package/dist/parsers/react-parser.js.map +1 -0
  137. package/dist/parsers/svelte-parser.d.ts +33 -0
  138. package/dist/parsers/svelte-parser.d.ts.map +1 -0
  139. package/dist/parsers/svelte-parser.js +408 -0
  140. package/dist/parsers/svelte-parser.js.map +1 -0
  141. package/dist/parsers/vanilla-parser.d.ts +31 -0
  142. package/dist/parsers/vanilla-parser.d.ts.map +1 -0
  143. package/dist/parsers/vanilla-parser.js +590 -0
  144. package/dist/parsers/vanilla-parser.js.map +1 -0
  145. package/dist/parsers/vue-parser.d.ts +9 -0
  146. package/dist/parsers/vue-parser.d.ts.map +1 -0
  147. package/dist/parsers/vue-parser.js +16 -0
  148. package/dist/parsers/vue-parser.js.map +1 -0
  149. package/dist/terminal-header.d.ts +12 -0
  150. package/dist/terminal-header.js +45 -0
  151. package/dist/types/index.d.ts +83 -0
  152. package/dist/types/index.d.ts.map +1 -0
  153. package/dist/types/index.js +5 -0
  154. package/dist/types/index.js.map +1 -0
  155. package/dist/ui/components.d.ts +133 -0
  156. package/dist/ui/components.d.ts.map +1 -0
  157. package/dist/ui/components.js +482 -0
  158. package/dist/ui/components.js.map +1 -0
  159. package/dist/ui/help.d.ts +11 -0
  160. package/dist/ui/help.d.ts.map +1 -0
  161. package/dist/ui/help.js +161 -0
  162. package/dist/ui/help.js.map +1 -0
  163. package/dist/ui/index.d.ts +5 -0
  164. package/dist/ui/index.d.ts.map +1 -0
  165. package/dist/ui/index.js +5 -0
  166. package/dist/ui/index.js.map +1 -0
  167. package/dist/ui/prompts.d.ts +63 -0
  168. package/dist/ui/prompts.d.ts.map +1 -0
  169. package/dist/ui/prompts.js +611 -0
  170. package/dist/ui/prompts.js.map +1 -0
  171. package/dist/ui/terminal-header.d.ts +13 -0
  172. package/dist/ui/terminal-header.d.ts.map +1 -0
  173. package/dist/ui/terminal-header.js +46 -0
  174. package/dist/ui/terminal-header.js.map +1 -0
  175. package/package.json +80 -0
  176. package/src/ai/__tests__/gemini-analyzer.test.ts +181 -0
  177. package/src/ai/fix-manager.ts +362 -0
  178. package/src/ai/gemini-analyzer.ts +671 -0
  179. package/src/ai/index.ts +4 -0
  180. package/src/ai/jules-implementer.ts +459 -0
  181. package/src/commands/automation.ts +344 -0
  182. package/src/commands/check.ts +299 -0
  183. package/src/commands/config.ts +365 -0
  184. package/src/commands/fix.ts +234 -0
  185. package/src/commands/index.ts +6 -0
  186. package/src/commands/init.ts +142 -0
  187. package/src/commands/status.ts +0 -0
  188. package/src/core/api-key-manager.ts +298 -0
  189. package/src/core/baseguard.ts +742 -0
  190. package/src/core/baseline-checker.ts +563 -0
  191. package/src/core/cache-manager.ts +270 -0
  192. package/src/core/configuration-recovery.ts +676 -0
  193. package/src/core/configuration.ts +559 -0
  194. package/src/core/debug-logger.ts +590 -0
  195. package/src/core/directory-filter.ts +421 -0
  196. package/src/core/error-handler.ts +517 -0
  197. package/src/core/file-processor.ts +331 -0
  198. package/src/core/gitignore-manager.ts +169 -0
  199. package/src/core/graceful-degradation-manager.ts +596 -0
  200. package/src/core/index.ts +13 -0
  201. package/src/core/lazy-loader.ts +307 -0
  202. package/src/core/logger.ts +0 -0
  203. package/src/core/memory-manager.ts +294 -0
  204. package/src/core/startup-optimizer.ts +173 -0
  205. package/src/core/system-error-handler.ts +746 -0
  206. package/src/git/automation-engine.ts +361 -0
  207. package/src/git/github-manager.ts +260 -0
  208. package/src/git/hook-manager.ts +210 -0
  209. package/src/git/index.ts +4 -0
  210. package/src/index.ts +8 -0
  211. package/src/parsers/feature-validator.ts +559 -0
  212. package/src/parsers/index.ts +8 -0
  213. package/src/parsers/parser-manager.ts +419 -0
  214. package/src/parsers/parser.ts +26 -0
  215. package/src/parsers/react-parser-optimized.ts +161 -0
  216. package/src/parsers/react-parser.ts +359 -0
  217. package/src/parsers/svelte-parser.ts +506 -0
  218. package/src/parsers/vanilla-parser.ts +682 -0
  219. package/src/parsers/vue-parser.ts +472 -0
  220. package/src/types/index.ts +92 -0
  221. package/src/ui/components.ts +567 -0
  222. package/src/ui/help.ts +193 -0
  223. package/src/ui/index.ts +4 -0
  224. package/src/ui/prompts.ts +688 -0
  225. package/src/ui/terminal-header.ts +59 -0
  226. package/test-config-commands.js +56 -0
  227. package/test-header-simple.js +33 -0
  228. package/test-terminal-header.js +12 -0
  229. package/test-ui.js +29 -0
  230. package/tests/e2e/baseguard.e2e.test.ts +516 -0
  231. package/tests/e2e/cross-platform.e2e.test.ts +420 -0
  232. package/tests/e2e/git-integration.e2e.test.ts +487 -0
  233. package/tests/fixtures/react-project/package.json +14 -0
  234. package/tests/fixtures/react-project/src/App.css +76 -0
  235. package/tests/fixtures/react-project/src/App.tsx +77 -0
  236. package/tests/fixtures/svelte-project/package.json +11 -0
  237. package/tests/fixtures/svelte-project/src/App.svelte +369 -0
  238. package/tests/fixtures/vanilla-project/index.html +76 -0
  239. package/tests/fixtures/vanilla-project/script.js +331 -0
  240. package/tests/fixtures/vanilla-project/styles.css +359 -0
  241. package/tests/fixtures/vue-project/package.json +12 -0
  242. package/tests/fixtures/vue-project/src/App.vue +216 -0
  243. package/tsconfig.json +36 -0
  244. package/vitest.config.ts +10 -0
@@ -0,0 +1,567 @@
1
+ import chalk from 'chalk';
2
+ import * as Table from 'cli-table3';
3
+ import boxen from 'boxen';
4
+ import ora, { type Ora } from 'ora';
5
+ import type { Violation, Analysis, Fix, Configuration } from '../types/index.js';
6
+
7
+ /**
8
+ * Color scheme for consistent CLI output
9
+ */
10
+ export const Colors = {
11
+ primary: chalk.cyan,
12
+ success: chalk.green,
13
+ error: chalk.red,
14
+ warning: chalk.yellow,
15
+ info: chalk.blue,
16
+ muted: chalk.gray,
17
+ highlight: chalk.magenta,
18
+ feature: chalk.white.bold,
19
+ file: chalk.dim,
20
+ browser: chalk.yellow,
21
+ baseline: chalk.cyan
22
+ } as const;
23
+
24
+ /**
25
+ * UI components for beautiful CLI output
26
+ */
27
+ export { Prompts } from './prompts.js';
28
+
29
+ export class UIComponents {
30
+ /**
31
+ * Display violations grouped by file with clear visual hierarchy
32
+ */
33
+ static showViolations(violations: Violation[]): void {
34
+ if (violations.length === 0) {
35
+ this.showSuccessBox('No compatibility violations found! 🎉');
36
+ return;
37
+ }
38
+
39
+ this.showSectionHeader(`Found ${violations.length} compatibility violation${violations.length === 1 ? '' : 's'}`);
40
+
41
+ // Group violations by file
42
+ const violationsByFile = this.groupViolationsByFile(violations);
43
+
44
+ Object.entries(violationsByFile).forEach(([file, fileViolations]) => {
45
+ console.log(`\n${Colors.file.bold(file)}`);
46
+ console.log(Colors.muted('─'.repeat(Math.min(file.length, 60))));
47
+
48
+ fileViolations.forEach((violation, index) => {
49
+ this.showSingleViolation(violation, index + 1);
50
+ });
51
+ });
52
+
53
+ // Show summary
54
+ this.showViolationSummary(violations);
55
+ }
56
+
57
+ /**
58
+ * Display a single violation with context and details
59
+ */
60
+ static showSingleViolation(violation: Violation, index: number): void {
61
+ const lineInfo = `${Colors.muted('Line')} ${Colors.highlight(violation.line.toString())}`;
62
+ const featureInfo = `${Colors.feature(violation.feature)}`;
63
+ const browserInfo = `${Colors.browser(violation.browser)} ${violation.required}`;
64
+ const baselineInfo = this.formatBaselineStatus(violation.baselineStatus);
65
+
66
+ console.log(`\n ${Colors.error('●')} ${featureInfo} ${Colors.muted('at')} ${lineInfo}`);
67
+ console.log(` ${Colors.muted('Browser:')} ${browserInfo}`);
68
+ console.log(` ${Colors.muted('Baseline:')} ${baselineInfo}`);
69
+
70
+ if (violation.reason) {
71
+ console.log(` ${Colors.muted('Issue:')} ${violation.reason}`);
72
+ }
73
+
74
+ // Show code context with syntax highlighting
75
+ if (violation.context) {
76
+ console.log(` ${Colors.muted('Context:')}`);
77
+ this.showCodeContext(violation.context, violation.line, violation.column);
78
+ }
79
+ }
80
+
81
+ /**
82
+ * Show code context with basic syntax highlighting
83
+ */
84
+ static showCodeContext(context: string, line: number, column: number): void {
85
+ const lines = context.split('\n');
86
+ const contextLines = lines.slice(Math.max(0, lines.length - 3), lines.length);
87
+
88
+ contextLines.forEach((contextLine, index) => {
89
+ const lineNumber = line - (contextLines.length - 1 - index);
90
+ const isTargetLine = lineNumber === line;
91
+
92
+ const lineNumberStr = Colors.muted(lineNumber.toString().padStart(3));
93
+ const prefix = isTargetLine ? Colors.error('►') : Colors.muted('│');
94
+ const content = isTargetLine ? Colors.highlight(contextLine) : Colors.muted(contextLine);
95
+
96
+ console.log(` ${lineNumberStr} ${prefix} ${content}`);
97
+
98
+ // Show column indicator for target line
99
+ if (isTargetLine && column > 0) {
100
+ const spaces = ' '.repeat(6 + 4 + column - 1);
101
+ console.log(`${spaces}${Colors.error('^')}`);
102
+ }
103
+ });
104
+ }
105
+
106
+ /**
107
+ * Format baseline status with appropriate colors
108
+ */
109
+ static formatBaselineStatus(status: string): string {
110
+ switch (status) {
111
+ case 'widely':
112
+ return Colors.success('✓ Widely supported');
113
+ case 'newly':
114
+ return Colors.warning('⚠ Newly available');
115
+ case 'false':
116
+ return Colors.error('✗ Not baseline');
117
+ default:
118
+ return Colors.muted(`${status}`);
119
+ }
120
+ }
121
+
122
+ /**
123
+ * Group violations by file for organized display
124
+ */
125
+ static groupViolationsByFile(violations: Violation[]): Record<string, Violation[]> {
126
+ return violations.reduce((groups, violation) => {
127
+ const file = violation.file;
128
+ if (!groups[file]) {
129
+ groups[file] = [];
130
+ }
131
+ groups[file].push(violation);
132
+ return groups;
133
+ }, {} as Record<string, Violation[]>);
134
+ }
135
+
136
+ /**
137
+ * Show violation summary with actionable next steps
138
+ */
139
+ static showViolationSummary(violations: Violation[]): void {
140
+ console.log('\n');
141
+ this.showSectionHeader('Summary');
142
+
143
+ // Count by browser
144
+ const browserCounts = violations.reduce((counts, v) => {
145
+ const key = `${v.browser} ${v.required}`;
146
+ counts[key] = (counts[key] || 0) + 1;
147
+ return counts;
148
+ }, {} as Record<string, number>);
149
+
150
+ // Count by baseline status
151
+ const baselineCounts = violations.reduce((counts, v) => {
152
+ counts[v.baselineStatus] = (counts[v.baselineStatus] || 0) + 1;
153
+ return counts;
154
+ }, {} as Record<string, number>);
155
+
156
+ // Show browser breakdown
157
+ console.log(Colors.muted('Affected browsers:'));
158
+ Object.entries(browserCounts).forEach(([browser, count]) => {
159
+ console.log(` ${Colors.browser('●')} ${browser}: ${Colors.highlight(count.toString())} issue${count === 1 ? '' : 's'}`);
160
+ });
161
+
162
+ // Show baseline breakdown
163
+ console.log(Colors.muted('\nBaseline status:'));
164
+ Object.entries(baselineCounts).forEach(([status, count]) => {
165
+ const statusDisplay = this.formatBaselineStatus(status);
166
+ console.log(` ${statusDisplay}: ${Colors.highlight(count.toString())} issue${count === 1 ? '' : 's'}`);
167
+ });
168
+
169
+ // Show next steps
170
+ console.log(Colors.muted('\nNext steps:'));
171
+ this.showList([
172
+ `Run ${Colors.primary('base fix')} to automatically fix issues with AI`,
173
+ `Run ${Colors.primary('base check --strict')} to see detailed analysis`,
174
+ `Configure different browser targets with ${Colors.primary('base config targets')}`
175
+ ]);
176
+ }
177
+
178
+ /**
179
+ * Display analysis results with sources and recommendations
180
+ */
181
+ static showAnalysis(analysis: Analysis): void {
182
+ const { violation, userImpact, fixStrategy, bestPractices, sources, plainEnglish, confidence } = analysis;
183
+
184
+ console.log(`\n${Colors.highlight('🧠 AI Analysis')}`);
185
+ console.log(Colors.muted('─'.repeat(20)));
186
+
187
+ console.log(`${Colors.feature(violation.feature)} ${Colors.muted('in')} ${Colors.file(violation.file)}`);
188
+
189
+ if (userImpact) {
190
+ console.log(`\n${Colors.muted('User Impact:')} ${userImpact}`);
191
+ }
192
+
193
+ if (fixStrategy) {
194
+ console.log(`${Colors.muted('Fix Strategy:')} ${fixStrategy}`);
195
+ }
196
+
197
+ if (bestPractices.length > 0) {
198
+ console.log(`\n${Colors.muted('Best Practices:')}`);
199
+ this.showList(bestPractices);
200
+ }
201
+
202
+ if (plainEnglish) {
203
+ console.log(`\n${Colors.muted('Explanation:')}`);
204
+ console.log(this.wrapText(plainEnglish, 70));
205
+ }
206
+
207
+ if (sources.length > 0) {
208
+ console.log(`\n${Colors.muted('Sources:')}`);
209
+ sources.forEach(source => {
210
+ console.log(` ${Colors.info('🔗')} ${Colors.primary(source)}`);
211
+ });
212
+ }
213
+
214
+ const confidenceColor = confidence > 0.8 ? Colors.success : confidence > 0.6 ? Colors.warning : Colors.error;
215
+ console.log(`\n${Colors.muted('Confidence:')} ${confidenceColor((confidence * 100).toFixed(0) + '%')}`);
216
+ }
217
+
218
+ /**
219
+ * Display fix preview with diff-like formatting
220
+ */
221
+ static showFixPreview(fix: Fix): void {
222
+ console.log(`\n${Colors.highlight('🔧 Proposed Fix')}`);
223
+ console.log(Colors.muted('─'.repeat(20)));
224
+
225
+ console.log(`${Colors.file(fix.filePath)}`);
226
+ console.log(`${Colors.muted('Explanation:')} ${fix.explanation}`);
227
+
228
+ if (fix.patch) {
229
+ console.log(`\n${Colors.muted('Changes:')}`);
230
+ this.showDiff(fix.patch);
231
+ }
232
+
233
+ if (fix.preview) {
234
+ console.log(`\n${Colors.muted('Preview:')}`);
235
+ console.log(this.wrapText(fix.preview, 70));
236
+ }
237
+
238
+ const confidenceColor = fix.confidence > 0.8 ? Colors.success : fix.confidence > 0.6 ? Colors.warning : Colors.error;
239
+ console.log(`\n${Colors.muted('Confidence:')} ${confidenceColor((fix.confidence * 100).toFixed(0) + '%')}`);
240
+ }
241
+
242
+ /**
243
+ * Display diff with syntax highlighting
244
+ */
245
+ static showDiff(patch: string): void {
246
+ const lines = patch.split('\n');
247
+
248
+ lines.forEach(line => {
249
+ if (line.startsWith('+')) {
250
+ console.log(` ${Colors.success(line)}`);
251
+ } else if (line.startsWith('-')) {
252
+ console.log(` ${Colors.error(line)}`);
253
+ } else if (line.startsWith('@@')) {
254
+ console.log(` ${Colors.info(line)}`);
255
+ } else {
256
+ console.log(` ${Colors.muted(line)}`);
257
+ }
258
+ });
259
+ }
260
+
261
+ /**
262
+ * Show configuration in a readable format
263
+ */
264
+ static showConfiguration(config: Configuration): void {
265
+ this.showSectionHeader('BaseGuard Configuration');
266
+
267
+ console.log(`${Colors.muted('Version:')} ${config.version}`);
268
+
269
+ console.log(`\n${Colors.muted('Browser Targets:')}`);
270
+ config.targets.forEach(target => {
271
+ const version = target.minVersion === 'baseline' ? Colors.baseline('baseline') :
272
+ target.minVersion === 'baseline-newly' ? Colors.warning('baseline-newly') :
273
+ Colors.highlight(target.minVersion);
274
+ console.log(` ${Colors.browser('●')} ${target.browser} ${version}`);
275
+ });
276
+
277
+ console.log(`\n${Colors.muted('API Keys:')}`);
278
+ const julesStatus = config.apiKeys.jules ? Colors.success('✓ Configured') : Colors.error('✗ Not set');
279
+ const geminiStatus = config.apiKeys.gemini ? Colors.success('✓ Configured') : Colors.error('✗ Not set');
280
+ console.log(` ${Colors.muted('Jules:')} ${julesStatus}`);
281
+ console.log(` ${Colors.muted('Gemini:')} ${geminiStatus}`);
282
+
283
+ if (config.automation) {
284
+ console.log(`\n${Colors.muted('Git Automation:')}`);
285
+ const enabledStatus = config.automation.enabled ? Colors.success('✓ Enabled') : Colors.error('✗ Disabled');
286
+ console.log(` ${Colors.muted('Status:')} ${enabledStatus}`);
287
+
288
+ if (config.automation.enabled) {
289
+ console.log(` ${Colors.muted('Trigger:')} ${config.automation.trigger}`);
290
+ console.log(` ${Colors.muted('Auto-analyze:')} ${config.automation.autoAnalyze ? '✓' : '✗'}`);
291
+ console.log(` ${Colors.muted('Auto-fix:')} ${config.automation.autoFix ? '✓' : '✗'}`);
292
+ console.log(` ${Colors.muted('Block commits:')} ${config.automation.blockCommit ? '✓' : '✗'}`);
293
+ }
294
+ }
295
+ }
296
+
297
+ /**
298
+ * Wrap text to specified width
299
+ */
300
+ static wrapText(text: string, width: number): string {
301
+ const words = text.split(' ');
302
+ const lines: string[] = [];
303
+ let currentLine = '';
304
+
305
+ words.forEach(word => {
306
+ if (currentLine.length + word.length + 1 <= width) {
307
+ currentLine += (currentLine ? ' ' : '') + word;
308
+ } else {
309
+ if (currentLine) lines.push(currentLine);
310
+ currentLine = word;
311
+ }
312
+ });
313
+
314
+ if (currentLine) lines.push(currentLine);
315
+
316
+ return lines.map(line => ` ${line}`).join('\n');
317
+ }
318
+
319
+ /**
320
+ * Create a spinner for long-running operations
321
+ */
322
+ static createSpinner(text: string): Ora {
323
+ return ora({
324
+ text: Colors.primary(text),
325
+ spinner: 'dots',
326
+ color: 'cyan'
327
+ });
328
+ }
329
+
330
+ /**
331
+ * Show success message in a box
332
+ */
333
+ static showSuccessBox(message: string): void {
334
+ console.log(boxen(
335
+ Colors.success.bold('✅ ' + message),
336
+ {
337
+ padding: 1,
338
+ borderStyle: 'round',
339
+ borderColor: 'green'
340
+ }
341
+ ));
342
+ }
343
+
344
+ /**
345
+ * Show error message in a box
346
+ */
347
+ static showErrorBox(message: string): void {
348
+ console.log(boxen(
349
+ Colors.error.bold('❌ ' + message),
350
+ {
351
+ padding: 1,
352
+ borderStyle: 'round',
353
+ borderColor: 'red'
354
+ }
355
+ ));
356
+ }
357
+
358
+ /**
359
+ * Show warning message in a box
360
+ */
361
+ static showWarningBox(message: string): void {
362
+ console.log(boxen(
363
+ Colors.warning.bold('⚠️ ' + message),
364
+ {
365
+ padding: 1,
366
+ borderStyle: 'round',
367
+ borderColor: 'yellow'
368
+ }
369
+ ));
370
+ }
371
+
372
+ /**
373
+ * Show info message in a box
374
+ */
375
+ static showInfoBox(message: string): void {
376
+ console.log(boxen(
377
+ Colors.info.bold('ℹ️ ' + message),
378
+ {
379
+ padding: 1,
380
+ borderStyle: 'round',
381
+ borderColor: 'cyan'
382
+ }
383
+ ));
384
+ }
385
+
386
+ /**
387
+ * Show a formatted header with Baseguard branding
388
+ */
389
+ static showHeader(): void {
390
+ const header = `
391
+ ${Colors.primary.bold('🛡️ Baseguard')} ${Colors.muted('- Never ship incompatible code again')}
392
+ ${Colors.muted('━'.repeat(60))}
393
+ `.trim();
394
+ console.log(header);
395
+ }
396
+
397
+ /**
398
+ * Show the full terminal header with ASCII art
399
+ */
400
+ static showFullHeader(): void {
401
+ try {
402
+ // Use the simple header for now
403
+ this.showTerminalHeader();
404
+ } catch {
405
+ // Fallback to simple header if terminal-header is not available
406
+ this.showHeader();
407
+ }
408
+ }
409
+
410
+ /**
411
+ * Show beautiful terminal header with ASCII art
412
+ */
413
+ static showTerminalHeader(): void {
414
+ console.clear();
415
+
416
+ // Well-spaced ASCII art for "Baseguard" - monochrome for good contrast
417
+ const logo = `
418
+ ██████╗ █████╗ ███████╗ ███████╗ ██████╗ ██╗ ██╗ █████╗ ██████╗ ██████╗
419
+ ██╔══██╗ ██╔══██╗ ██╔════╝ ██╔════╝ ██╔════╝ ██║ ██║ ██╔══██╗ ██╔══██╗ ██╔══██╗
420
+ ██████╔╝ ███████║ ███████╗ █████╗ ██║ ███╗ ██║ ██║ ███████║ ██████╔╝ ██║ ██║
421
+ ██╔══██╗ ██╔══██║ ╚════██║ ██╔══╝ ██║ ██║ ██║ ██║ ██╔══██║ ██╔══██╗ ██║ ██║
422
+ ██████╔╝ ██║ ██║ ███████║ ███████╗ ╚██████╔╝ ╚██████╔╝ ██║ ██║ ██║ ██║ ██████╔╝
423
+ ╚═════╝ ╚═╝ ╚═╝ ╚══════╝ ╚══════╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝
424
+ `;
425
+
426
+ // Use monochrome styling for good contrast
427
+ console.log(chalk.bold.white(logo));
428
+
429
+ // Tagline with styling
430
+ console.log(
431
+ chalk.bold.white(' Ship Modern Code') +
432
+ chalk.dim(' ✨\n')
433
+ );
434
+ }
435
+
436
+ /**
437
+ * Show a formatted section header
438
+ */
439
+ static showSectionHeader(title: string): void {
440
+ console.log(`\n${Colors.highlight.bold(title)}`);
441
+ console.log(Colors.muted('─'.repeat(title.length)));
442
+ }
443
+
444
+ /**
445
+ * Create a formatted table for displaying data
446
+ */
447
+ static createTable(headers: string[]): any {
448
+ return new (Table as any)({
449
+ head: headers.map(h => Colors.primary.bold(h)),
450
+ style: {
451
+ head: [],
452
+ border: ['dim'],
453
+ 'padding-left': 1,
454
+ 'padding-right': 1
455
+ },
456
+ chars: {
457
+ 'top': '─',
458
+ 'top-mid': '┬',
459
+ 'top-left': '┌',
460
+ 'top-right': '┐',
461
+ 'bottom': '─',
462
+ 'bottom-mid': '┴',
463
+ 'bottom-left': '└',
464
+ 'bottom-right': '┘',
465
+ 'left': '│',
466
+ 'left-mid': '├',
467
+ 'mid': '─',
468
+ 'mid-mid': '┼',
469
+ 'right': '│',
470
+ 'right-mid': '┤',
471
+ 'middle': '│'
472
+ }
473
+ });
474
+ }
475
+
476
+ /**
477
+ * Show a progress indicator
478
+ */
479
+ static showProgress(current: number, total: number, operation: string): void {
480
+ const percentage = Math.round((current / total) * 100);
481
+ const progressBar = '█'.repeat(Math.floor(percentage / 5)) + '░'.repeat(20 - Math.floor(percentage / 5));
482
+
483
+ process.stdout.write(`\r${Colors.primary('[')}${Colors.success(progressBar)}${Colors.primary(']')} ${percentage}% ${Colors.muted(operation)}`);
484
+
485
+ if (current === total) {
486
+ process.stdout.write('\n');
487
+ }
488
+ }
489
+
490
+ /**
491
+ * Clear the current line (useful for progress indicators)
492
+ */
493
+ static clearLine(): void {
494
+ process.stdout.write('\r\x1b[K');
495
+ }
496
+
497
+ /**
498
+ * Show a simple status message with icon
499
+ */
500
+ static showStatus(type: 'success' | 'error' | 'warning' | 'info', message: string): void {
501
+ const icons = {
502
+ success: '✅',
503
+ error: '❌',
504
+ warning: '⚠️',
505
+ info: 'ℹ️'
506
+ };
507
+
508
+ const colors = {
509
+ success: Colors.success,
510
+ error: Colors.error,
511
+ warning: Colors.warning,
512
+ info: Colors.info
513
+ };
514
+
515
+ console.log(`${icons[type]} ${colors[type](message)}`);
516
+ }
517
+
518
+ /**
519
+ * Show a list of items with bullets
520
+ */
521
+ static showList(items: string[], bullet: string = '•'): void {
522
+ items.forEach(item => {
523
+ console.log(` ${Colors.muted(bullet)} ${item}`);
524
+ });
525
+ }
526
+
527
+ /**
528
+ * Show a key-value pair
529
+ */
530
+ static showKeyValue(key: string, value: string, indent: number = 0): void {
531
+ const spaces = ' '.repeat(indent);
532
+ console.log(`${spaces}${Colors.muted(key + ':')} ${value}`);
533
+ }
534
+
535
+ /**
536
+ * Show JUnit XML report for violations
537
+ */
538
+ static showJUnitReport(violations: Violation[]): void {
539
+ const testSuites = this.groupViolationsByFile(violations);
540
+ const totalTests = Object.keys(testSuites).length;
541
+ const totalFailures = violations.length;
542
+
543
+ console.log('<?xml version="1.0" encoding="UTF-8"?>');
544
+ console.log(`<testsuites tests="${totalTests}" failures="${totalFailures}" time="0">`);
545
+
546
+ Object.entries(testSuites).forEach(([file, fileViolations]) => {
547
+ console.log(` <testsuite name="${file}" tests="1" failures="${fileViolations.length}" time="0">`);
548
+
549
+ if (fileViolations.length > 0) {
550
+ console.log(` <testcase name="compatibility-check" classname="${file}">`);
551
+ fileViolations.forEach(violation => {
552
+ console.log(` <failure message="${violation.feature} not compatible with ${violation.browser} ${violation.required}">`);
553
+ console.log(` ${violation.reason || 'Compatibility violation detected'}`);
554
+ console.log(` Line: ${violation.line}, Column: ${violation.column}`);
555
+ console.log(' </failure>');
556
+ });
557
+ console.log(' </testcase>');
558
+ } else {
559
+ console.log(` <testcase name="compatibility-check" classname="${file}"/>`);
560
+ }
561
+
562
+ console.log(' </testsuite>');
563
+ });
564
+
565
+ console.log('</testsuites>');
566
+ }
567
+ }