@vibecheckai/cli 3.1.2 → 3.1.4
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 +60 -33
- package/bin/registry.js +319 -34
- package/bin/runners/CLI_REFACTOR_SUMMARY.md +229 -0
- package/bin/runners/REPORT_AUDIT.md +64 -0
- package/bin/runners/lib/entitlements-v2.js +97 -28
- package/bin/runners/lib/entitlements.js +3 -6
- package/bin/runners/lib/init-wizard.js +1 -1
- package/bin/runners/lib/report-engine.js +459 -280
- package/bin/runners/lib/report-html.js +1154 -1423
- package/bin/runners/lib/report-output.js +187 -0
- package/bin/runners/lib/report-templates.js +848 -850
- package/bin/runners/lib/scan-output.js +545 -0
- package/bin/runners/lib/server-usage.js +0 -12
- package/bin/runners/lib/ship-output.js +641 -0
- package/bin/runners/lib/status-output.js +253 -0
- package/bin/runners/lib/terminal-ui.js +853 -0
- package/bin/runners/runCheckpoint.js +502 -0
- package/bin/runners/runContracts.js +105 -0
- package/bin/runners/runExport.js +93 -0
- package/bin/runners/runFix.js +31 -24
- package/bin/runners/runInit.js +377 -112
- package/bin/runners/runInstall.js +1 -5
- package/bin/runners/runLabs.js +3 -3
- package/bin/runners/runPolish.js +2452 -0
- package/bin/runners/runProve.js +2 -2
- package/bin/runners/runReport.js +251 -200
- package/bin/runners/runRuntime.js +110 -0
- package/bin/runners/runScan.js +477 -379
- package/bin/runners/runSecurity.js +92 -0
- package/bin/runners/runShip.js +137 -207
- package/bin/runners/runStatus.js +16 -68
- package/bin/runners/utils.js +5 -5
- package/bin/vibecheck.js +25 -11
- package/mcp-server/index.js +150 -18
- package/mcp-server/package.json +2 -2
- package/mcp-server/premium-tools.js +13 -13
- package/mcp-server/tier-auth.js +292 -27
- package/mcp-server/vibecheck-tools.js +9 -9
- package/package.json +1 -1
- package/bin/runners/runClaimVerifier.js +0 -483
- package/bin/runners/runContextCompiler.js +0 -385
- package/bin/runners/runGate.js +0 -17
- package/bin/runners/runInitGha.js +0 -164
- package/bin/runners/runInteractive.js +0 -388
- package/bin/runners/runMdc.js +0 -204
- package/bin/runners/runMissionGenerator.js +0 -282
- package/bin/runners/runTruthpack.js +0 -636
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* vibecheck security - AuthZ + IDOR + Sensitive Security Proofs
|
|
3
|
+
*
|
|
4
|
+
* Subcommands:
|
|
5
|
+
* security model = learn (extract auth model)
|
|
6
|
+
* security matrix = build AuthZ matrix
|
|
7
|
+
* security idor = detect IDOR candidates
|
|
8
|
+
* security prove --url ... = runtime verification
|
|
9
|
+
*
|
|
10
|
+
* Replaces: permissions
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
"use strict";
|
|
14
|
+
|
|
15
|
+
const c = {
|
|
16
|
+
reset: '\x1b[0m',
|
|
17
|
+
bold: '\x1b[1m',
|
|
18
|
+
dim: '\x1b[2m',
|
|
19
|
+
cyan: '\x1b[36m',
|
|
20
|
+
yellow: '\x1b[33m',
|
|
21
|
+
red: '\x1b[31m',
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
function printHelp() {
|
|
25
|
+
console.log(`
|
|
26
|
+
${c.cyan}${c.bold}🔒 vibecheck security${c.reset} - Authorization & Security Verification
|
|
27
|
+
|
|
28
|
+
AuthZ matrix & IDOR detection for sensitive security proofs.
|
|
29
|
+
|
|
30
|
+
${c.bold}SUBCOMMANDS${c.reset}
|
|
31
|
+
${c.cyan}model${c.reset} ${c.dim}Extract auth model from codebase (replaces 'permissions --learn')${c.reset}
|
|
32
|
+
${c.cyan}matrix${c.reset} ${c.dim}Build AuthZ matrix (replaces 'permissions --matrix')${c.reset}
|
|
33
|
+
${c.cyan}idor${c.reset} ${c.dim}Detect IDOR candidates (replaces 'permissions --idor')${c.reset}
|
|
34
|
+
${c.cyan}prove${c.reset} --url <url> ${c.dim}Runtime verification (replaces 'permissions --prove')${c.reset}
|
|
35
|
+
|
|
36
|
+
${c.bold}EXAMPLES${c.reset}
|
|
37
|
+
vibecheck security model
|
|
38
|
+
vibecheck security matrix
|
|
39
|
+
vibecheck security idor
|
|
40
|
+
vibecheck security prove --url http://localhost:3000
|
|
41
|
+
|
|
42
|
+
${c.dim}Note: Old command still works as alias:
|
|
43
|
+
vibecheck permissions → vibecheck security model${c.reset}
|
|
44
|
+
`);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async function runSecurity(args) {
|
|
48
|
+
if (!args || args.length === 0 || args[0] === "--help" || args[0] === "-h") {
|
|
49
|
+
printHelp();
|
|
50
|
+
return 0;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const subcommand = args[0];
|
|
54
|
+
const subArgs = args.slice(1);
|
|
55
|
+
|
|
56
|
+
// Map subcommands to permissions flags
|
|
57
|
+
let permissionsArgs = [];
|
|
58
|
+
|
|
59
|
+
switch (subcommand) {
|
|
60
|
+
case "model":
|
|
61
|
+
permissionsArgs = ["--learn", ...subArgs];
|
|
62
|
+
break;
|
|
63
|
+
|
|
64
|
+
case "matrix":
|
|
65
|
+
permissionsArgs = ["--matrix", ...subArgs];
|
|
66
|
+
break;
|
|
67
|
+
|
|
68
|
+
case "idor":
|
|
69
|
+
permissionsArgs = ["--idor", ...subArgs];
|
|
70
|
+
break;
|
|
71
|
+
|
|
72
|
+
case "prove":
|
|
73
|
+
permissionsArgs = ["--prove", ...subArgs];
|
|
74
|
+
break;
|
|
75
|
+
|
|
76
|
+
default:
|
|
77
|
+
console.error(`${c.red}Unknown subcommand:${c.reset} ${subcommand}`);
|
|
78
|
+
console.log(`\n${c.dim}Run 'vibecheck security --help' for available subcommands.${c.reset}\n`);
|
|
79
|
+
return 1;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Delegate to runPermissions
|
|
83
|
+
try {
|
|
84
|
+
const { runPermissions } = require("./runPermissions");
|
|
85
|
+
return await runPermissions(permissionsArgs);
|
|
86
|
+
} catch (e) {
|
|
87
|
+
console.error(`${c.red}Error:${c.reset} Security command unavailable: ${e.message}`);
|
|
88
|
+
return 1;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
module.exports = { runSecurity };
|
package/bin/runners/runShip.js
CHANGED
|
@@ -39,120 +39,45 @@ const upsell = require("./lib/upsell");
|
|
|
39
39
|
const entitlements = require("./lib/entitlements-v2");
|
|
40
40
|
|
|
41
41
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
42
|
-
//
|
|
42
|
+
// ENHANCED TERMINAL UI & OUTPUT MODULES
|
|
43
43
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
44
44
|
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
strike: '\x1b[9m',
|
|
55
|
-
// Colors
|
|
56
|
-
black: '\x1b[30m',
|
|
57
|
-
red: '\x1b[31m',
|
|
58
|
-
green: '\x1b[32m',
|
|
59
|
-
yellow: '\x1b[33m',
|
|
60
|
-
blue: '\x1b[34m',
|
|
61
|
-
magenta: '\x1b[35m',
|
|
62
|
-
cyan: '\x1b[36m',
|
|
63
|
-
white: '\x1b[37m',
|
|
64
|
-
// Bright colors
|
|
65
|
-
gray: '\x1b[90m',
|
|
66
|
-
brightRed: '\x1b[91m',
|
|
67
|
-
brightGreen: '\x1b[92m',
|
|
68
|
-
brightYellow: '\x1b[93m',
|
|
69
|
-
brightBlue: '\x1b[94m',
|
|
70
|
-
brightMagenta: '\x1b[95m',
|
|
71
|
-
brightCyan: '\x1b[96m',
|
|
72
|
-
brightWhite: '\x1b[97m',
|
|
73
|
-
// Background
|
|
74
|
-
bgBlack: '\x1b[40m',
|
|
75
|
-
bgRed: '\x1b[41m',
|
|
76
|
-
bgGreen: '\x1b[42m',
|
|
77
|
-
bgYellow: '\x1b[43m',
|
|
78
|
-
bgBlue: '\x1b[44m',
|
|
79
|
-
bgMagenta: '\x1b[45m',
|
|
80
|
-
bgCyan: '\x1b[46m',
|
|
81
|
-
bgWhite: '\x1b[47m',
|
|
82
|
-
bgBrightBlack: '\x1b[100m',
|
|
83
|
-
bgBrightRed: '\x1b[101m',
|
|
84
|
-
bgBrightGreen: '\x1b[102m',
|
|
85
|
-
bgBrightYellow: '\x1b[103m',
|
|
86
|
-
// Cursor control
|
|
87
|
-
cursorUp: (n = 1) => `\x1b[${n}A`,
|
|
88
|
-
cursorDown: (n = 1) => `\x1b[${n}B`,
|
|
89
|
-
cursorRight: (n = 1) => `\x1b[${n}C`,
|
|
90
|
-
cursorLeft: (n = 1) => `\x1b[${n}D`,
|
|
91
|
-
clearLine: '\x1b[2K',
|
|
92
|
-
clearScreen: '\x1b[2J',
|
|
93
|
-
saveCursor: '\x1b[s',
|
|
94
|
-
restoreCursor: '\x1b[u',
|
|
95
|
-
hideCursor: '\x1b[?25l',
|
|
96
|
-
showCursor: '\x1b[?25h',
|
|
97
|
-
};
|
|
45
|
+
const {
|
|
46
|
+
ansi,
|
|
47
|
+
colors,
|
|
48
|
+
icons,
|
|
49
|
+
Spinner,
|
|
50
|
+
renderBanner,
|
|
51
|
+
renderSection,
|
|
52
|
+
formatDuration,
|
|
53
|
+
} = require("./lib/terminal-ui");
|
|
98
54
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
gradient4: rgb(100, 100, 255), // Purple-blue
|
|
110
|
-
gradient5: rgb(150, 50, 255), // Purple
|
|
111
|
-
gradient6: rgb(200, 0, 255), // Magenta
|
|
112
|
-
|
|
113
|
-
// Verdict colors
|
|
114
|
-
shipGreen: rgb(0, 255, 150),
|
|
115
|
-
warnAmber: rgb(255, 200, 0),
|
|
116
|
-
blockRed: rgb(255, 80, 80),
|
|
117
|
-
|
|
118
|
-
// UI colors
|
|
119
|
-
accent: rgb(100, 200, 255),
|
|
120
|
-
muted: rgb(120, 120, 140),
|
|
121
|
-
subtle: rgb(80, 80, 100),
|
|
122
|
-
highlight: rgb(255, 255, 255),
|
|
123
|
-
|
|
124
|
-
// Severity colors
|
|
125
|
-
critical: rgb(255, 60, 60),
|
|
126
|
-
high: rgb(255, 120, 60),
|
|
127
|
-
medium: rgb(255, 200, 60),
|
|
128
|
-
low: rgb(100, 200, 255),
|
|
129
|
-
info: rgb(150, 150, 180),
|
|
130
|
-
};
|
|
55
|
+
const {
|
|
56
|
+
formatShipOutput,
|
|
57
|
+
renderVerdictCard,
|
|
58
|
+
renderFixModeHeader,
|
|
59
|
+
renderFixResults,
|
|
60
|
+
renderBadgeOutput,
|
|
61
|
+
getExitCode,
|
|
62
|
+
EXIT_CODES,
|
|
63
|
+
shipIcons,
|
|
64
|
+
} = require("./lib/ship-output");
|
|
131
65
|
|
|
132
66
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
133
67
|
// PREMIUM BANNER
|
|
134
68
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
135
69
|
|
|
136
|
-
const
|
|
137
|
-
${rgb(0, 255, 200)}
|
|
138
|
-
${rgb(0, 230, 220)}
|
|
139
|
-
${rgb(0, 200, 255)}
|
|
140
|
-
${rgb(50, 150, 255)}
|
|
141
|
-
${rgb(100, 100, 255)}
|
|
142
|
-
${rgb(150, 50, 255)}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
${
|
|
147
|
-
${rgb(0, 230, 220)} ██║ ██║██║██╔══██╗██╔════╝██╔════╝██║ ██║██╔════╝██╔════╝██║ ██╔╝${c.reset}
|
|
148
|
-
${rgb(0, 200, 255)} ██║ ██║██║██████╔╝█████╗ ██║ ███████║█████╗ ██║ █████╔╝ ${c.reset}
|
|
149
|
-
${rgb(50, 150, 255)} ╚██╗ ██╔╝██║██╔══██╗██╔══╝ ██║ ██╔══██║██╔══╝ ██║ ██╔═██╗ ${c.reset}
|
|
150
|
-
${rgb(100, 100, 255)} ╚████╔╝ ██║██████╔╝███████╗╚██████╗██║ ██║███████╗╚██████╗██║ ██╗${c.reset}
|
|
151
|
-
${rgb(150, 50, 255)} ╚═══╝ ╚═╝╚═════╝ ╚══════╝ ╚═════╝╚═╝ ╚═╝╚══════╝ ╚═════╝╚═╝ ╚═╝${c.reset}
|
|
152
|
-
|
|
153
|
-
${c.dim} ┌─────────────────────────────────────────────────────────────────────┐${c.reset}
|
|
154
|
-
${c.dim} │${c.reset} ${rgb(0, 255, 200)}🚀${c.reset} ${c.bold}SHIP${c.reset} ${c.dim}•${c.reset} ${rgb(200, 200, 200)}The One Command${c.reset} ${c.dim}•${c.reset} ${rgb(150, 150, 150)}Zero Config${c.reset} ${c.dim}•${c.reset} ${rgb(100, 100, 100)}Plain English${c.reset} ${c.dim}│${c.reset}
|
|
155
|
-
${c.dim} └─────────────────────────────────────────────────────────────────────┘${c.reset}
|
|
70
|
+
const BANNER = `
|
|
71
|
+
${ansi.rgb(0, 255, 200)} ██╗ ██╗██╗██████╗ ███████╗ ██████╗██╗ ██╗███████╗ ██████╗██╗ ██╗${ansi.reset}
|
|
72
|
+
${ansi.rgb(0, 230, 220)} ██║ ██║██║██╔══██╗██╔════╝██╔════╝██║ ██║██╔════╝██╔════╝██║ ██╔╝${ansi.reset}
|
|
73
|
+
${ansi.rgb(0, 200, 255)} ██║ ██║██║██████╔╝█████╗ ██║ ███████║█████╗ ██║ █████╔╝ ${ansi.reset}
|
|
74
|
+
${ansi.rgb(50, 150, 255)} ╚██╗ ██╔╝██║██╔══██╗██╔══╝ ██║ ██╔══██║██╔══╝ ██║ ██╔═██╗ ${ansi.reset}
|
|
75
|
+
${ansi.rgb(100, 100, 255)} ╚████╔╝ ██║██████╔╝███████╗╚██████╗██║ ██║███████╗╚██████╗██║ ██╗${ansi.reset}
|
|
76
|
+
${ansi.rgb(150, 50, 255)} ╚═══╝ ╚═╝╚═════╝ ╚══════╝ ╚═════╝╚═╝ ╚═╝╚══════╝ ╚═════╝╚═╝ ╚═╝${ansi.reset}
|
|
77
|
+
|
|
78
|
+
${ansi.dim} ┌─────────────────────────────────────────────────────────────────────┐${ansi.reset}
|
|
79
|
+
${ansi.dim} │${ansi.reset} ${ansi.rgb(0, 255, 200)}🚀${ansi.reset} ${ansi.bold}SHIP${ansi.reset} ${ansi.dim}•${ansi.reset} ${ansi.rgb(200, 200, 200)}The One Command${ansi.reset} ${ansi.dim}•${ansi.reset} ${ansi.rgb(150, 150, 150)}Zero Config${ansi.reset} ${ansi.dim}•${ansi.reset} ${ansi.rgb(100, 100, 100)}Plain English${ansi.reset} ${ansi.dim}│${ansi.reset}
|
|
80
|
+
${ansi.dim} └─────────────────────────────────────────────────────────────────────┘${ansi.reset}
|
|
156
81
|
`;
|
|
157
82
|
|
|
158
83
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
@@ -211,11 +136,7 @@ let spinnerIndex = 0;
|
|
|
211
136
|
let spinnerInterval = null;
|
|
212
137
|
let spinnerStartTime = null;
|
|
213
138
|
|
|
214
|
-
|
|
215
|
-
if (ms < 1000) return `${ms}ms`;
|
|
216
|
-
if (ms < 60000) return `${(ms / 1000).toFixed(1)}s`;
|
|
217
|
-
return `${Math.floor(ms / 60000)}m ${Math.floor((ms % 60000) / 1000)}s`;
|
|
218
|
-
}
|
|
139
|
+
// formatDuration is imported from terminal-ui.js
|
|
219
140
|
|
|
220
141
|
function formatNumber(num) {
|
|
221
142
|
return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
|
|
@@ -280,8 +201,18 @@ function stopSpinner(message, success = true) {
|
|
|
280
201
|
spinnerStartTime = null;
|
|
281
202
|
}
|
|
282
203
|
|
|
204
|
+
// Compact banner for fix mode
|
|
205
|
+
const SHIP_BANNER = `
|
|
206
|
+
${ansi.rgb(0, 255, 200)} ███████╗██╗ ██╗██╗██████╗ ${ansi.reset}
|
|
207
|
+
${ansi.rgb(0, 230, 220)} ██╔════╝██║ ██║██║██╔══██╗${ansi.reset}
|
|
208
|
+
${ansi.rgb(0, 200, 255)} ███████╗███████║██║██████╔╝${ansi.reset}
|
|
209
|
+
${ansi.rgb(50, 150, 255)} ╚════██║██╔══██║██║██╔═══╝ ${ansi.reset}
|
|
210
|
+
${ansi.rgb(100, 100, 255)} ███████║██║ ██║██║██║ ${ansi.reset}
|
|
211
|
+
${ansi.rgb(150, 50, 255)} ╚══════╝╚═╝ ╚═╝╚═╝╚═╝ ${ansi.reset}
|
|
212
|
+
`;
|
|
213
|
+
|
|
283
214
|
function printBanner(compact = false) {
|
|
284
|
-
console.log(compact ? SHIP_BANNER :
|
|
215
|
+
console.log(compact ? SHIP_BANNER : BANNER);
|
|
285
216
|
}
|
|
286
217
|
|
|
287
218
|
function printDivider(char = '─', width = 69, color = c.dim) {
|
|
@@ -677,41 +608,41 @@ function printFixResults(fixResults) {
|
|
|
677
608
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
678
609
|
|
|
679
610
|
function printHelp() {
|
|
680
|
-
console.log(
|
|
611
|
+
console.log(BANNER);
|
|
681
612
|
console.log(`
|
|
682
|
-
${
|
|
683
|
-
|
|
684
|
-
${
|
|
685
|
-
|
|
686
|
-
${
|
|
687
|
-
${colors.accent}--fix, -f${
|
|
688
|
-
${colors.accent}--assist${
|
|
689
|
-
${colors.accent}--badge, -b${
|
|
690
|
-
${colors.accent}--strict${
|
|
691
|
-
${colors.accent}--ci${
|
|
692
|
-
${colors.accent}--json${
|
|
693
|
-
${colors.accent}--path, -p${
|
|
694
|
-
${colors.accent}--verbose, -v${
|
|
695
|
-
${colors.accent}--help, -h${
|
|
696
|
-
|
|
697
|
-
${
|
|
698
|
-
${colors.
|
|
699
|
-
${colors.
|
|
700
|
-
${colors.
|
|
701
|
-
|
|
702
|
-
${
|
|
703
|
-
${
|
|
613
|
+
${ansi.bold}Usage:${ansi.reset} vibecheck ship [options]
|
|
614
|
+
|
|
615
|
+
${ansi.bold}The One Command${ansi.reset} — Get a ship verdict: ${colors.success}SHIP${ansi.reset} | ${colors.warning}WARN${ansi.reset} | ${colors.error}BLOCK${ansi.reset}
|
|
616
|
+
|
|
617
|
+
${ansi.bold}Options:${ansi.reset}
|
|
618
|
+
${colors.accent}--fix, -f${ansi.reset} Try safe mechanical fixes ${ansi.dim}(shows plan first)${ansi.reset}
|
|
619
|
+
${colors.accent}--assist${ansi.reset} Generate AI mission prompts for complex issues
|
|
620
|
+
${colors.accent}--badge, -b${ansi.reset} Generate embeddable badge for README
|
|
621
|
+
${colors.accent}--strict${ansi.reset} Treat warnings as blockers
|
|
622
|
+
${colors.accent}--ci${ansi.reset} Machine output for CI/CD pipelines
|
|
623
|
+
${colors.accent}--json${ansi.reset} Output results as JSON
|
|
624
|
+
${colors.accent}--path, -p${ansi.reset} Project path ${ansi.dim}(default: current directory)${ansi.reset}
|
|
625
|
+
${colors.accent}--verbose, -v${ansi.reset} Show detailed progress
|
|
626
|
+
${colors.accent}--help, -h${ansi.reset} Show this help
|
|
627
|
+
|
|
628
|
+
${ansi.bold}Exit Codes:${ansi.reset}
|
|
629
|
+
${colors.success}0${ansi.reset} SHIP — Ready to ship
|
|
630
|
+
${colors.warning}1${ansi.reset} WARN — Warnings found, review recommended
|
|
631
|
+
${colors.error}2${ansi.reset} BLOCK — Blockers found, must fix before shipping
|
|
632
|
+
|
|
633
|
+
${ansi.bold}Examples:${ansi.reset}
|
|
634
|
+
${ansi.dim}# Quick ship check${ansi.reset}
|
|
704
635
|
vibecheck ship
|
|
705
636
|
|
|
706
|
-
${
|
|
637
|
+
${ansi.dim}# Auto-fix what can be fixed${ansi.reset}
|
|
707
638
|
vibecheck ship --fix
|
|
708
639
|
|
|
709
|
-
${
|
|
640
|
+
${ansi.dim}# Generate README badge${ansi.reset}
|
|
710
641
|
vibecheck ship --badge
|
|
711
642
|
|
|
712
|
-
${
|
|
643
|
+
${ansi.dim}# Strict CI mode (warnings = failure)${ansi.reset}
|
|
713
644
|
vibecheck ship --strict --ci
|
|
714
|
-
|
|
645
|
+
`);
|
|
715
646
|
}
|
|
716
647
|
|
|
717
648
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
@@ -832,6 +763,8 @@ function parseArgs(args) {
|
|
|
832
763
|
assist: false,
|
|
833
764
|
strict: false,
|
|
834
765
|
ci: false,
|
|
766
|
+
mode: null, // "scan" for scan mode
|
|
767
|
+
withRuntime: false, // merge runtime results
|
|
835
768
|
help: false,
|
|
836
769
|
};
|
|
837
770
|
|
|
@@ -844,6 +777,11 @@ function parseArgs(args) {
|
|
|
844
777
|
else if (a === "--assist") opts.assist = true;
|
|
845
778
|
else if (a === "--strict") opts.strict = true;
|
|
846
779
|
else if (a === "--ci") opts.ci = true;
|
|
780
|
+
else if (a === "--mode") opts.mode = args[++i];
|
|
781
|
+
else if (a === "--with") {
|
|
782
|
+
const next = args[++i];
|
|
783
|
+
if (next === "runtime") opts.withRuntime = true;
|
|
784
|
+
}
|
|
847
785
|
else if (a === "--help" || a === "-h") opts.help = true;
|
|
848
786
|
else if (a.startsWith("--path=")) opts.path = a.split("=")[1];
|
|
849
787
|
else if (a === "--path" || a === "-p") opts.path = args[++i];
|
|
@@ -879,8 +817,8 @@ async function runShip(args, context = {}) {
|
|
|
879
817
|
}
|
|
880
818
|
} catch (err) {
|
|
881
819
|
if (err.code === 'LIMIT_EXCEEDED' || err.code === 'FEATURE_NOT_AVAILABLE') {
|
|
882
|
-
console.error(`\n ${colors.
|
|
883
|
-
return
|
|
820
|
+
console.error(`\n ${colors.error}${icons.error}${ansi.reset} ${err.upgradePrompt || err.message}\n`);
|
|
821
|
+
return EXIT_CODES.WARN;
|
|
884
822
|
}
|
|
885
823
|
throw err;
|
|
886
824
|
}
|
|
@@ -891,11 +829,11 @@ async function runShip(args, context = {}) {
|
|
|
891
829
|
const outputDir = path.join(projectPath, ".vibecheck");
|
|
892
830
|
const projectName = path.basename(projectPath);
|
|
893
831
|
|
|
894
|
-
// Print banner (
|
|
832
|
+
// Print banner (unless quiet/json/ci)
|
|
895
833
|
if (!opts.json && !opts.ci) {
|
|
896
|
-
printBanner(
|
|
897
|
-
console.log(` ${
|
|
898
|
-
console.log(` ${
|
|
834
|
+
printBanner();
|
|
835
|
+
console.log(` ${ansi.dim}Project:${ansi.reset} ${ansi.bold}${projectName}${ansi.reset}`);
|
|
836
|
+
console.log(` ${ansi.dim}Path:${ansi.reset} ${projectPath}`);
|
|
899
837
|
console.log();
|
|
900
838
|
}
|
|
901
839
|
|
|
@@ -912,8 +850,14 @@ async function runShip(args, context = {}) {
|
|
|
912
850
|
};
|
|
913
851
|
|
|
914
852
|
try {
|
|
853
|
+
// Initialize spinner
|
|
854
|
+
let spinner;
|
|
855
|
+
if (!opts.json && !opts.ci) {
|
|
856
|
+
spinner = new Spinner({ color: colors.accent });
|
|
857
|
+
}
|
|
858
|
+
|
|
915
859
|
// Phase 1: Production Integrity Check
|
|
916
|
-
if (
|
|
860
|
+
if (spinner) spinner.start('Checking production integrity...');
|
|
917
861
|
|
|
918
862
|
try {
|
|
919
863
|
const { auditProductionIntegrity } = require(
|
|
@@ -926,13 +870,13 @@ async function runShip(args, context = {}) {
|
|
|
926
870
|
results.deductions = integrity.deductions;
|
|
927
871
|
results.integrity = integrityResults;
|
|
928
872
|
} catch (err) {
|
|
929
|
-
if (opts.verbose) console.warn(` ${
|
|
873
|
+
if (opts.verbose) console.warn(` ${ansi.dim}Integrity check skipped: ${err.message}${ansi.reset}`);
|
|
930
874
|
}
|
|
931
875
|
|
|
932
|
-
if (
|
|
876
|
+
if (spinner) spinner.succeed('Production integrity checked');
|
|
933
877
|
|
|
934
878
|
// Phase 2: Route Truth Analysis
|
|
935
|
-
if (
|
|
879
|
+
if (spinner) spinner.start('Building route truth map...');
|
|
936
880
|
|
|
937
881
|
const fastifyEntry = detectFastifyEntry(projectPath);
|
|
938
882
|
const truthpack = await buildTruthpack({ repoRoot: projectPath, fastifyEntry });
|
|
@@ -948,7 +892,8 @@ async function runShip(args, context = {}) {
|
|
|
948
892
|
...findStripeWebhookViolations(truthpack),
|
|
949
893
|
...findPaidSurfaceNotEnforced(truthpack),
|
|
950
894
|
...findOwnerModeBypass(projectPath),
|
|
951
|
-
|
|
895
|
+
// Merge runtime findings if --with runtime is specified
|
|
896
|
+
...(opts.withRuntime ? findingsFromReality(projectPath) : [])
|
|
952
897
|
];
|
|
953
898
|
|
|
954
899
|
// Contract drift detection
|
|
@@ -960,15 +905,15 @@ async function runShip(args, context = {}) {
|
|
|
960
905
|
|
|
961
906
|
results.findings = allFindings;
|
|
962
907
|
|
|
963
|
-
if (
|
|
908
|
+
if (spinner) spinner.succeed(`Route truth mapped (${truthpack.routes?.server?.length || 0} routes)`);
|
|
964
909
|
|
|
965
910
|
// Phase 3: Build Proof Graph
|
|
966
|
-
if (
|
|
911
|
+
if (spinner) spinner.start('Building proof graph...');
|
|
967
912
|
|
|
968
913
|
const proofGraph = buildProofGraph(allFindings, truthpack, projectPath);
|
|
969
914
|
results.proofGraph = proofGraph;
|
|
970
915
|
|
|
971
|
-
if (
|
|
916
|
+
if (spinner) spinner.succeed(`Proof graph built (${proofGraph.summary.totalClaims} claims)`);
|
|
972
917
|
|
|
973
918
|
// Calculate final verdict
|
|
974
919
|
const blockers = allFindings.filter(f => f.severity === 'BLOCK' || f.severity === 'critical');
|
|
@@ -1008,7 +953,7 @@ async function runShip(args, context = {}) {
|
|
|
1008
953
|
runId,
|
|
1009
954
|
command: "ship",
|
|
1010
955
|
startTime,
|
|
1011
|
-
exitCode:
|
|
956
|
+
exitCode: getExitCode(verdict),
|
|
1012
957
|
verdict,
|
|
1013
958
|
result: {
|
|
1014
959
|
verdict,
|
|
@@ -1050,7 +995,7 @@ async function runShip(args, context = {}) {
|
|
|
1050
995
|
});
|
|
1051
996
|
const proofPath = saveArtifact(runId, "proof-graph", proofGraph);
|
|
1052
997
|
|
|
1053
|
-
return
|
|
998
|
+
return getExitCode(verdict);
|
|
1054
999
|
}
|
|
1055
1000
|
|
|
1056
1001
|
// CI output mode (minimal)
|
|
@@ -1069,69 +1014,52 @@ async function runShip(args, context = {}) {
|
|
|
1069
1014
|
timestamp: new Date().toISOString()
|
|
1070
1015
|
});
|
|
1071
1016
|
|
|
1072
|
-
return
|
|
1017
|
+
return getExitCode(verdict);
|
|
1073
1018
|
}
|
|
1074
1019
|
|
|
1075
1020
|
// Fix mode
|
|
1021
|
+
let fixResults = null;
|
|
1076
1022
|
if (opts.fix) {
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1023
|
+
if (spinner) spinner.start('Applying safe fixes...');
|
|
1024
|
+
fixResults = await runAutoFix(projectPath, results, outputDir, allFindings);
|
|
1025
|
+
if (spinner) spinner.succeed('Safe fixes applied');
|
|
1080
1026
|
}
|
|
1081
1027
|
|
|
1082
|
-
//
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
}
|
|
1028
|
+
// Human-readable output using ship-output module
|
|
1029
|
+
const result = {
|
|
1030
|
+
verdict,
|
|
1031
|
+
score: results.score,
|
|
1032
|
+
findings: allFindings,
|
|
1033
|
+
blockers,
|
|
1034
|
+
warnings,
|
|
1035
|
+
truthpack,
|
|
1036
|
+
proofGraph,
|
|
1037
|
+
fixResults,
|
|
1038
|
+
duration: Date.now() - executionStart,
|
|
1039
|
+
};
|
|
1095
1040
|
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1041
|
+
console.log(formatShipOutput(result, {
|
|
1042
|
+
verbose: opts.verbose,
|
|
1043
|
+
showFix: opts.fix,
|
|
1044
|
+
showBadge: opts.badge,
|
|
1045
|
+
outputDir,
|
|
1046
|
+
projectPath,
|
|
1047
|
+
}));
|
|
1100
1048
|
|
|
1101
|
-
// Badge generation
|
|
1049
|
+
// Badge file generation
|
|
1102
1050
|
if (opts.badge) {
|
|
1103
|
-
const
|
|
1051
|
+
const { data: badgeData } = renderBadgeOutput(projectPath, verdict, results.score);
|
|
1104
1052
|
|
|
1105
1053
|
// Save badge info
|
|
1106
1054
|
fs.mkdirSync(outputDir, { recursive: true });
|
|
1107
1055
|
fs.writeFileSync(
|
|
1108
1056
|
path.join(outputDir, 'badge.json'),
|
|
1109
|
-
JSON.stringify({ ...
|
|
1057
|
+
JSON.stringify({ ...badgeData, verdict, score: results.score, generatedAt: new Date().toISOString() }, null, 2)
|
|
1110
1058
|
);
|
|
1111
1059
|
}
|
|
1112
1060
|
|
|
1113
|
-
//
|
|
1114
|
-
printSection('REPORTS', ICONS.doc);
|
|
1115
|
-
console.log();
|
|
1116
|
-
console.log(` ${colors.accent}${outputDir}/report.html${c.reset}`);
|
|
1117
|
-
console.log(` ${c.dim}${outputDir}/last_ship.json${c.reset}`);
|
|
1118
|
-
if (opts.fix) {
|
|
1119
|
-
console.log(` ${colors.accent}${outputDir}/fixes.md${c.reset}`);
|
|
1120
|
-
console.log(` ${colors.accent}${outputDir}/ai-fix-prompt.md${c.reset}`);
|
|
1121
|
-
}
|
|
1122
|
-
console.log();
|
|
1123
|
-
|
|
1124
|
-
// Quick actions
|
|
1061
|
+
// Earned upsell: Badge withheld when verdict != SHIP
|
|
1125
1062
|
if (!results.canShip) {
|
|
1126
|
-
printSection('NEXT STEPS', ICONS.lightning);
|
|
1127
|
-
console.log();
|
|
1128
|
-
if (!opts.fix) {
|
|
1129
|
-
console.log(` ${colors.accent}vibecheck ship --fix${c.reset} ${c.dim}Auto-fix what can be fixed${c.reset}`);
|
|
1130
|
-
}
|
|
1131
|
-
console.log(` ${colors.accent}vibecheck ship --assist${c.reset} ${c.dim}Get AI help for complex issues${c.reset}`);
|
|
1132
|
-
console.log();
|
|
1133
|
-
|
|
1134
|
-
// Earned upsell: Badge withheld when verdict != SHIP
|
|
1135
1063
|
const currentTier = context?.authInfo?.access?.tier || "free";
|
|
1136
1064
|
if (entitlements.tierMeetsMinimum(currentTier, "starter")) {
|
|
1137
1065
|
// User has badge access but verdict prevents it
|
|
@@ -1154,14 +1082,14 @@ async function runShip(args, context = {}) {
|
|
|
1154
1082
|
issueCount: allFindings.length,
|
|
1155
1083
|
});
|
|
1156
1084
|
|
|
1157
|
-
// Write artifacts
|
|
1085
|
+
// Write artifacts (no HTML report - use 'vibecheck report' command instead)
|
|
1158
1086
|
try {
|
|
1159
1087
|
const { writeArtifacts } = require("./utils");
|
|
1160
|
-
writeArtifacts(outputDir, results);
|
|
1088
|
+
writeArtifacts(outputDir, results, { skipHtmlReport: true });
|
|
1161
1089
|
} catch {}
|
|
1162
1090
|
|
|
1163
1091
|
// Exit code: 0=SHIP, 1=WARN, 2=BLOCK
|
|
1164
|
-
const exitCode =
|
|
1092
|
+
const exitCode = getExitCode(verdict);
|
|
1165
1093
|
|
|
1166
1094
|
// Save final results
|
|
1167
1095
|
saveArtifact(runId, "summary", {
|
|
@@ -1175,14 +1103,14 @@ async function runShip(args, context = {}) {
|
|
|
1175
1103
|
return exitCode;
|
|
1176
1104
|
|
|
1177
1105
|
} catch (error) {
|
|
1178
|
-
if (
|
|
1106
|
+
if (spinner) spinner.fail(`Ship check failed: ${error.message}`);
|
|
1179
1107
|
|
|
1180
|
-
console.error(`\n ${colors.
|
|
1108
|
+
console.error(`\n ${colors.error}${icons.error}${ansi.reset} ${ansi.bold}Error:${ansi.reset} ${error.message}`);
|
|
1181
1109
|
if (opts.verbose) {
|
|
1182
|
-
console.error(` ${
|
|
1110
|
+
console.error(` ${ansi.dim}${error.stack}${ansi.reset}`);
|
|
1183
1111
|
}
|
|
1184
1112
|
|
|
1185
|
-
return
|
|
1113
|
+
return EXIT_CODES.ERROR;
|
|
1186
1114
|
}
|
|
1187
1115
|
}
|
|
1188
1116
|
|
|
@@ -1272,6 +1200,8 @@ async function shipCore({ repoRoot, fastifyEntry, jsonOut, noWrite } = {}) {
|
|
|
1272
1200
|
fs.writeFileSync(path.join(outDir, "last_ship.json"), JSON.stringify(report, null, 2));
|
|
1273
1201
|
fs.writeFileSync(path.join(outDir, "proof-graph.json"), JSON.stringify(proofGraph, null, 2));
|
|
1274
1202
|
|
|
1203
|
+
// Note: HTML reports are generated by 'vibecheck report' command, not here
|
|
1204
|
+
|
|
1275
1205
|
if (jsonOut) {
|
|
1276
1206
|
fs.writeFileSync(path.isAbsolute(jsonOut) ? jsonOut : path.join(root, jsonOut), JSON.stringify(report, null, 2));
|
|
1277
1207
|
}
|