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