@vibecheckai/cli 3.0.8 → 3.0.10

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.
@@ -1,121 +1,731 @@
1
1
  /**
2
- * vibecheck badge - Generate ship badges for your project
2
+ * vibecheck badge - Generate Premium Ship Badges
3
+ *
4
+ * ═══════════════════════════════════════════════════════════════════════════════
5
+ * ENTERPRISE EDITION - World-Class Badge Generation
6
+ * ═══════════════════════════════════════════════════════════════════════════════
7
+ *
8
+ * Generate stunning, modern badges for your project README.
9
+ * Multiple styles: Classic, Gradient, Cyberpunk, Minimal, Full Stats
3
10
  */
4
11
 
5
12
  const fs = require("fs");
6
13
  const path = require("path");
7
14
 
15
+ // ═══════════════════════════════════════════════════════════════════════════════
16
+ // ADVANCED TERMINAL - ANSI CODES & UTILITIES
17
+ // ═══════════════════════════════════════════════════════════════════════════════
18
+
8
19
  const c = {
9
- reset: "\x1b[0m",
10
- bold: "\x1b[1m",
11
- dim: "\x1b[2m",
12
- green: "\x1b[32m",
13
- yellow: "\x1b[33m",
14
- red: "\x1b[31m",
15
- cyan: "\x1b[36m",
20
+ reset: '\x1b[0m',
21
+ bold: '\x1b[1m',
22
+ dim: '\x1b[2m',
23
+ italic: '\x1b[3m',
24
+ underline: '\x1b[4m',
25
+ blink: '\x1b[5m',
26
+ inverse: '\x1b[7m',
27
+ hidden: '\x1b[8m',
28
+ strike: '\x1b[9m',
29
+ black: '\x1b[30m',
30
+ red: '\x1b[31m',
31
+ green: '\x1b[32m',
32
+ yellow: '\x1b[33m',
33
+ blue: '\x1b[34m',
34
+ magenta: '\x1b[35m',
35
+ cyan: '\x1b[36m',
36
+ white: '\x1b[37m',
37
+ gray: '\x1b[90m',
38
+ brightRed: '\x1b[91m',
39
+ brightGreen: '\x1b[92m',
40
+ brightYellow: '\x1b[93m',
41
+ brightBlue: '\x1b[94m',
42
+ brightMagenta: '\x1b[95m',
43
+ brightCyan: '\x1b[96m',
44
+ brightWhite: '\x1b[97m',
45
+ bgBlack: '\x1b[40m',
46
+ bgRed: '\x1b[41m',
47
+ bgGreen: '\x1b[42m',
48
+ bgYellow: '\x1b[43m',
49
+ bgBlue: '\x1b[44m',
50
+ bgMagenta: '\x1b[45m',
51
+ bgCyan: '\x1b[46m',
52
+ bgWhite: '\x1b[47m',
53
+ clearLine: '\x1b[2K',
54
+ hideCursor: '\x1b[?25l',
55
+ showCursor: '\x1b[?25h',
56
+ };
57
+
58
+ // True color support
59
+ const rgb = (r, g, b) => `\x1b[38;2;${r};${g};${b}m`;
60
+ const bgRgb = (r, g, b) => `\x1b[48;2;${r};${g};${b}m`;
61
+
62
+ // Premium color palette (emerald/teal theme for badges)
63
+ const colors = {
64
+ // Gradient for banner
65
+ gradient1: rgb(0, 255, 200),
66
+ gradient2: rgb(0, 230, 180),
67
+ gradient3: rgb(0, 210, 160),
68
+ gradient4: rgb(0, 190, 140),
69
+ gradient5: rgb(0, 170, 120),
70
+ gradient6: rgb(0, 150, 100),
71
+
72
+ // Verdict colors
73
+ ship: rgb(0, 255, 150),
74
+ warn: rgb(255, 200, 0),
75
+ block: rgb(255, 80, 80),
76
+
77
+ // UI colors
78
+ accent: rgb(0, 255, 200),
79
+ muted: rgb(120, 140, 130),
80
+ success: rgb(0, 255, 150),
81
+ info: rgb(100, 200, 255),
82
+ highlight: rgb(255, 255, 255),
83
+
84
+ // Badge colors
85
+ neonCyan: rgb(0, 255, 255),
86
+ neonGreen: rgb(0, 255, 128),
87
+ neonPink: rgb(255, 0, 128),
88
+ neonPurple: rgb(180, 0, 255),
89
+ };
90
+
91
+ // ═══════════════════════════════════════════════════════════════════════════════
92
+ // PREMIUM BANNER
93
+ // ═══════════════════════════════════════════════════════════════════════════════
94
+
95
+ const BADGE_BANNER = `
96
+ ${rgb(0, 255, 200)} ██████╗ █████╗ ██████╗ ██████╗ ███████╗${c.reset}
97
+ ${rgb(0, 230, 180)} ██╔══██╗██╔══██╗██╔══██╗██╔════╝ ██╔════╝${c.reset}
98
+ ${rgb(0, 210, 160)} ██████╔╝███████║██║ ██║██║ ███╗█████╗ ${c.reset}
99
+ ${rgb(0, 190, 140)} ██╔══██╗██╔══██║██║ ██║██║ ██║██╔══╝ ${c.reset}
100
+ ${rgb(0, 170, 120)} ██████╔╝██║ ██║██████╔╝╚██████╔╝███████╗${c.reset}
101
+ ${rgb(0, 150, 100)} ╚═════╝ ╚═╝ ╚═╝╚═════╝ ╚═════╝ ╚══════╝${c.reset}
102
+ `;
103
+
104
+ const BANNER_FULL = `
105
+ ${rgb(0, 255, 200)} ██╗ ██╗██╗██████╗ ███████╗ ██████╗██╗ ██╗███████╗ ██████╗██╗ ██╗${c.reset}
106
+ ${rgb(0, 230, 180)} ██║ ██║██║██╔══██╗██╔════╝██╔════╝██║ ██║██╔════╝██╔════╝██║ ██╔╝${c.reset}
107
+ ${rgb(0, 210, 160)} ██║ ██║██║██████╔╝█████╗ ██║ ███████║█████╗ ██║ █████╔╝ ${c.reset}
108
+ ${rgb(0, 190, 140)} ╚██╗ ██╔╝██║██╔══██╗██╔══╝ ██║ ██╔══██║██╔══╝ ██║ ██╔═██╗ ${c.reset}
109
+ ${rgb(0, 170, 120)} ╚████╔╝ ██║██████╔╝███████╗╚██████╗██║ ██║███████╗╚██████╗██║ ██╗${c.reset}
110
+ ${rgb(0, 150, 100)} ╚═══╝ ╚═╝╚═════╝ ╚══════╝ ╚═════╝╚═╝ ╚═╝╚══════╝ ╚═════╝╚═╝ ╚═╝${c.reset}
111
+
112
+ ${c.dim} ┌─────────────────────────────────────────────────────────────────────┐${c.reset}
113
+ ${c.dim} │${c.reset} ${rgb(0, 255, 200)}🏆${c.reset} ${c.bold}BADGE${c.reset} ${c.dim}•${c.reset} ${rgb(200, 200, 200)}Premium Badges${c.reset} ${c.dim}•${c.reset} ${rgb(150, 150, 150)}Show Off Your Ship Status${c.reset} ${c.dim}│${c.reset}
114
+ ${c.dim} └─────────────────────────────────────────────────────────────────────┘${c.reset}
115
+ `;
116
+
117
+ // ═══════════════════════════════════════════════════════════════════════════════
118
+ // ICONS & BOX DRAWING
119
+ // ═══════════════════════════════════════════════════════════════════════════════
120
+
121
+ const ICONS = {
122
+ badge: '🏆',
123
+ ship: '🚀',
124
+ warn: '⚠️',
125
+ block: '🛑',
126
+ check: '✓',
127
+ cross: '✗',
128
+ arrow: '→',
129
+ sparkle: '✨',
130
+ star: '★',
131
+ copy: '📋',
132
+ file: '📄',
133
+ paint: '🎨',
134
+ lightning: '⚡',
135
+ };
136
+
137
+ const BOX = {
138
+ topLeft: '╭', topRight: '╮', bottomLeft: '╰', bottomRight: '╯',
139
+ horizontal: '─', vertical: '│',
140
+ dTopLeft: '╔', dTopRight: '╗', dBottomLeft: '╚', dBottomRight: '╝',
141
+ dHorizontal: '═', dVertical: '║',
142
+ };
143
+
144
+ // ═══════════════════════════════════════════════════════════════════════════════
145
+ // UTILITY FUNCTIONS
146
+ // ═══════════════════════════════════════════════════════════════════════════════
147
+
148
+ function padCenter(str, width) {
149
+ const padding = Math.max(0, width - str.length);
150
+ const left = Math.floor(padding / 2);
151
+ const right = padding - left;
152
+ return ' '.repeat(left) + str + ' '.repeat(right);
153
+ }
154
+
155
+ function printDivider(char = '─', width = 69, color = c.dim) {
156
+ console.log(`${color} ${char.repeat(width)}${c.reset}`);
157
+ }
158
+
159
+ function printSection(title, icon = '◆') {
160
+ console.log();
161
+ console.log(` ${colors.accent}${icon}${c.reset} ${c.bold}${title}${c.reset}`);
162
+ printDivider();
163
+ }
164
+
165
+ // ═══════════════════════════════════════════════════════════════════════════════
166
+ // BADGE GENERATION - PREMIUM SVG BADGES
167
+ // ═══════════════════════════════════════════════════════════════════════════════
168
+
169
+ /**
170
+ * Style: Classic (shields.io compatible)
171
+ */
172
+ function generateClassicBadge(verdict, score) {
173
+ const colorMap = {
174
+ SHIP: 'brightgreen',
175
+ WARN: 'yellow',
176
+ BLOCK: 'red',
177
+ };
178
+ const color = colorMap[verdict] || 'lightgrey';
179
+ const message = verdict === 'SHIP' ? 'SHIP%20✓' : verdict;
180
+ return {
181
+ markdown: `[![vibecheck](https://img.shields.io/badge/vibecheck-${message}-${color}?style=flat-square)](https://vibecheckai.dev)`,
182
+ html: `<a href="https://vibecheckai.dev"><img src="https://img.shields.io/badge/vibecheck-${message}-${color}?style=flat-square" alt="vibecheck"></a>`,
183
+ };
184
+ }
185
+
186
+ /**
187
+ * Style: Gradient - Modern gradient badge with glow
188
+ */
189
+ function generateGradientBadge(verdict, score) {
190
+ const configs = {
191
+ SHIP: {
192
+ gradient: ['#00FF96', '#00D4AA'],
193
+ glow: '#00FF96',
194
+ text: 'SHIP READY',
195
+ icon: '✓',
196
+ },
197
+ WARN: {
198
+ gradient: ['#FFD700', '#FFA500'],
199
+ glow: '#FFD700',
200
+ text: 'WARNINGS',
201
+ icon: '⚠',
202
+ },
203
+ BLOCK: {
204
+ gradient: ['#FF6B6B', '#FF4757'],
205
+ glow: '#FF6B6B',
206
+ text: 'BLOCKED',
207
+ icon: '✗',
208
+ },
209
+ };
210
+
211
+ const config = configs[verdict] || configs.SHIP;
212
+
213
+ const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="140" height="28" viewBox="0 0 140 28">
214
+ <defs>
215
+ <linearGradient id="bg" x1="0%" y1="0%" x2="100%" y2="0%">
216
+ <stop offset="0%" style="stop-color:#1a1a2e"/>
217
+ <stop offset="100%" style="stop-color:#16213e"/>
218
+ </linearGradient>
219
+ <linearGradient id="accent" x1="0%" y1="0%" x2="100%" y2="0%">
220
+ <stop offset="0%" style="stop-color:${config.gradient[0]}"/>
221
+ <stop offset="100%" style="stop-color:${config.gradient[1]}"/>
222
+ </linearGradient>
223
+ <filter id="glow">
224
+ <feGaussianBlur stdDeviation="2" result="coloredBlur"/>
225
+ <feMerge>
226
+ <feMergeNode in="coloredBlur"/>
227
+ <feMergeNode in="SourceGraphic"/>
228
+ </feMerge>
229
+ </filter>
230
+ </defs>
231
+ <rect width="140" height="28" rx="6" fill="url(#bg)"/>
232
+ <rect x="1" y="1" width="138" height="26" rx="5" fill="none" stroke="url(#accent)" stroke-width="1" opacity="0.5"/>
233
+ <text x="12" y="18" font-family="system-ui, -apple-system, sans-serif" font-size="11" font-weight="600" fill="url(#accent)" filter="url(#glow)">⚡</text>
234
+ <text x="28" y="18" font-family="system-ui, -apple-system, sans-serif" font-size="10" font-weight="500" fill="#ffffff">vibecheck</text>
235
+ <rect x="88" y="6" width="46" height="16" rx="4" fill="url(#accent)"/>
236
+ <text x="111" y="18" font-family="system-ui, -apple-system, sans-serif" font-size="9" font-weight="700" fill="#000" text-anchor="middle">${config.icon} ${verdict}</text>
237
+ </svg>`;
238
+
239
+ return {
240
+ svg,
241
+ markdown: `![vibecheck ${verdict}](data:image/svg+xml;base64,${Buffer.from(svg).toString('base64')})`,
242
+ html: svg,
243
+ };
244
+ }
245
+
246
+ /**
247
+ * Style: Cyberpunk - Neon glow effect with tech aesthetic
248
+ */
249
+ function generateCyberpunkBadge(verdict, score) {
250
+ const configs = {
251
+ SHIP: {
252
+ primary: '#00FFFF',
253
+ secondary: '#00FF88',
254
+ text: 'SHIP',
255
+ scanline: '#00FFFF',
256
+ },
257
+ WARN: {
258
+ primary: '#FFD700',
259
+ secondary: '#FF8800',
260
+ text: 'WARN',
261
+ scanline: '#FFD700',
262
+ },
263
+ BLOCK: {
264
+ primary: '#FF0066',
265
+ secondary: '#FF4444',
266
+ text: 'BLOCK',
267
+ scanline: '#FF0066',
268
+ },
269
+ };
270
+
271
+ const config = configs[verdict] || configs.SHIP;
272
+
273
+ const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="160" height="32" viewBox="0 0 160 32">
274
+ <defs>
275
+ <linearGradient id="cyberbg" x1="0%" y1="0%" x2="100%" y2="100%">
276
+ <stop offset="0%" style="stop-color:#0a0a0f"/>
277
+ <stop offset="50%" style="stop-color:#1a1a2e"/>
278
+ <stop offset="100%" style="stop-color:#0a0a0f"/>
279
+ </linearGradient>
280
+ <linearGradient id="cyberglow" x1="0%" y1="0%" x2="100%" y2="0%">
281
+ <stop offset="0%" style="stop-color:${config.primary};stop-opacity:0"/>
282
+ <stop offset="50%" style="stop-color:${config.primary};stop-opacity:0.3"/>
283
+ <stop offset="100%" style="stop-color:${config.primary};stop-opacity:0"/>
284
+ </linearGradient>
285
+ <filter id="neon">
286
+ <feGaussianBlur stdDeviation="1.5" result="blur"/>
287
+ <feMerge>
288
+ <feMergeNode in="blur"/>
289
+ <feMergeNode in="blur"/>
290
+ <feMergeNode in="SourceGraphic"/>
291
+ </feMerge>
292
+ </filter>
293
+ <clipPath id="clip">
294
+ <rect width="160" height="32" rx="4"/>
295
+ </clipPath>
296
+ </defs>
297
+ <g clip-path="url(#clip)">
298
+ <rect width="160" height="32" fill="url(#cyberbg)"/>
299
+ <rect width="160" height="1" y="8" fill="url(#cyberglow)"/>
300
+ <rect width="160" height="1" y="23" fill="url(#cyberglow)"/>
301
+ <rect x="0" y="0" width="2" height="32" fill="${config.primary}" opacity="0.8"/>
302
+ <rect x="158" y="0" width="2" height="32" fill="${config.secondary}" opacity="0.8"/>
303
+ </g>
304
+ <text x="14" y="21" font-family="'Courier New', monospace" font-size="11" font-weight="bold" fill="${config.primary}" filter="url(#neon)">VIBE</text>
305
+ <text x="48" y="21" font-family="'Courier New', monospace" font-size="11" font-weight="bold" fill="#ffffff">CHECK</text>
306
+ <rect x="98" y="7" width="54" height="18" rx="2" fill="none" stroke="${config.primary}" stroke-width="1"/>
307
+ <text x="125" y="20" font-family="'Courier New', monospace" font-size="10" font-weight="bold" fill="${config.primary}" text-anchor="middle" filter="url(#neon)">${config.text}</text>
308
+ </svg>`;
309
+
310
+ return {
311
+ svg,
312
+ markdown: `![vibecheck ${verdict}](data:image/svg+xml;base64,${Buffer.from(svg).toString('base64')})`,
313
+ html: svg,
314
+ };
315
+ }
316
+
317
+ /**
318
+ * Style: Minimal - Clean, modern, Apple-inspired
319
+ */
320
+ function generateMinimalBadge(verdict, score) {
321
+ const configs = {
322
+ SHIP: { color: '#34C759', symbol: '●' },
323
+ WARN: { color: '#FF9500', symbol: '●' },
324
+ BLOCK: { color: '#FF3B30', symbol: '●' },
325
+ };
326
+
327
+ const config = configs[verdict] || configs.SHIP;
328
+
329
+ const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="90" height="24" viewBox="0 0 90 24">
330
+ <rect width="90" height="24" rx="12" fill="#f5f5f7"/>
331
+ <circle cx="14" cy="12" r="4" fill="${config.color}"/>
332
+ <text x="26" y="16" font-family="system-ui, -apple-system, SF Pro Text, sans-serif" font-size="11" font-weight="500" fill="#1d1d1f">vibecheck</text>
333
+ </svg>`;
334
+
335
+ return {
336
+ svg,
337
+ markdown: `![vibecheck](data:image/svg+xml;base64,${Buffer.from(svg).toString('base64')})`,
338
+ html: svg,
339
+ };
340
+ }
341
+
342
+ /**
343
+ * Style: Full Stats - Complete badge with score and details
344
+ */
345
+ function generateFullStatsBadge(verdict, score, stats = {}) {
346
+ const configs = {
347
+ SHIP: {
348
+ gradient: ['#00C853', '#00E676'],
349
+ icon: '🚀',
350
+ label: 'SHIP READY',
351
+ },
352
+ WARN: {
353
+ gradient: ['#FF8F00', '#FFB300'],
354
+ icon: '⚠️',
355
+ label: 'WARNINGS',
356
+ },
357
+ BLOCK: {
358
+ gradient: ['#D50000', '#FF1744'],
359
+ icon: '🛑',
360
+ label: 'BLOCKED',
361
+ },
362
+ };
363
+
364
+ const config = configs[verdict] || configs.SHIP;
365
+ const blockers = stats.blockers || 0;
366
+ const warnings = stats.warnings || 0;
367
+
368
+ const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="200" height="60" viewBox="0 0 200 60">
369
+ <defs>
370
+ <linearGradient id="fullbg" x1="0%" y1="0%" x2="100%" y2="100%">
371
+ <stop offset="0%" style="stop-color:#1e1e2e"/>
372
+ <stop offset="100%" style="stop-color:#2d2d44"/>
373
+ </linearGradient>
374
+ <linearGradient id="statusgrad" x1="0%" y1="0%" x2="100%" y2="0%">
375
+ <stop offset="0%" style="stop-color:${config.gradient[0]}"/>
376
+ <stop offset="100%" style="stop-color:${config.gradient[1]}"/>
377
+ </linearGradient>
378
+ </defs>
379
+ <rect width="200" height="60" rx="8" fill="url(#fullbg)"/>
380
+ <rect x="1" y="1" width="198" height="58" rx="7" fill="none" stroke="#3d3d5c" stroke-width="1"/>
381
+
382
+ <!-- Header -->
383
+ <text x="16" y="22" font-family="system-ui, -apple-system, sans-serif" font-size="12" font-weight="600" fill="#ffffff">⚡ vibecheck</text>
384
+
385
+ <!-- Status pill -->
386
+ <rect x="100" y="8" width="90" height="20" rx="10" fill="url(#statusgrad)"/>
387
+ <text x="145" y="22" font-family="system-ui, -apple-system, sans-serif" font-size="10" font-weight="700" fill="#000" text-anchor="middle">${config.label}</text>
388
+
389
+ <!-- Stats row -->
390
+ <text x="16" y="45" font-family="system-ui, -apple-system, sans-serif" font-size="10" fill="#888">Score</text>
391
+ <text x="16" y="55" font-family="system-ui, -apple-system, sans-serif" font-size="11" font-weight="600" fill="#fff">${score}%</text>
392
+
393
+ <text x="70" y="45" font-family="system-ui, -apple-system, sans-serif" font-size="10" fill="#888">Blockers</text>
394
+ <text x="70" y="55" font-family="system-ui, -apple-system, sans-serif" font-size="11" font-weight="600" fill="${blockers > 0 ? '#FF5252' : '#4CAF50'}">${blockers}</text>
395
+
396
+ <text x="130" y="45" font-family="system-ui, -apple-system, sans-serif" font-size="10" fill="#888">Warnings</text>
397
+ <text x="130" y="55" font-family="system-ui, -apple-system, sans-serif" font-size="11" font-weight="600" fill="${warnings > 0 ? '#FFB300' : '#4CAF50'}">${warnings}</text>
398
+ </svg>`;
399
+
400
+ return {
401
+ svg,
402
+ markdown: `![vibecheck](data:image/svg+xml;base64,${Buffer.from(svg).toString('base64')})`,
403
+ html: svg,
404
+ };
405
+ }
406
+
407
+ /**
408
+ * Style: Animated - CSS animation for web use
409
+ */
410
+ function generateAnimatedBadge(verdict, score) {
411
+ const configs = {
412
+ SHIP: {
413
+ gradient: ['#00FF96', '#00D4AA', '#00B8D4'],
414
+ text: 'SHIP ✓',
415
+ },
416
+ WARN: {
417
+ gradient: ['#FFD700', '#FFA500', '#FF8C00'],
418
+ text: 'WARN ⚠',
419
+ },
420
+ BLOCK: {
421
+ gradient: ['#FF6B6B', '#FF4757', '#FF3838'],
422
+ text: 'BLOCK ✗',
423
+ },
424
+ };
425
+
426
+ const config = configs[verdict] || configs.SHIP;
427
+
428
+ const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="150" height="30" viewBox="0 0 150 30">
429
+ <defs>
430
+ <linearGradient id="animbg" x1="0%" y1="0%" x2="100%" y2="0%">
431
+ <stop offset="0%" style="stop-color:#1a1a2e"/>
432
+ <stop offset="100%" style="stop-color:#16213e"/>
433
+ </linearGradient>
434
+ <linearGradient id="animgrad" x1="0%" y1="0%" x2="100%" y2="0%">
435
+ <stop offset="0%" style="stop-color:${config.gradient[0]}">
436
+ <animate attributeName="stop-color" values="${config.gradient.join(';')};${config.gradient[0]}" dur="3s" repeatCount="indefinite"/>
437
+ </stop>
438
+ <stop offset="100%" style="stop-color:${config.gradient[1]}">
439
+ <animate attributeName="stop-color" values="${config.gradient.slice(1).join(';')};${config.gradient[0]};${config.gradient[1]}" dur="3s" repeatCount="indefinite"/>
440
+ </stop>
441
+ </linearGradient>
442
+ <filter id="animglow">
443
+ <feGaussianBlur stdDeviation="1" result="blur"/>
444
+ <feMerge>
445
+ <feMergeNode in="blur"/>
446
+ <feMergeNode in="SourceGraphic"/>
447
+ </feMerge>
448
+ </filter>
449
+ </defs>
450
+ <rect width="150" height="30" rx="6" fill="url(#animbg)"/>
451
+ <rect x="1" y="1" width="148" height="28" rx="5" fill="none" stroke="url(#animgrad)" stroke-width="1.5">
452
+ <animate attributeName="stroke-opacity" values="0.5;1;0.5" dur="2s" repeatCount="indefinite"/>
453
+ </rect>
454
+ <text x="12" y="20" font-family="system-ui, sans-serif" font-size="11" font-weight="600" fill="url(#animgrad)" filter="url(#animglow)">⚡</text>
455
+ <text x="28" y="20" font-family="system-ui, sans-serif" font-size="11" font-weight="500" fill="#fff">vibecheck</text>
456
+ <rect x="95" y="6" width="48" height="18" rx="4" fill="url(#animgrad)"/>
457
+ <text x="119" y="19" font-family="system-ui, sans-serif" font-size="9" font-weight="700" fill="#000" text-anchor="middle">${config.text}</text>
458
+ </svg>`;
459
+
460
+ return {
461
+ svg,
462
+ markdown: `![vibecheck ${verdict}](data:image/svg+xml;base64,${Buffer.from(svg).toString('base64')})`,
463
+ html: svg,
464
+ };
465
+ }
466
+
467
+ /**
468
+ * Style: Holographic - Premium iridescent effect
469
+ */
470
+ function generateHolographicBadge(verdict, score) {
471
+ const configs = {
472
+ SHIP: { text: 'SHIP', icon: '✓' },
473
+ WARN: { text: 'WARN', icon: '⚠' },
474
+ BLOCK: { text: 'BLOCK', icon: '✗' },
475
+ };
476
+
477
+ const config = configs[verdict] || configs.SHIP;
478
+
479
+ const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="160" height="36" viewBox="0 0 160 36">
480
+ <defs>
481
+ <linearGradient id="holobg" x1="0%" y1="0%" x2="100%" y2="100%">
482
+ <stop offset="0%" style="stop-color:#0f0f1a"/>
483
+ <stop offset="50%" style="stop-color:#1a1a2e"/>
484
+ <stop offset="100%" style="stop-color:#0f0f1a"/>
485
+ </linearGradient>
486
+ <linearGradient id="hologram" x1="0%" y1="0%" x2="100%" y2="100%">
487
+ <stop offset="0%" style="stop-color:#00FFFF">
488
+ <animate attributeName="stop-color" values="#00FFFF;#FF00FF;#FFFF00;#00FF00;#00FFFF" dur="4s" repeatCount="indefinite"/>
489
+ </stop>
490
+ <stop offset="25%" style="stop-color:#FF00FF">
491
+ <animate attributeName="stop-color" values="#FF00FF;#FFFF00;#00FF00;#00FFFF;#FF00FF" dur="4s" repeatCount="indefinite"/>
492
+ </stop>
493
+ <stop offset="50%" style="stop-color:#FFFF00">
494
+ <animate attributeName="stop-color" values="#FFFF00;#00FF00;#00FFFF;#FF00FF;#FFFF00" dur="4s" repeatCount="indefinite"/>
495
+ </stop>
496
+ <stop offset="75%" style="stop-color:#00FF00">
497
+ <animate attributeName="stop-color" values="#00FF00;#00FFFF;#FF00FF;#FFFF00;#00FF00" dur="4s" repeatCount="indefinite"/>
498
+ </stop>
499
+ <stop offset="100%" style="stop-color:#00FFFF">
500
+ <animate attributeName="stop-color" values="#00FFFF;#FF00FF;#FFFF00;#00FF00;#00FFFF" dur="4s" repeatCount="indefinite"/>
501
+ </stop>
502
+ </linearGradient>
503
+ <filter id="hologlow">
504
+ <feGaussianBlur stdDeviation="2" result="blur"/>
505
+ <feMerge>
506
+ <feMergeNode in="blur"/>
507
+ <feMergeNode in="blur"/>
508
+ <feMergeNode in="SourceGraphic"/>
509
+ </feMerge>
510
+ </filter>
511
+ <mask id="holomask">
512
+ <rect width="160" height="36" rx="8" fill="white"/>
513
+ </mask>
514
+ </defs>
515
+ <g mask="url(#holomask)">
516
+ <rect width="160" height="36" fill="url(#holobg)"/>
517
+ <rect x="0" y="0" width="160" height="36" fill="url(#hologram)" opacity="0.1">
518
+ <animate attributeName="opacity" values="0.05;0.15;0.05" dur="2s" repeatCount="indefinite"/>
519
+ </rect>
520
+ </g>
521
+ <rect x="1" y="1" width="158" height="34" rx="7" fill="none" stroke="url(#hologram)" stroke-width="1.5"/>
522
+ <text x="16" y="24" font-family="system-ui, -apple-system, sans-serif" font-size="13" font-weight="700" fill="url(#hologram)" filter="url(#hologlow)">⚡ vibecheck</text>
523
+ <rect x="108" y="8" width="44" height="20" rx="4" fill="url(#hologram)" opacity="0.9"/>
524
+ <text x="130" y="22" font-family="system-ui, -apple-system, sans-serif" font-size="10" font-weight="800" fill="#000" text-anchor="middle">${config.icon} ${config.text}</text>
525
+ </svg>`;
526
+
527
+ return {
528
+ svg,
529
+ markdown: `![vibecheck ${verdict}](data:image/svg+xml;base64,${Buffer.from(svg).toString('base64')})`,
530
+ html: svg,
531
+ };
532
+ }
533
+
534
+ // ═══════════════════════════════════════════════════════════════════════════════
535
+ // BADGE STYLE REGISTRY
536
+ // ═══════════════════════════════════════════════════════════════════════════════
537
+
538
+ const BADGE_STYLES = {
539
+ classic: {
540
+ name: 'Classic',
541
+ description: 'shields.io compatible',
542
+ generator: generateClassicBadge,
543
+ },
544
+ gradient: {
545
+ name: 'Gradient',
546
+ description: 'Modern gradient with glow',
547
+ generator: generateGradientBadge,
548
+ },
549
+ cyberpunk: {
550
+ name: 'Cyberpunk',
551
+ description: 'Neon tech aesthetic',
552
+ generator: generateCyberpunkBadge,
553
+ },
554
+ minimal: {
555
+ name: 'Minimal',
556
+ description: 'Clean Apple-inspired',
557
+ generator: generateMinimalBadge,
558
+ },
559
+ full: {
560
+ name: 'Full Stats',
561
+ description: 'Complete with score & stats',
562
+ generator: generateFullStatsBadge,
563
+ },
564
+ animated: {
565
+ name: 'Animated',
566
+ description: 'CSS animation for web',
567
+ generator: generateAnimatedBadge,
568
+ },
569
+ holographic: {
570
+ name: 'Holographic',
571
+ description: 'Premium iridescent effect',
572
+ generator: generateHolographicBadge,
573
+ },
16
574
  };
