@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.
- package/README.md +77 -484
- package/bin/runners/lib/__tests__/entitlements-v2.test.js +295 -0
- package/bin/runners/lib/entitlements-v2.js +409 -299
- package/bin/runners/lib/report-html.js +378 -1
- package/bin/runners/runBadge.js +823 -116
- package/bin/runners/runCtx.js +602 -119
- package/bin/runners/runDoctor.js +329 -42
- package/bin/runners/runFix.js +562 -83
- package/bin/runners/runGraph.js +231 -74
- package/bin/runners/runInit.js +647 -88
- package/bin/runners/runInstall.js +207 -46
- package/bin/runners/runMcp.js +58 -0
- package/bin/runners/runPR.js +172 -13
- package/bin/runners/runProve.js +818 -97
- package/bin/runners/runReality.js +831 -65
- package/bin/runners/runReport.js +108 -2
- package/bin/runners/runShare.js +156 -38
- package/bin/runners/runShip.js +919 -792
- package/bin/runners/runWatch.js +215 -38
- package/bin/vibecheck.js +158 -59
- package/mcp-server/package.json +1 -1
- package/package.json +1 -1
package/bin/runners/runCtx.js
CHANGED
|
@@ -1,20 +1,577 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* vibecheck ctx - Truth Pack Generator
|
|
3
|
+
*
|
|
4
|
+
* ═══════════════════════════════════════════════════════════════════════════════
|
|
5
|
+
* ENTERPRISE EDITION - World-Class Terminal Experience
|
|
6
|
+
* ═══════════════════════════════════════════════════════════════════════════════
|
|
7
|
+
*
|
|
8
|
+
* Build a comprehensive truth pack of your codebase for AI agents.
|
|
9
|
+
* The truth pack is the ground truth that powers all vibecheck analysis.
|
|
10
|
+
*/
|
|
11
|
+
|
|
2
12
|
const path = require("path");
|
|
3
13
|
const fs = require("fs");
|
|
4
14
|
const { buildTruthpack, writeTruthpack, detectFastifyEntry } = require("./lib/truth");
|
|
5
15
|
|
|
16
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
17
|
+
// ADVANCED TERMINAL - ANSI CODES & UTILITIES
|
|
18
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
19
|
+
|
|
6
20
|
const c = {
|
|
7
21
|
reset: '\x1b[0m',
|
|
8
22
|
bold: '\x1b[1m',
|
|
9
23
|
dim: '\x1b[2m',
|
|
24
|
+
italic: '\x1b[3m',
|
|
25
|
+
underline: '\x1b[4m',
|
|
26
|
+
blink: '\x1b[5m',
|
|
27
|
+
inverse: '\x1b[7m',
|
|
28
|
+
hidden: '\x1b[8m',
|
|
29
|
+
strike: '\x1b[9m',
|
|
30
|
+
// Colors
|
|
31
|
+
black: '\x1b[30m',
|
|
10
32
|
red: '\x1b[31m',
|
|
11
33
|
green: '\x1b[32m',
|
|
12
34
|
yellow: '\x1b[33m',
|
|
13
35
|
blue: '\x1b[34m',
|
|
14
|
-
cyan: '\x1b[36m',
|
|
15
36
|
magenta: '\x1b[35m',
|
|
37
|
+
cyan: '\x1b[36m',
|
|
38
|
+
white: '\x1b[37m',
|
|
39
|
+
// Bright colors
|
|
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
|
+
// Background
|
|
49
|
+
bgBlack: '\x1b[40m',
|
|
50
|
+
bgRed: '\x1b[41m',
|
|
51
|
+
bgGreen: '\x1b[42m',
|
|
52
|
+
bgYellow: '\x1b[43m',
|
|
53
|
+
bgBlue: '\x1b[44m',
|
|
54
|
+
bgMagenta: '\x1b[45m',
|
|
55
|
+
bgCyan: '\x1b[46m',
|
|
56
|
+
bgWhite: '\x1b[47m',
|
|
57
|
+
// Cursor control
|
|
58
|
+
cursorUp: (n = 1) => `\x1b[${n}A`,
|
|
59
|
+
cursorDown: (n = 1) => `\x1b[${n}B`,
|
|
60
|
+
cursorRight: (n = 1) => `\x1b[${n}C`,
|
|
61
|
+
cursorLeft: (n = 1) => `\x1b[${n}D`,
|
|
62
|
+
clearLine: '\x1b[2K',
|
|
63
|
+
clearScreen: '\x1b[2J',
|
|
64
|
+
saveCursor: '\x1b[s',
|
|
65
|
+
restoreCursor: '\x1b[u',
|
|
66
|
+
hideCursor: '\x1b[?25l',
|
|
67
|
+
showCursor: '\x1b[?25h',
|
|
16
68
|
};
|
|
17
69
|
|
|
70
|
+
// True color support
|
|
71
|
+
const rgb = (r, g, b) => `\x1b[38;2;${r};${g};${b}m`;
|
|
72
|
+
const bgRgb = (r, g, b) => `\x1b[48;2;${r};${g};${b}m`;
|
|
73
|
+
|
|
74
|
+
// Premium color palette (blue/teal theme for "truth/context")
|
|
75
|
+
const colors = {
|
|
76
|
+
// Gradient for banner
|
|
77
|
+
gradient1: rgb(0, 200, 255), // Bright cyan
|
|
78
|
+
gradient2: rgb(0, 180, 240), // Cyan
|
|
79
|
+
gradient3: rgb(0, 160, 220), // Cyan-blue
|
|
80
|
+
gradient4: rgb(0, 140, 200), // Blue-cyan
|
|
81
|
+
gradient5: rgb(0, 120, 180), // Blue
|
|
82
|
+
gradient6: rgb(0, 100, 160), // Deep blue
|
|
83
|
+
|
|
84
|
+
// Category colors
|
|
85
|
+
routes: rgb(100, 200, 255), // Light blue
|
|
86
|
+
env: rgb(200, 150, 255), // Purple
|
|
87
|
+
auth: rgb(100, 180, 255), // Blue
|
|
88
|
+
billing: rgb(255, 200, 100), // Gold
|
|
89
|
+
enforcement: rgb(255, 150, 100), // Orange
|
|
90
|
+
|
|
91
|
+
// Status colors
|
|
92
|
+
success: rgb(0, 255, 150),
|
|
93
|
+
warning: rgb(255, 200, 0),
|
|
94
|
+
error: rgb(255, 80, 80),
|
|
95
|
+
info: rgb(100, 200, 255),
|
|
96
|
+
|
|
97
|
+
// UI colors
|
|
98
|
+
accent: rgb(0, 200, 255),
|
|
99
|
+
muted: rgb(120, 140, 160),
|
|
100
|
+
subtle: rgb(80, 100, 120),
|
|
101
|
+
highlight: rgb(255, 255, 255),
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
105
|
+
// PREMIUM BANNER
|
|
106
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
107
|
+
|
|
108
|
+
const CTX_BANNER = `
|
|
109
|
+
${rgb(0, 220, 255)} ██████╗████████╗██╗ ██╗${c.reset}
|
|
110
|
+
${rgb(0, 200, 255)} ██╔════╝╚══██╔══╝╚██╗██╔╝${c.reset}
|
|
111
|
+
${rgb(0, 180, 240)} ██║ ██║ ╚███╔╝ ${c.reset}
|
|
112
|
+
${rgb(0, 160, 220)} ██║ ██║ ██╔██╗ ${c.reset}
|
|
113
|
+
${rgb(0, 140, 200)} ╚██████╗ ██║ ██╔╝ ██╗${c.reset}
|
|
114
|
+
${rgb(0, 120, 180)} ╚═════╝ ╚═╝ ╚═╝ ╚═╝${c.reset}
|
|
115
|
+
`;
|
|
116
|
+
|
|
117
|
+
const BANNER_FULL = `
|
|
118
|
+
${rgb(0, 220, 255)} ██╗ ██╗██╗██████╗ ███████╗ ██████╗██╗ ██╗███████╗ ██████╗██╗ ██╗${c.reset}
|
|
119
|
+
${rgb(0, 200, 255)} ██║ ██║██║██╔══██╗██╔════╝██╔════╝██║ ██║██╔════╝██╔════╝██║ ██╔╝${c.reset}
|
|
120
|
+
${rgb(0, 180, 240)} ██║ ██║██║██████╔╝█████╗ ██║ ███████║█████╗ ██║ █████╔╝ ${c.reset}
|
|
121
|
+
${rgb(0, 160, 220)} ╚██╗ ██╔╝██║██╔══██╗██╔══╝ ██║ ██╔══██║██╔══╝ ██║ ██╔═██╗ ${c.reset}
|
|
122
|
+
${rgb(0, 140, 200)} ╚████╔╝ ██║██████╔╝███████╗╚██████╗██║ ██║███████╗╚██████╗██║ ██╗${c.reset}
|
|
123
|
+
${rgb(0, 120, 180)} ╚═══╝ ╚═╝╚═════╝ ╚══════╝ ╚═════╝╚═╝ ╚═╝╚══════╝ ╚═════╝╚═╝ ╚═╝${c.reset}
|
|
124
|
+
|
|
125
|
+
${c.dim} ┌─────────────────────────────────────────────────────────────────────┐${c.reset}
|
|
126
|
+
${c.dim} │${c.reset} ${rgb(0, 200, 255)}📦${c.reset} ${c.bold}CTX${c.reset} ${c.dim}•${c.reset} ${rgb(200, 200, 200)}Truth Pack Generator${c.reset} ${c.dim}•${c.reset} ${rgb(150, 150, 150)}Ground Truth for AI${c.reset} ${c.dim}│${c.reset}
|
|
127
|
+
${c.dim} └─────────────────────────────────────────────────────────────────────┘${c.reset}
|
|
128
|
+
`;
|
|
129
|
+
|
|
130
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
131
|
+
// ICONS & SYMBOLS
|
|
132
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
133
|
+
|
|
134
|
+
const ICONS = {
|
|
135
|
+
// Main
|
|
136
|
+
package: '📦',
|
|
137
|
+
truth: '🎯',
|
|
138
|
+
context: '📋',
|
|
139
|
+
|
|
140
|
+
// Status
|
|
141
|
+
check: '✓',
|
|
142
|
+
cross: '✗',
|
|
143
|
+
warning: '⚠',
|
|
144
|
+
info: 'ℹ',
|
|
145
|
+
arrow: '→',
|
|
146
|
+
bullet: '•',
|
|
147
|
+
|
|
148
|
+
// Categories
|
|
149
|
+
routes: '🛤️',
|
|
150
|
+
env: '🌍',
|
|
151
|
+
auth: '🔒',
|
|
152
|
+
billing: '💰',
|
|
153
|
+
enforcement: '🛡️',
|
|
154
|
+
|
|
155
|
+
// Frameworks
|
|
156
|
+
nextjs: '▲',
|
|
157
|
+
fastify: '⚡',
|
|
158
|
+
express: '🚂',
|
|
159
|
+
|
|
160
|
+
// Objects
|
|
161
|
+
file: '📄',
|
|
162
|
+
folder: '📁',
|
|
163
|
+
snapshot: '📸',
|
|
164
|
+
clock: '⏱',
|
|
165
|
+
lightning: '⚡',
|
|
166
|
+
sparkle: '✨',
|
|
167
|
+
link: '🔗',
|
|
168
|
+
gap: '🕳️',
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
172
|
+
// BOX DRAWING
|
|
173
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
174
|
+
|
|
175
|
+
const BOX = {
|
|
176
|
+
topLeft: '╭', topRight: '╮', bottomLeft: '╰', bottomRight: '╯',
|
|
177
|
+
horizontal: '─', vertical: '│',
|
|
178
|
+
teeRight: '├', teeLeft: '┤', teeDown: '┬', teeUp: '┴',
|
|
179
|
+
cross: '┼',
|
|
180
|
+
// Double line
|
|
181
|
+
dTopLeft: '╔', dTopRight: '╗', dBottomLeft: '╚', dBottomRight: '╝',
|
|
182
|
+
dHorizontal: '═', dVertical: '║',
|
|
183
|
+
// Rounded
|
|
184
|
+
rTopLeft: '╭', rTopRight: '╮', rBottomLeft: '╰', rBottomRight: '╯',
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
188
|
+
// SPINNER & PROGRESS
|
|
189
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
190
|
+
|
|
191
|
+
const SPINNER_FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
192
|
+
const SPINNER_DOTS = ['⣾', '⣽', '⣻', '⢿', '⡿', '⣟', '⣯', '⣷'];
|
|
193
|
+
const SPINNER_BLOCKS = ['▁', '▂', '▃', '▄', '▅', '▆', '▇', '█', '▇', '▆', '▅', '▄', '▃', '▂'];
|
|
194
|
+
|
|
195
|
+
let spinnerIndex = 0;
|
|
196
|
+
let spinnerInterval = null;
|
|
197
|
+
let spinnerStartTime = null;
|
|
198
|
+
|
|
199
|
+
function formatDuration(ms) {
|
|
200
|
+
if (ms < 1000) return `${ms}ms`;
|
|
201
|
+
if (ms < 60000) return `${(ms / 1000).toFixed(1)}s`;
|
|
202
|
+
const mins = Math.floor(ms / 60000);
|
|
203
|
+
const secs = Math.floor((ms % 60000) / 1000);
|
|
204
|
+
return `${mins}m ${secs}s`;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function formatNumber(num) {
|
|
208
|
+
return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function truncate(str, len) {
|
|
212
|
+
if (!str) return '';
|
|
213
|
+
if (str.length <= len) return str;
|
|
214
|
+
return str.slice(0, len - 3) + '...';
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function padCenter(str, width) {
|
|
218
|
+
const padding = Math.max(0, width - str.length);
|
|
219
|
+
const left = Math.floor(padding / 2);
|
|
220
|
+
const right = padding - left;
|
|
221
|
+
return ' '.repeat(left) + str + ' '.repeat(right);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function progressBar(percent, width = 30, opts = {}) {
|
|
225
|
+
const filled = Math.round((percent / 100) * width);
|
|
226
|
+
const empty = width - filled;
|
|
227
|
+
|
|
228
|
+
let filledColor = opts.color || colors.accent;
|
|
229
|
+
if (!opts.color) {
|
|
230
|
+
if (percent >= 80) filledColor = colors.success;
|
|
231
|
+
else if (percent >= 50) filledColor = colors.warning;
|
|
232
|
+
else filledColor = colors.error;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const filledChar = opts.filled || '█';
|
|
236
|
+
const emptyChar = opts.empty || '░';
|
|
237
|
+
|
|
238
|
+
return `${filledColor}${filledChar.repeat(filled)}${c.dim}${emptyChar.repeat(empty)}${c.reset}`;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function startSpinner(message, color = colors.accent) {
|
|
242
|
+
spinnerStartTime = Date.now();
|
|
243
|
+
process.stdout.write(c.hideCursor);
|
|
244
|
+
|
|
245
|
+
spinnerInterval = setInterval(() => {
|
|
246
|
+
const elapsed = formatDuration(Date.now() - spinnerStartTime);
|
|
247
|
+
process.stdout.write(`\r${c.clearLine} ${color}${SPINNER_DOTS[spinnerIndex]}${c.reset} ${message} ${c.dim}${elapsed}${c.reset}`);
|
|
248
|
+
spinnerIndex = (spinnerIndex + 1) % SPINNER_DOTS.length;
|
|
249
|
+
}, 80);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
function stopSpinner(message, success = true) {
|
|
253
|
+
if (spinnerInterval) {
|
|
254
|
+
clearInterval(spinnerInterval);
|
|
255
|
+
spinnerInterval = null;
|
|
256
|
+
}
|
|
257
|
+
const elapsed = spinnerStartTime ? formatDuration(Date.now() - spinnerStartTime) : '';
|
|
258
|
+
const icon = success ? `${colors.success}${ICONS.check}${c.reset}` : `${colors.error}${ICONS.cross}${c.reset}`;
|
|
259
|
+
process.stdout.write(`\r${c.clearLine} ${icon} ${message} ${c.dim}${elapsed}${c.reset}\n`);
|
|
260
|
+
process.stdout.write(c.showCursor);
|
|
261
|
+
spinnerStartTime = null;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
265
|
+
// SECTION HEADERS
|
|
266
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
267
|
+
|
|
268
|
+
function printBanner() {
|
|
269
|
+
console.log(BANNER_FULL);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
function printCompactBanner() {
|
|
273
|
+
console.log(CTX_BANNER);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
function printDivider(char = '─', width = 69, color = c.dim) {
|
|
277
|
+
console.log(`${color} ${char.repeat(width)}${c.reset}`);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
function printSection(title, icon = '◆') {
|
|
281
|
+
console.log();
|
|
282
|
+
console.log(` ${colors.accent}${icon}${c.reset} ${c.bold}${title}${c.reset}`);
|
|
283
|
+
printDivider();
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
287
|
+
// CATEGORY BOXES - Premium Truth Pack Display
|
|
288
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
289
|
+
|
|
290
|
+
function printCategoryBox(title, icon, color, items) {
|
|
291
|
+
const w = 68;
|
|
292
|
+
|
|
293
|
+
console.log(` ${color}${BOX.topLeft}${BOX.horizontal} ${icon} ${title} ${BOX.horizontal.repeat(w - title.length - 6)}${BOX.topRight}${c.reset}`);
|
|
294
|
+
|
|
295
|
+
for (const item of items) {
|
|
296
|
+
let line = ` ${color}${BOX.vertical}${c.reset} `;
|
|
297
|
+
|
|
298
|
+
if (item.label && item.value !== undefined) {
|
|
299
|
+
const valueStr = typeof item.value === 'number' ? formatNumber(item.value) : item.value;
|
|
300
|
+
const labelPad = 18;
|
|
301
|
+
line += `${c.bold}${item.label.padEnd(labelPad)}${c.reset} ${valueStr}`;
|
|
302
|
+
|
|
303
|
+
if (item.extra) {
|
|
304
|
+
line += ` ${c.dim}${item.extra}${c.reset}`;
|
|
305
|
+
}
|
|
306
|
+
if (item.status) {
|
|
307
|
+
const statusColor = item.status === 'success' ? colors.success :
|
|
308
|
+
item.status === 'warning' ? colors.warning :
|
|
309
|
+
item.status === 'error' ? colors.error : colors.muted;
|
|
310
|
+
const statusIcon = item.status === 'success' ? ICONS.check :
|
|
311
|
+
item.status === 'warning' ? ICONS.warning :
|
|
312
|
+
item.status === 'error' ? ICONS.cross : ICONS.bullet;
|
|
313
|
+
line += ` ${statusColor}${statusIcon}${c.reset}`;
|
|
314
|
+
if (item.statusText) {
|
|
315
|
+
line += ` ${statusColor}${item.statusText}${c.reset}`;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
} else if (item.text) {
|
|
319
|
+
line += item.text;
|
|
320
|
+
} else if (item.divider) {
|
|
321
|
+
line = ` ${color}${BOX.teeRight}${BOX.horizontal.repeat(w)}${BOX.teeLeft}${c.reset}`;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
console.log(line);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
console.log(` ${color}${BOX.bottomLeft}${BOX.horizontal.repeat(w)}${BOX.bottomRight}${c.reset}`);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
331
|
+
// TRUTH PACK SUMMARY DISPLAY
|
|
332
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
333
|
+
|
|
334
|
+
function printTruthpackSummary(truthpack, root) {
|
|
335
|
+
// Calculate stats
|
|
336
|
+
const nextRoutes = (truthpack.routes?.server || []).filter(r =>
|
|
337
|
+
r.handler?.includes("app/api") || r.handler?.includes("pages/api")
|
|
338
|
+
).length;
|
|
339
|
+
const otherRoutes = (truthpack.routes?.server || []).filter(r =>
|
|
340
|
+
!r.handler?.includes("app/api") && !r.handler?.includes("pages/api")
|
|
341
|
+
).length;
|
|
342
|
+
|
|
343
|
+
const detectedFramework = truthpack.meta?.framework ||
|
|
344
|
+
(truthpack.routes?.server || []).find(r => r.evidence?.[0]?.reason)?.evidence?.[0]?.reason?.split(' ')[0] ||
|
|
345
|
+
'Express';
|
|
346
|
+
|
|
347
|
+
const clientRefs = truthpack.routes?.clientRefs?.length || 0;
|
|
348
|
+
const gaps = truthpack.routes?.gaps?.length || 0;
|
|
349
|
+
const totalRoutes = (truthpack.routes?.server || []).length;
|
|
350
|
+
|
|
351
|
+
const envUsed = truthpack.env?.vars?.length || 0;
|
|
352
|
+
const envDeclared = truthpack.env?.declared?.length || 0;
|
|
353
|
+
const envSources = (truthpack.env?.declaredSources || []).join(", ") || "none";
|
|
354
|
+
const envCoverage = envUsed > 0 ? Math.round((envDeclared / envUsed) * 100) : 100;
|
|
355
|
+
|
|
356
|
+
const nextMiddleware = truthpack.auth?.nextMiddleware?.length || 0;
|
|
357
|
+
const fastifySignals = truthpack.auth?.fastify?.signalTypes?.length || 0;
|
|
358
|
+
const authPatterns = truthpack.auth?.patterns?.length || 0;
|
|
359
|
+
|
|
360
|
+
const hasStripe = truthpack.billing?.hasStripe;
|
|
361
|
+
const webhooks = truthpack.billing?.summary?.webhookHandlersFound || 0;
|
|
362
|
+
const stripeUsage = truthpack.billing?.stripeUsage?.length || 0;
|
|
363
|
+
|
|
364
|
+
const enforced = truthpack.enforcement?.enforcedCount || 0;
|
|
365
|
+
const checked = truthpack.enforcement?.checkedCount || 0;
|
|
366
|
+
const enforcementRate = checked > 0 ? Math.round((enforced / checked) * 100) : 100;
|
|
367
|
+
|
|
368
|
+
// Routes Box
|
|
369
|
+
console.log();
|
|
370
|
+
printCategoryBox('ROUTES', ICONS.routes, colors.routes, [
|
|
371
|
+
{
|
|
372
|
+
label: `${ICONS.nextjs} Next.js:`,
|
|
373
|
+
value: nextRoutes,
|
|
374
|
+
extra: 'API routes'
|
|
375
|
+
},
|
|
376
|
+
{
|
|
377
|
+
label: `${ICONS.fastify} ${detectedFramework}:`,
|
|
378
|
+
value: otherRoutes,
|
|
379
|
+
extra: 'server routes'
|
|
380
|
+
},
|
|
381
|
+
{ divider: true },
|
|
382
|
+
{
|
|
383
|
+
label: 'Client refs:',
|
|
384
|
+
value: clientRefs,
|
|
385
|
+
status: gaps > 0 ? 'warning' : 'success',
|
|
386
|
+
statusText: gaps > 0 ? `${gaps} gaps` : 'no gaps'
|
|
387
|
+
},
|
|
388
|
+
{
|
|
389
|
+
label: 'Total:',
|
|
390
|
+
value: totalRoutes,
|
|
391
|
+
extra: 'routes mapped'
|
|
392
|
+
},
|
|
393
|
+
]);
|
|
394
|
+
|
|
395
|
+
// Environment Box
|
|
396
|
+
printCategoryBox('ENVIRONMENT', ICONS.env, colors.env, [
|
|
397
|
+
{
|
|
398
|
+
label: 'Used:',
|
|
399
|
+
value: envUsed,
|
|
400
|
+
extra: 'env vars'
|
|
401
|
+
},
|
|
402
|
+
{
|
|
403
|
+
label: 'Declared:',
|
|
404
|
+
value: envDeclared,
|
|
405
|
+
extra: `(${envSources})`
|
|
406
|
+
},
|
|
407
|
+
{ divider: true },
|
|
408
|
+
{
|
|
409
|
+
label: 'Coverage:',
|
|
410
|
+
value: `${envCoverage}%`,
|
|
411
|
+
status: envCoverage >= 100 ? 'success' : envCoverage >= 80 ? 'warning' : 'error',
|
|
412
|
+
statusText: envCoverage >= 100 ? 'complete' : 'missing declarations'
|
|
413
|
+
},
|
|
414
|
+
]);
|
|
415
|
+
|
|
416
|
+
// Auth Box
|
|
417
|
+
printCategoryBox('AUTH', ICONS.auth, colors.auth, [
|
|
418
|
+
{
|
|
419
|
+
label: 'Next middleware:',
|
|
420
|
+
value: nextMiddleware,
|
|
421
|
+
extra: 'handlers'
|
|
422
|
+
},
|
|
423
|
+
{
|
|
424
|
+
label: 'Fastify signals:',
|
|
425
|
+
value: fastifySignals,
|
|
426
|
+
extra: 'types'
|
|
427
|
+
},
|
|
428
|
+
{
|
|
429
|
+
label: 'Auth patterns:',
|
|
430
|
+
value: authPatterns,
|
|
431
|
+
extra: 'detected'
|
|
432
|
+
},
|
|
433
|
+
]);
|
|
434
|
+
|
|
435
|
+
// Billing Box
|
|
436
|
+
printCategoryBox('BILLING', ICONS.billing, colors.billing, [
|
|
437
|
+
{
|
|
438
|
+
label: 'Stripe:',
|
|
439
|
+
value: hasStripe ? 'detected' : 'not detected',
|
|
440
|
+
status: hasStripe ? 'success' : null,
|
|
441
|
+
},
|
|
442
|
+
{
|
|
443
|
+
label: 'Webhooks:',
|
|
444
|
+
value: webhooks,
|
|
445
|
+
extra: 'handlers'
|
|
446
|
+
},
|
|
447
|
+
{
|
|
448
|
+
label: 'Stripe usage:',
|
|
449
|
+
value: stripeUsage,
|
|
450
|
+
extra: 'call sites'
|
|
451
|
+
},
|
|
452
|
+
]);
|
|
453
|
+
|
|
454
|
+
// Enforcement Box
|
|
455
|
+
printCategoryBox('ENFORCEMENT', ICONS.enforcement, colors.enforcement, [
|
|
456
|
+
{
|
|
457
|
+
label: 'Paid routes:',
|
|
458
|
+
value: `${enforced}/${checked}`,
|
|
459
|
+
extra: 'enforced'
|
|
460
|
+
},
|
|
461
|
+
{
|
|
462
|
+
label: 'Coverage:',
|
|
463
|
+
value: `${enforcementRate}%`,
|
|
464
|
+
status: enforcementRate >= 100 ? 'success' : enforcementRate >= 80 ? 'warning' : 'error',
|
|
465
|
+
},
|
|
466
|
+
]);
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
470
|
+
// SUCCESS CARD
|
|
471
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
472
|
+
|
|
473
|
+
function printSuccessCard(outputPath, snapshotPath = null, fastifyEntry = null, duration = null) {
|
|
474
|
+
const w = 68;
|
|
475
|
+
|
|
476
|
+
console.log();
|
|
477
|
+
console.log(` ${colors.success}${BOX.dTopLeft}${BOX.dHorizontal.repeat(w)}${BOX.dTopRight}${c.reset}`);
|
|
478
|
+
console.log(` ${colors.success}${BOX.dVertical}${c.reset}${' '.repeat(w)}${colors.success}${BOX.dVertical}${c.reset}`);
|
|
479
|
+
|
|
480
|
+
const headline = `${ICONS.sparkle} TRUTH PACK GENERATED`;
|
|
481
|
+
const headlinePadded = padCenter(headline, w);
|
|
482
|
+
console.log(` ${colors.success}${BOX.dVertical}${c.reset}${colors.success}${c.bold}${headlinePadded}${c.reset}${colors.success}${BOX.dVertical}${c.reset}`);
|
|
483
|
+
|
|
484
|
+
console.log(` ${colors.success}${BOX.dVertical}${c.reset}${' '.repeat(w)}${colors.success}${BOX.dVertical}${c.reset}`);
|
|
485
|
+
|
|
486
|
+
// Output path
|
|
487
|
+
const outputLabel = `${ICONS.arrow} `;
|
|
488
|
+
const outputValue = truncate(outputPath, 55);
|
|
489
|
+
const outputLine = ` ${outputLabel}${outputValue}`;
|
|
490
|
+
const outputPadded = outputLine + ' '.repeat(Math.max(0, w - outputLine.length + 5));
|
|
491
|
+
console.log(` ${colors.success}${BOX.dVertical}${c.reset}${outputPadded}${colors.success}${BOX.dVertical}${c.reset}`);
|
|
492
|
+
|
|
493
|
+
// Snapshot path (if any)
|
|
494
|
+
if (snapshotPath) {
|
|
495
|
+
const snapLabel = `${ICONS.snapshot} `;
|
|
496
|
+
const snapValue = truncate(snapshotPath, 55);
|
|
497
|
+
const snapLine = ` ${snapLabel}${snapValue}`;
|
|
498
|
+
const snapPadded = snapLine + ' '.repeat(Math.max(0, w - snapLine.length + 5));
|
|
499
|
+
console.log(` ${colors.success}${BOX.dVertical}${c.reset}${snapPadded}${colors.success}${BOX.dVertical}${c.reset}`);
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
// Fastify entry (if detected)
|
|
503
|
+
if (fastifyEntry) {
|
|
504
|
+
const entryLabel = `${ICONS.fastify} Fastify: `;
|
|
505
|
+
const entryValue = truncate(fastifyEntry, 45);
|
|
506
|
+
const entryLine = ` ${entryLabel}${c.dim}${entryValue}${c.reset}`;
|
|
507
|
+
// Need to account for ANSI codes in padding
|
|
508
|
+
const visibleLength = ` ${ICONS.fastify} Fastify: ${entryValue}`.length;
|
|
509
|
+
const entryPadded = entryLine + ' '.repeat(Math.max(0, w - visibleLength));
|
|
510
|
+
console.log(` ${colors.success}${BOX.dVertical}${c.reset}${entryPadded}${colors.success}${BOX.dVertical}${c.reset}`);
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
// Duration (if provided)
|
|
514
|
+
if (duration) {
|
|
515
|
+
const durLabel = `${ICONS.clock} `;
|
|
516
|
+
const durValue = formatDuration(duration);
|
|
517
|
+
const durLine = ` ${durLabel}${durValue}`;
|
|
518
|
+
const durPadded = durLine + ' '.repeat(Math.max(0, w - durLine.length + 5));
|
|
519
|
+
console.log(` ${colors.success}${BOX.dVertical}${c.reset}${c.dim}${durPadded}${c.reset}${colors.success}${BOX.dVertical}${c.reset}`);
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
console.log(` ${colors.success}${BOX.dVertical}${c.reset}${' '.repeat(w)}${colors.success}${BOX.dVertical}${c.reset}`);
|
|
523
|
+
console.log(` ${colors.success}${BOX.dBottomLeft}${BOX.dHorizontal.repeat(w)}${BOX.dBottomRight}${c.reset}`);
|
|
524
|
+
console.log();
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
528
|
+
// HELP DISPLAY
|
|
529
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
530
|
+
|
|
531
|
+
function printHelp() {
|
|
532
|
+
console.log(BANNER_FULL);
|
|
533
|
+
console.log(`
|
|
534
|
+
${c.bold}Usage:${c.reset} vibecheck ctx [options]
|
|
535
|
+
|
|
536
|
+
${c.bold}Truth Pack Generator${c.reset} — Build ground truth for AI agents.
|
|
537
|
+
|
|
538
|
+
${c.bold}What It Generates:${c.reset}
|
|
539
|
+
${colors.routes}${ICONS.routes} routes${c.reset} Server routes + client refs + gaps
|
|
540
|
+
${colors.env}${ICONS.env} env${c.reset} Used vars + declared vars + sources
|
|
541
|
+
${colors.auth}${ICONS.auth} auth${c.reset} Middleware + patterns + signals
|
|
542
|
+
${colors.billing}${ICONS.billing} billing${c.reset} Stripe usage + webhooks + gates
|
|
543
|
+
${colors.enforcement}${ICONS.enforcement} enforcement${c.reset} Paid surface checks
|
|
544
|
+
|
|
545
|
+
${c.bold}Options:${c.reset}
|
|
546
|
+
${colors.accent}--json, -j${c.reset} Output raw JSON
|
|
547
|
+
${colors.accent}--snapshot, -s${c.reset} Save timestamped snapshot
|
|
548
|
+
${colors.accent}--fastify-entry${c.reset} Fastify entry file ${c.dim}(e.g. src/server.ts)${c.reset}
|
|
549
|
+
${colors.accent}--path, -p${c.reset} Project path ${c.dim}(default: current directory)${c.reset}
|
|
550
|
+
${colors.accent}--help, -h${c.reset} Show this help
|
|
551
|
+
|
|
552
|
+
${c.bold}Output:${c.reset}
|
|
553
|
+
${c.dim}.vibecheck/truth/truthpack.json${c.reset} Main truth pack
|
|
554
|
+
${c.dim}.vibecheck/truth/truthpack.md${c.reset} Human-readable summary
|
|
555
|
+
|
|
556
|
+
${c.bold}Examples:${c.reset}
|
|
557
|
+
${c.dim}# Generate truth pack${c.reset}
|
|
558
|
+
vibecheck ctx
|
|
559
|
+
|
|
560
|
+
${c.dim}# Save timestamped snapshot${c.reset}
|
|
561
|
+
vibecheck ctx --snapshot
|
|
562
|
+
|
|
563
|
+
${c.dim}# Export as JSON${c.reset}
|
|
564
|
+
vibecheck ctx --json > truthpack.json
|
|
565
|
+
|
|
566
|
+
${c.dim}# Specify fastify entry${c.reset}
|
|
567
|
+
vibecheck ctx --fastify-entry src/server.ts
|
|
568
|
+
`);
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
572
|
+
// ARGS PARSER
|
|
573
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
574
|
+
|
|
18
575
|
function parseArgs(args) {
|
|
19
576
|
const opts = {
|
|
20
577
|
path: process.cwd(),
|
|
@@ -22,12 +579,14 @@ function parseArgs(args) {
|
|
|
22
579
|
json: false,
|
|
23
580
|
snapshot: false,
|
|
24
581
|
help: false,
|
|
582
|
+
verbose: false,
|
|
25
583
|
};
|
|
26
584
|
|
|
27
585
|
for (let i = 0; i < args.length; i++) {
|
|
28
586
|
const arg = args[i];
|
|
29
587
|
if (arg === '--json' || arg === '-j') opts.json = true;
|
|
30
588
|
else if (arg === '--snapshot' || arg === '-s') opts.snapshot = true;
|
|
589
|
+
else if (arg === '--verbose' || arg === '-v') opts.verbose = true;
|
|
31
590
|
else if (arg === '--fastify-entry') opts.fastifyEntry = args[++i];
|
|
32
591
|
else if (arg === '--path' || arg === '-p') opts.path = args[++i];
|
|
33
592
|
else if (arg === '--help' || arg === '-h') opts.help = true;
|
|
@@ -36,41 +595,9 @@ function parseArgs(args) {
|
|
|
36
595
|
return opts;
|
|
37
596
|
}
|
|
38
597
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
${c.dim}Build a comprehensive truth pack of your codebase for AI agents.${c.reset}
|
|
44
|
-
|
|
45
|
-
${c.bold}USAGE${c.reset}
|
|
46
|
-
vibecheck ctx Generate truth pack
|
|
47
|
-
vibecheck ctx --snapshot Save timestamped snapshot
|
|
48
|
-
vibecheck ctx --json Output as JSON
|
|
49
|
-
|
|
50
|
-
${c.bold}OPTIONS${c.reset}
|
|
51
|
-
--json, -j Output raw JSON
|
|
52
|
-
--snapshot, -s Save to snapshots/ with timestamp
|
|
53
|
-
--fastify-entry Fastify entry file (e.g. src/server.ts)
|
|
54
|
-
--path, -p Project path (default: current directory)
|
|
55
|
-
--help, -h Show this help
|
|
56
|
-
|
|
57
|
-
${c.bold}TRUTH PACK CONTENTS${c.reset}
|
|
58
|
-
• ${c.cyan}routes${c.reset} Server routes + client refs + gaps
|
|
59
|
-
• ${c.cyan}env${c.reset} Used vars + declared vars + sources
|
|
60
|
-
• ${c.cyan}auth${c.reset} Middleware + patterns + signals
|
|
61
|
-
• ${c.cyan}billing${c.reset} Stripe usage + webhooks + gates
|
|
62
|
-
• ${c.cyan}enforcement${c.reset} Paid surface checks
|
|
63
|
-
|
|
64
|
-
${c.bold}OUTPUT${c.reset}
|
|
65
|
-
.vibecheck/truth/truthpack.json Main truth pack
|
|
66
|
-
.vibecheck/truth/truthpack.md Human-readable summary
|
|
67
|
-
|
|
68
|
-
${c.bold}EXAMPLES${c.reset}
|
|
69
|
-
vibecheck ctx # Generate truth pack
|
|
70
|
-
vibecheck ctx --snapshot # Save snapshot
|
|
71
|
-
vibecheck ctx --json > truthpack.json # Export as JSON
|
|
72
|
-
`);
|
|
73
|
-
}
|
|
598
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
599
|
+
// MAIN CTX FUNCTION
|
|
600
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
74
601
|
|
|
75
602
|
async function runCtx(args) {
|
|
76
603
|
const opts = typeof args === 'object' && !Array.isArray(args)
|
|
@@ -83,110 +610,66 @@ async function runCtx(args) {
|
|
|
83
610
|
}
|
|
84
611
|
|
|
85
612
|
const root = opts.repoRoot || opts.path || process.cwd();
|
|
613
|
+
const projectName = path.basename(root);
|
|
86
614
|
const entry = opts.fastifyEntry || detectFastifyEntry(root);
|
|
615
|
+
const startTime = Date.now();
|
|
87
616
|
|
|
88
|
-
//
|
|
89
|
-
if (
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
console.log(
|
|
93
|
-
|
|
94
|
-
console.log(`${c.cyan}║${c.reset} ${c.dim}Building ground truth for AI agents${c.reset} ${c.cyan}║${c.reset}`);
|
|
95
|
-
console.log(`${c.cyan}║${c.reset} ${c.cyan}║${c.reset}`);
|
|
96
|
-
console.log(`${c.cyan}╚══════════════════════════════════════════════════════════════════════╝${c.reset}`);
|
|
97
|
-
console.log('');
|
|
617
|
+
// JSON mode - minimal output
|
|
618
|
+
if (opts.json || opts.print) {
|
|
619
|
+
const truthpack = await buildTruthpack({ repoRoot: root, fastifyEntry: entry });
|
|
620
|
+
writeTruthpack(root, truthpack);
|
|
621
|
+
console.log(JSON.stringify(truthpack, null, 2));
|
|
622
|
+
return 0;
|
|
98
623
|
}
|
|
99
624
|
|
|
100
|
-
//
|
|
101
|
-
|
|
102
|
-
|
|
625
|
+
// Print banner
|
|
626
|
+
printBanner();
|
|
627
|
+
|
|
628
|
+
console.log(` ${c.dim}Project:${c.reset} ${c.bold}${projectName}${c.reset}`);
|
|
629
|
+
console.log(` ${c.dim}Path:${c.reset} ${root}`);
|
|
630
|
+
if (entry) {
|
|
631
|
+
console.log(` ${c.dim}Fastify:${c.reset} ${colors.accent}${entry}${c.reset}`);
|
|
103
632
|
}
|
|
104
633
|
|
|
634
|
+
// Build truthpack with spinner
|
|
635
|
+
console.log();
|
|
636
|
+
startSpinner('Building truth pack...', colors.accent);
|
|
637
|
+
|
|
105
638
|
const truthpack = await buildTruthpack({ repoRoot: root, fastifyEntry: entry });
|
|
106
639
|
writeTruthpack(root, truthpack);
|
|
107
640
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
}
|
|
641
|
+
const buildDuration = Date.now() - startTime;
|
|
642
|
+
stopSpinner('Truth pack built', true);
|
|
111
643
|
|
|
112
644
|
// Handle snapshot
|
|
645
|
+
let snapshotPath = null;
|
|
113
646
|
if (opts.snapshot) {
|
|
647
|
+
startSpinner('Saving snapshot...', colors.accent);
|
|
648
|
+
|
|
114
649
|
const snapshotsDir = path.join(root, ".vibecheck", "truth", "snapshots");
|
|
115
650
|
fs.mkdirSync(snapshotsDir, { recursive: true });
|
|
116
651
|
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
117
|
-
|
|
652
|
+
snapshotPath = path.join(snapshotsDir, `truthpack-${timestamp}.json`);
|
|
118
653
|
fs.writeFileSync(snapshotPath, JSON.stringify(truthpack, null, 2));
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
}
|
|
654
|
+
|
|
655
|
+
stopSpinner('Snapshot saved', true);
|
|
122
656
|
}
|
|
123
657
|
|
|
124
|
-
//
|
|
125
|
-
|
|
126
|
-
console.log(JSON.stringify(truthpack, null, 2));
|
|
127
|
-
return 0;
|
|
128
|
-
}
|
|
658
|
+
// Print summary
|
|
659
|
+
printTruthpackSummary(truthpack, root);
|
|
129
660
|
|
|
130
|
-
//
|
|
131
|
-
const
|
|
132
|
-
|
|
133
|
-
)
|
|
134
|
-
const otherRoutes = (truthpack.routes?.server || []).filter(r =>
|
|
135
|
-
!r.handler?.includes("app/api") && !r.handler?.includes("pages/api")
|
|
136
|
-
).length;
|
|
661
|
+
// Success card
|
|
662
|
+
const outputPath = path.join(root, ".vibecheck", "truth", "truthpack.json");
|
|
663
|
+
const relativeOutput = path.relative(root, outputPath);
|
|
664
|
+
const relativeSnapshot = snapshotPath ? path.relative(root, snapshotPath) : null;
|
|
137
665
|
|
|
138
|
-
|
|
139
|
-
const detectedFramework = truthpack.meta?.framework ||
|
|
140
|
-
(truthpack.routes?.server || []).find(r => r.evidence?.[0]?.reason)?.evidence?.[0]?.reason?.split(' ')[0] ||
|
|
141
|
-
'Express';
|
|
142
|
-
const clientRefs = truthpack.routes?.clientRefs?.length || 0;
|
|
143
|
-
const gaps = truthpack.routes?.gaps?.length || 0;
|
|
144
|
-
|
|
145
|
-
const envUsed = truthpack.env?.vars?.length || 0;
|
|
146
|
-
const envDeclared = truthpack.env?.declared?.length || 0;
|
|
147
|
-
const envSources = (truthpack.env?.declaredSources || []).join(", ") || "none";
|
|
148
|
-
|
|
149
|
-
const nextMiddleware = truthpack.auth?.nextMiddleware?.length || 0;
|
|
150
|
-
const fastifySignals = truthpack.auth?.fastify?.signalTypes?.length || 0;
|
|
151
|
-
|
|
152
|
-
const hasStripe = truthpack.billing?.hasStripe;
|
|
153
|
-
const webhooks = truthpack.billing?.summary?.webhookHandlersFound || 0;
|
|
666
|
+
printSuccessCard(relativeOutput, relativeSnapshot, entry, buildDuration);
|
|
154
667
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
console.log('');
|
|
159
|
-
console.log(`${c.cyan}┌─ Routes ──────────────────────────────────────────────────────────────┐${c.reset}`);
|
|
160
|
-
console.log(`${c.cyan}│${c.reset} ${c.bold}▲ Next.js:${c.reset} ${nextRoutes} routes`);
|
|
161
|
-
console.log(`${c.cyan}│${c.reset} ${c.bold}⚡ ${detectedFramework}:${c.reset} ${otherRoutes} routes`);
|
|
162
|
-
console.log(`${c.cyan}│${c.reset} ${c.dim}Client refs:${c.reset} ${clientRefs} ${gaps > 0 ? `${c.yellow}⚠ ${gaps} gaps${c.reset}` : `${c.green}✓ no gaps${c.reset}`}`);
|
|
163
|
-
console.log(`${c.cyan}└───────────────────────────────────────────────────────────────────────┘${c.reset}`);
|
|
164
|
-
|
|
165
|
-
console.log(`${c.magenta}┌─ Environment ─────────────────────────────────────────────────────────┐${c.reset}`);
|
|
166
|
-
console.log(`${c.magenta}│${c.reset} ${c.bold}Used:${c.reset} ${envUsed} env vars`);
|
|
167
|
-
console.log(`${c.magenta}│${c.reset} ${c.bold}Declared:${c.reset} ${envDeclared} (${envSources})`);
|
|
168
|
-
console.log(`${c.magenta}└───────────────────────────────────────────────────────────────────────┘${c.reset}`);
|
|
169
|
-
|
|
170
|
-
console.log(`${c.blue}┌─ Auth ─────────────────────────────────────────────────────────────────┐${c.reset}`);
|
|
171
|
-
console.log(`${c.blue}│${c.reset} ${c.bold}Next middleware:${c.reset} ${nextMiddleware}`);
|
|
172
|
-
console.log(`${c.blue}│${c.reset} ${c.bold}Fastify signals:${c.reset} ${fastifySignals}`);
|
|
173
|
-
console.log(`${c.blue}└───────────────────────────────────────────────────────────────────────┘${c.reset}`);
|
|
174
|
-
|
|
175
|
-
console.log(`${c.yellow}┌─ Billing ──────────────────────────────────────────────────────────────┐${c.reset}`);
|
|
176
|
-
console.log(`${c.yellow}│${c.reset} ${c.bold}Stripe:${c.reset} ${hasStripe ? `${c.green}✓ detected${c.reset}` : `${c.dim}not detected${c.reset}`}`);
|
|
177
|
-
console.log(`${c.yellow}│${c.reset} ${c.bold}Webhooks:${c.reset} ${webhooks} handlers`);
|
|
178
|
-
console.log(`${c.yellow}│${c.reset} ${c.bold}Enforced:${c.reset} ${enforced}/${checked} paid routes`);
|
|
179
|
-
console.log(`${c.yellow}└───────────────────────────────────────────────────────────────────────┘${c.reset}`);
|
|
180
|
-
|
|
181
|
-
console.log('');
|
|
182
|
-
console.log(`${c.green}${c.bold}✓ Truth pack saved${c.reset}`);
|
|
183
|
-
console.log(` ${c.dim}→${c.reset} ${path.join(root, ".vibecheck", "truth", "truthpack.json")}`);
|
|
184
|
-
if (entry) {
|
|
185
|
-
console.log(` ${c.dim}Fastify entry:${c.reset} ${entry}`);
|
|
186
|
-
}
|
|
187
|
-
console.log('');
|
|
668
|
+
// Next steps hint
|
|
669
|
+
console.log(` ${c.dim}Next:${c.reset} ${colors.accent}vibecheck ship${c.reset} ${c.dim}to check if you're ready to ship${c.reset}`);
|
|
670
|
+
console.log();
|
|
188
671
|
|
|
189
672
|
return 0;
|
|
190
673
|
}
|
|
191
674
|
|
|
192
|
-
module.exports = { runCtx };
|
|
675
|
+
module.exports = { runCtx };
|