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