17
575
 
576
+ // ═══════════════════════════════════════════════════════════════════════════════
577
+ // TERMINAL PREVIEW
578
+ // ═══════════════════════════════════════════════════════════════════════════════
579
+
580
+ function printBadgePreview(verdict, score, style) {
581
+ const verdictColors = {
582
+ SHIP: colors.ship,
583
+ WARN: colors.warn,
584
+ BLOCK: colors.block,
585
+ };
586
+ const vColor = verdictColors[verdict] || colors.accent;
587
+ const vIcon = verdict === 'SHIP' ? ICONS.ship : verdict === 'WARN' ? ICONS.warn : ICONS.block;
588
+
589
+ console.log();
590
+ console.log(` ${c.dim}┌${'─'.repeat(50)}┐${c.reset}`);
591
+ console.log(` ${c.dim}│${c.reset} ${ICONS.paint} ${c.bold}Badge Preview${c.reset}${' '.repeat(33)}${c.dim}│${c.reset}`);
592
+ console.log(` ${c.dim}├${'─'.repeat(50)}┤${c.reset}`);
593
+ console.log(` ${c.dim}│${c.reset} ${c.dim}│${c.reset}`);
594
+
595
+ // Terminal representation of badge
596
+ const styleInfo = BADGE_STYLES[style] || BADGE_STYLES.gradient;
597
+ console.log(` ${c.dim}│${c.reset} ${bgRgb(26, 26, 46)} ${colors.accent}⚡${c.reset}${bgRgb(26, 26, 46)} vibecheck ${c.reset}${vColor}${bgRgb(0, 50, 40)} ${vIcon} ${verdict} ${c.reset} ${c.dim}│${c.reset}`);
598
+
599
+ console.log(` ${c.dim}│${c.reset} ${c.dim}│${c.reset}`);
600
+ console.log(` ${c.dim}│${c.reset} ${c.dim}Style:${c.reset} ${colors.accent}${styleInfo.name}${c.reset} ${c.dim}(${styleInfo.description})${c.reset}${' '.repeat(Math.max(0, 20 - styleInfo.name.length - styleInfo.description.length))}${c.dim}│${c.reset}`);
601
+ console.log(` ${c.dim}│${c.reset} ${c.dim}Score:${c.reset} ${score >= 80 ? colors.success : score >= 50 ? colors.warn : colors.block}${score}%${c.reset}${' '.repeat(39)}${c.dim}│${c.reset}`);
602
+ console.log(` ${c.dim}│${c.reset} ${c.dim}│${c.reset}`);
603
+ console.log(` ${c.dim}└${'─'.repeat(50)}┘${c.reset}`);
604
+ }
605
+
606
+ function printStyleGallery() {
607
+ printSection('AVAILABLE STYLES', ICONS.paint);
608
+ console.log();
609
+
610
+ for (const [key, style] of Object.entries(BADGE_STYLES)) {
611
+ const isAnimated = key === 'animated' || key === 'holographic';
612
+ const animTag = isAnimated ? ` ${c.dim}(animated)${c.reset}` : '';
613
+ console.log(` ${colors.accent}${key.padEnd(12)}${c.reset} ${c.bold}${style.name}${c.reset}${animTag}`);
614
+ console.log(` ${' '.repeat(12)} ${c.dim}${style.description}${c.reset}`);
615
+ console.log();
616
+ }
617
+ }
618
+
619
+ // ═══════════════════════════════════════════════════════════════════════════════
620
+ // ARGS PARSER
621
+ // ═══════════════════════════════════════════════════════════════════════════════
622
+
18
623
  function parseArgs(args) {
19
624
  const opts = {
20
625
  help: false,
21
626
  output: null,
22
- format: "markdown",
627
+ format: 'markdown',
628
+ style: 'gradient',
23
629
  verdict: null,
24
630
  score: null,
631
+ list: false,
632
+ all: false,
25
633
  };
26
634
 
27
635
  for (let i = 0; i < args.length; i++) {
28
636
  const arg = args[i];
29
637
  switch (arg) {
30
- case "--help":
31
- case "-h":
638
+ case '--help':
639
+ case '-h':
32
640
  opts.help = true;
33
641
  break;
34
- case "--output":
35
- case "-o":
642
+ case '--output':
643
+ case '-o':
36
644
  opts.output = args[++i];
37
645
  break;
38
- case "--format":
39
- case "-f":
646
+ case '--format':
647
+ case '-f':
40
648
  opts.format = args[++i];
41
649
  break;
42
- case "--verdict":
43
- opts.verdict = args[++i];
650
+ case '--style':
651
+ case '-s':
652
+ opts.style = args[++i];
44
653
  break;
45
- case "--score":
654
+ case '--verdict':
655
+ opts.verdict = args[++i]?.toUpperCase();
656
+ break;
657
+ case '--score':
46
658
  opts.score = parseInt(args[++i]);
47
659
  break;
660
+ case '--list':
661
+ case '-l':
662
+ opts.list = true;
663
+ break;
664
+ case '--all':
665
+ case '-a':
666
+ opts.all = true;
667
+ break;
48
668
  }
49
669
  }
50
670
 
51
671
  return opts;
52
672
  }
53
673
 
674
+ // ═══════════════════════════════════════════════════════════════════════════════
675
+ // HELP DISPLAY
676
+ // ═══════════════════════════════════════════════════════════════════════════════
677
+
54
678
  function printHelp() {
679
+ console.log(BANNER_FULL);
55
680
  console.log(`
56
- ${c.bold}vibecheck badge${c.reset} - Generate ship badges for your project
57
-
58
- ${c.bold}USAGE${c.reset}
59
- vibecheck badge [options]
60
-
61
- ${c.bold}OPTIONS${c.reset}
62
- --help, -h Show this help
63
- --output, -o <file> Output file (default: stdout)
64
- --format, -f <fmt> Format: markdown, html, svg (default: markdown)
65
- --verdict <v> Override verdict: SHIP, WARN, BLOCK
66
- --score <n> Override score (0-100)
67
-
68
- ${c.bold}EXAMPLES${c.reset}
69
- vibecheck badge # Generate from last scan
70
- vibecheck badge -o README.md # Append to README
71
- vibecheck badge --format svg # SVG badge
72
- vibecheck badge --verdict SHIP # Force SHIP badge
73
- `);
74
- }
681
+ ${c.bold}Usage:${c.reset} vibecheck badge [options]
75
682
 
76
- function getVerdictColor(verdict) {
77
- switch (verdict) {
78
- case "SHIP": return "brightgreen";
79
- case "WARN": return "yellow";
80
- case "BLOCK": return "red";
81
- default: return "lightgrey";
82
- }
83
- }
683
+ ${c.bold}Premium Badge Generator${c.reset} — Show off your ship status in style.
84
684
 
85
- function generateMarkdownBadge(verdict, score) {
86
- const color = getVerdictColor(verdict);
87
- const label = "vibecheck";
88
- const message = verdict === "SHIP" ? `${verdict}%20✓` : verdict;
89
- return `[![vibecheck](https://img.shields.io/badge/${label}-${message}-${color})](https://vibecheckai.dev)`;
90
- }
685
+ ${c.bold}Options:${c.reset}
686
+ ${colors.accent}--help, -h${c.reset} Show this help
687
+ ${colors.accent}--output, -o <file>${c.reset} Output file ${c.dim}(default: stdout)${c.reset}
688
+ ${colors.accent}--format, -f <fmt>${c.reset} Format: markdown, html, svg ${c.dim}(default: markdown)${c.reset}
689
+ ${colors.accent}--style, -s <style>${c.reset} Badge style ${c.dim}(default: gradient)${c.reset}
690
+ ${colors.accent}--verdict <v>${c.reset} Override: SHIP, WARN, BLOCK
691
+ ${colors.accent}--score <n>${c.reset} Override score (0-100)
692
+ ${colors.accent}--list, -l${c.reset} Show all available styles
693
+ ${colors.accent}--all, -a${c.reset} Generate all badge styles
91
694
 
92
- function generateHtmlBadge(verdict, score) {
93
- const color = verdict === "SHIP" ? "#4CAF50" : verdict === "WARN" ? "#FFC107" : "#F44336";
94
- return `<a href="https://vibecheckai.dev"><img src="https://img.shields.io/badge/vibecheck-${verdict}-${color.replace("#", "")}" alt="vibecheck ${verdict}"></a>`;
95
- }
695
+ ${c.bold}Badge Styles:${c.reset}
696
+ ${colors.accent}classic${c.reset} shields.io compatible
697
+ ${colors.accent}gradient${c.reset} Modern gradient with glow ${c.dim}(default)${c.reset}
698
+ ${colors.accent}cyberpunk${c.reset} Neon tech aesthetic
699
+ ${colors.accent}minimal${c.reset} Clean Apple-inspired
700
+ ${colors.accent}full${c.reset} Complete with score & stats
701
+ ${colors.accent}animated${c.reset} CSS animation for web
702
+ ${colors.accent}holographic${c.reset} Premium iridescent effect
96
703
 
97
- function generateSvgBadge(verdict, score) {
98
- const color = verdict === "SHIP" ? "#4CAF50" : verdict === "WARN" ? "#FFC107" : "#F44336";
99
- return `<svg xmlns="http://www.w3.org/2000/svg" width="100" height="20">
100
- <linearGradient id="b" x2="0" y2="100%">
101
- <stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
102
- <stop offset="1" stop-opacity=".1"/>
103
- </linearGradient>
104
- <mask id="a"><rect width="100" height="20" rx="3" fill="#fff"/></mask>
105
- <g mask="url(#a)">
106
- <rect width="60" height="20" fill="#555"/>
107
- <rect x="60" width="40" height="20" fill="${color}"/>
108
- <rect width="100" height="20" fill="url(#b)"/>
109
- </g>
110
- <g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
111
- <text x="30" y="15" fill="#010101" fill-opacity=".3">vibecheck</text>
112
- <text x="30" y="14">vibecheck</text>
113
- <text x="80" y="15" fill="#010101" fill-opacity=".3">${verdict}</text>
114
- <text x="80" y="14">${verdict}</text>
115
- </g>
116
- </svg>`;
704
+ ${c.bold}Examples:${c.reset}
705
+ ${c.dim}# Generate default gradient badge${c.reset}
706
+ vibecheck badge
707
+
708
+ ${c.dim}# Cyberpunk style badge${c.reset}
709
+ vibecheck badge --style cyberpunk
710
+
711
+ ${c.dim}# Add to README${c.reset}
712
+ vibecheck badge -o README.md
713
+
714
+ ${c.dim}# Force SHIP badge${c.reset}
715
+ vibecheck badge --verdict SHIP
716
+
717
+ ${c.dim}# SVG output${c.reset}
718
+ vibecheck badge --format svg -o badge.svg
719
+
720
+ ${c.dim}# Generate all styles${c.reset}
721
+ vibecheck badge --all
722
+ `);
117
723
  }
118
724
 
725
+ // ═══════════════════════════════════════════════════════════════════════════════
726
+ // MAIN BADGE FUNCTION
727
+ // ═══════════════════════════════════════════════════════════════════════════════
728
+
119
729
  async function runBadge(args) {
120
730
  const opts = parseArgs(args);
121
731
 
@@ -124,59 +734,156 @@ async function runBadge(args) {
124
734
  return 0;
125
735
  }
126
736
 
127
- // Try to load last scan result
128
- let verdict = opts.verdict || "SHIP";
737
+ if (opts.list) {
738
+ console.log(BANNER_FULL);
739
+ printStyleGallery();
740
+ return 0;
741
+ }
742
+
743
+ // Load last scan result
744
+ let verdict = opts.verdict || 'SHIP';
129
745
  let score = opts.score || 100;
746
+ let stats = { blockers: 0, warnings: 0 };
130
747
 
131
- const lastScanPath = path.join(process.cwd(), ".vibecheck", "last_scan.json");
132
- if (!opts.verdict && fs.existsSync(lastScanPath)) {
133
- try {
134
- const lastScan = JSON.parse(fs.readFileSync(lastScanPath, "utf8"));
135
- verdict = lastScan.verdict || "SHIP";
136
- score = lastScan.score || 100;
137
- } catch (e) {
138
- // Use defaults
748
+ const lastScanPath = path.join(process.cwd(), '.vibecheck', 'last_scan.json');
749
+ const lastShipPath = path.join(process.cwd(), '.vibecheck', 'last_ship.json');
750
+
751
+ if (!opts.verdict) {
752
+ // Try ship report first, then scan
753
+ const reportPath = fs.existsSync(lastShipPath) ? lastShipPath : lastScanPath;
754
+
755
+ if (fs.existsSync(reportPath)) {
756
+ try {
757
+ const report = JSON.parse(fs.readFileSync(reportPath, 'utf8'));
758
+ verdict = report.verdict || 'SHIP';
759
+ score = report.score ?? report.vibeScore ?? 100;
760
+ stats = {
761
+ blockers: report.counts?.BLOCK || report.findings?.filter(f => f.severity === 'BLOCK').length || 0,
762
+ warnings: report.counts?.WARN || report.findings?.filter(f => f.severity === 'WARN').length || 0,
763
+ };
764
+ } catch (e) {
765
+ // Use defaults
766
+ }
139
767
  }
140
768
  }
141
769
 
142
- // Generate badge
143
- let badge;
144
- switch (opts.format) {
145
- case "html":
146
- badge = generateHtmlBadge(verdict, score);
147
- break;
148
- case "svg":
149
- badge = generateSvgBadge(verdict, score);
150
- break;
151
- default:
152
- badge = generateMarkdownBadge(verdict, score);
153
- }
770
+ const projectName = path.basename(process.cwd());
771
+
772
+ // Print banner
773
+ console.log(BANNER_FULL);
774
+
775
+ console.log(` ${c.dim}Project:${c.reset} ${c.bold}${projectName}${c.reset}`);
776
+ console.log(` ${c.dim}Verdict:${c.reset} ${verdict === 'SHIP' ? colors.ship : verdict === 'WARN' ? colors.warn : colors.block}${verdict}${c.reset}`);
777
+ console.log(` ${c.dim}Score:${c.reset} ${score}%`);
778
+
779
+ // Generate badge(s)
780
+ if (opts.all) {
781
+ // Generate all styles
782
+ printSection('ALL BADGE STYLES', ICONS.sparkle);
783
+ console.log();
784
+
785
+ for (const [key, styleInfo] of Object.entries(BADGE_STYLES)) {
786
+ const badge = styleInfo.generator(verdict, score, stats);
787
+ console.log(` ${colors.accent}${c.bold}${styleInfo.name}${c.reset} ${c.dim}(--style ${key})${c.reset}`);
788
+ console.log();
789
+ console.log(` ${c.dim}Markdown:${c.reset}`);
790
+ console.log(` ${badge.markdown || badge}`);
791
+ console.log();
792
+ printDivider('─', 50);
793
+ console.log();
794
+ }
795
+ } else {
796
+ // Generate single style
797
+ const styleKey = opts.style || 'gradient';
798
+ const styleInfo = BADGE_STYLES[styleKey];
799
+
800
+ if (!styleInfo) {
801
+ console.log();
802
+ console.log(` ${colors.block}${ICONS.cross}${c.reset} Unknown style: ${styleKey}`);
803
+ console.log(` ${c.dim}Run 'vibecheck badge --list' to see available styles${c.reset}`);
804
+ return 1;
805
+ }
806
+
807
+ const badge = styleInfo.generator(verdict, score, stats);
808
+
809
+ // Preview
810
+ printBadgePreview(verdict, score, styleKey);
154
811
 
155
- // Output
156
- if (opts.output) {
157
- const outputPath = path.resolve(opts.output);
158
- if (fs.existsSync(outputPath)) {
159
- const content = fs.readFileSync(outputPath, "utf8");
160
- // Replace existing badge or append
161
- if (content.includes("vibecheck")) {
162
- const updated = content.replace(/\[!\[vibecheck\].*?\]\(https:\/\/vibecheckai\.dev\)/g, badge);
163
- fs.writeFileSync(outputPath, updated);
164
- console.log(`${c.green}✓${c.reset} Updated badge in ${outputPath}`);
812
+ // Output
813
+ printSection('GENERATED BADGE', ICONS.badge);
814
+ console.log();
815
+
816
+ if (opts.output) {
817
+ const outputPath = path.resolve(opts.output);
818
+ const content = opts.format === 'svg' ? badge.svg :
819
+ opts.format === 'html' ? badge.html :
820
+ badge.markdown || badge;
821
+
822
+ if (fs.existsSync(outputPath)) {
823
+ let existing = fs.readFileSync(outputPath, 'utf8');
824
+
825
+ // Replace existing badge or append
826
+ if (existing.includes('vibecheck')) {
827
+ const updated = existing.replace(/\[!\[vibecheck\].*?\]\(https:\/\/vibecheckai\.dev\)/g, content);
828
+ fs.writeFileSync(outputPath, updated);
829
+ console.log(` ${colors.success}${ICONS.check}${c.reset} Updated badge in ${c.bold}${outputPath}${c.reset}`);
830
+ } else {
831
+ fs.writeFileSync(outputPath, existing + '\n\n' + content + '\n');
832
+ console.log(` ${colors.success}${ICONS.check}${c.reset} Added badge to ${c.bold}${outputPath}${c.reset}`);
833
+ }
165
834
  } else {
166
- fs.writeFileSync(outputPath, content + "\n\n" + badge + "\n");
167
- console.log(`${c.green}✓${c.reset} Added badge to ${outputPath}`);
835
+ fs.writeFileSync(outputPath, content + '\n');
836
+ console.log(` ${colors.success}${ICONS.check}${c.reset} Created ${c.bold}${outputPath}${c.reset}`);
837
+ }
838
+
839
+ // Also save SVG to .vibecheck
840
+ const badgeDir = path.join(process.cwd(), '.vibecheck', 'badges');
841
+ if (!fs.existsSync(badgeDir)) {
842
+ fs.mkdirSync(badgeDir, { recursive: true });
843
+ }
844
+ if (badge.svg) {
845
+ fs.writeFileSync(path.join(badgeDir, `badge-${styleKey}.svg`), badge.svg);
846
+ console.log(` ${colors.success}${ICONS.check}${c.reset} Saved ${c.dim}.vibecheck/badges/badge-${styleKey}.svg${c.reset}`);
168
847
  }
169
848
  } else {
170
- fs.writeFileSync(outputPath, badge + "\n");
171
- console.log(`${c.green}✓${c.reset} Created ${outputPath} with badge`);
849
+ // Output to console
850
+ console.log(` ${c.dim}Format: ${opts.format}${c.reset}`);
851
+ console.log();
852
+
853
+ const output = opts.format === 'svg' ? badge.svg :
854
+ opts.format === 'html' ? badge.html :
855
+ badge.markdown || badge;
856
+
857
+ console.log(` ${colors.accent}${BOX.topLeft}${'─'.repeat(60)}${BOX.topRight}${c.reset}`);
858
+ console.log(` ${colors.accent}${BOX.vertical}${c.reset} ${ICONS.copy} ${c.bold}Copy this to your README:${c.reset}${' '.repeat(30)}${colors.accent}${BOX.vertical}${c.reset}`);
859
+ console.log(` ${colors.accent}${BOX.vertical}${c.reset}${' '.repeat(60)}${colors.accent}${BOX.vertical}${c.reset}`);
860
+
861
+ // Word wrap for long content
862
+ const maxWidth = 56;
863
+ if (output.length > maxWidth) {
864
+ const lines = [];
865
+ for (let i = 0; i < output.length; i += maxWidth) {
866
+ lines.push(output.slice(i, i + maxWidth));
867
+ }
868
+ for (const line of lines) {
869
+ console.log(` ${colors.accent}${BOX.vertical}${c.reset} ${c.dim}${line}${c.reset}${' '.repeat(Math.max(0, maxWidth - line.length + 2))}${colors.accent}${BOX.vertical}${c.reset}`);
870
+ }
871
+ } else {
872
+ console.log(` ${colors.accent}${BOX.vertical}${c.reset} ${c.dim}${output}${c.reset}${' '.repeat(Math.max(0, maxWidth - output.length + 2))}${colors.accent}${BOX.vertical}${c.reset}`);
873
+ }
874
+
875
+ console.log(` ${colors.accent}${BOX.vertical}${c.reset}${' '.repeat(60)}${colors.accent}${BOX.vertical}${c.reset}`);
876
+ console.log(` ${colors.accent}${BOX.bottomLeft}${'─'.repeat(60)}${BOX.bottomRight}${c.reset}`);
172
877
  }
173
- } else {
174
- console.log(`\n${c.bold}Generated Badge:${c.reset}\n`);
175
- console.log(badge);
176
- console.log(`\n${c.dim}Copy and paste into your README.md${c.reset}\n`);
177
878
  }
178
879
 
880
+ // Tips
881
+ console.log();
882
+ console.log(` ${c.dim}Tip: Use ${colors.accent}--style holographic${c.reset}${c.dim} for the premium animated effect${c.reset}`);
883
+ console.log(` ${c.dim} Use ${colors.accent}--all${c.reset}${c.dim} to see all available styles${c.reset}`);
884
+ console.log();
885
+
179
886
  return 0;
180
887
  }
181
888
 
182
- module.exports = { runBadge };
889
+ module.exports = { runBadge }