@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/runLaunch.js
DELETED
|
@@ -1,181 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* vibecheck launch - Pre-launch checklist wizard
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
const fs = require("fs");
|
|
6
|
-
const path = require("path");
|
|
7
|
-
const readline = require("readline");
|
|
8
|
-
|
|
9
|
-
const c = {
|
|
10
|
-
reset: "\x1b[0m",
|
|
11
|
-
bold: "\x1b[1m",
|
|
12
|
-
dim: "\x1b[2m",
|
|
13
|
-
green: "\x1b[32m",
|
|
14
|
-
yellow: "\x1b[33m",
|
|
15
|
-
red: "\x1b[31m",
|
|
16
|
-
cyan: "\x1b[36m",
|
|
17
|
-
bgGreen: "\x1b[42m",
|
|
18
|
-
bgYellow: "\x1b[43m",
|
|
19
|
-
bgRed: "\x1b[41m",
|
|
20
|
-
white: "\x1b[37m",
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
const CHECKLIST = [
|
|
24
|
-
{ id: "env", name: "Environment variables documented", category: "Config" },
|
|
25
|
-
{ id: "secrets", name: "No hardcoded secrets", category: "Security" },
|
|
26
|
-
{ id: "auth", name: "Authentication configured", category: "Security" },
|
|
27
|
-
{ id: "https", name: "HTTPS/TLS enabled", category: "Security" },
|
|
28
|
-
{ id: "cors", name: "CORS configured properly", category: "Security" },
|
|
29
|
-
{ id: "rate-limit", name: "Rate limiting enabled", category: "Security" },
|
|
30
|
-
{ id: "errors", name: "Error handling in place", category: "Reliability" },
|
|
31
|
-
{ id: "logging", name: "Logging configured", category: "Observability" },
|
|
32
|
-
{ id: "monitoring", name: "Monitoring/alerting set up", category: "Observability" },
|
|
33
|
-
{ id: "backup", name: "Database backup configured", category: "Data" },
|
|
34
|
-
{ id: "tests", name: "Tests passing", category: "Quality" },
|
|
35
|
-
{ id: "docs", name: "README updated", category: "Documentation" },
|
|
36
|
-
{ id: "changelog", name: "CHANGELOG updated", category: "Documentation" },
|
|
37
|
-
{ id: "version", name: "Version bumped", category: "Release" },
|
|
38
|
-
];
|
|
39
|
-
|
|
40
|
-
function parseArgs(args) {
|
|
41
|
-
const opts = {
|
|
42
|
-
help: false,
|
|
43
|
-
interactive: true,
|
|
44
|
-
json: false,
|
|
45
|
-
check: null,
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
for (let i = 0; i < args.length; i++) {
|
|
49
|
-
const arg = args[i];
|
|
50
|
-
switch (arg) {
|
|
51
|
-
case "--help":
|
|
52
|
-
case "-h":
|
|
53
|
-
opts.help = true;
|
|
54
|
-
break;
|
|
55
|
-
case "--json":
|
|
56
|
-
opts.json = true;
|
|
57
|
-
opts.interactive = false;
|
|
58
|
-
break;
|
|
59
|
-
case "--check":
|
|
60
|
-
opts.check = args[++i];
|
|
61
|
-
opts.interactive = false;
|
|
62
|
-
break;
|
|
63
|
-
case "--no-interactive":
|
|
64
|
-
opts.interactive = false;
|
|
65
|
-
break;
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
return opts;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
function printHelp() {
|
|
73
|
-
console.log(`
|
|
74
|
-
${c.bold}vibecheck launch${c.reset} - Pre-launch checklist wizard
|
|
75
|
-
|
|
76
|
-
${c.bold}USAGE${c.reset}
|
|
77
|
-
vibecheck launch [options]
|
|
78
|
-
|
|
79
|
-
${c.bold}OPTIONS${c.reset}
|
|
80
|
-
--help, -h Show this help
|
|
81
|
-
--json Output checklist as JSON
|
|
82
|
-
--check <id> Auto-check specific item
|
|
83
|
-
--no-interactive Non-interactive mode
|
|
84
|
-
|
|
85
|
-
${c.bold}CHECKLIST ITEMS${c.reset}
|
|
86
|
-
${CHECKLIST.map(item => ` ${item.id.padEnd(12)} ${item.name}`).join("\n")}
|
|
87
|
-
|
|
88
|
-
${c.bold}EXAMPLES${c.reset}
|
|
89
|
-
vibecheck launch # Interactive wizard
|
|
90
|
-
vibecheck launch --json # Get checklist as JSON
|
|
91
|
-
vibecheck launch --check env # Mark env as done
|
|
92
|
-
`);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
async function runInteractiveChecklist() {
|
|
96
|
-
const rl = readline.createInterface({
|
|
97
|
-
input: process.stdin,
|
|
98
|
-
output: process.stdout,
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
const ask = (q) => new Promise((resolve) => rl.question(q, resolve));
|
|
102
|
-
|
|
103
|
-
console.log(`
|
|
104
|
-
${c.bold}🚀 PRE-LAUNCH CHECKLIST${c.reset}
|
|
105
|
-
${c.dim}─────────────────────────────────────────${c.reset}
|
|
106
|
-
`);
|
|
107
|
-
|
|
108
|
-
const results = [];
|
|
109
|
-
let currentCategory = "";
|
|
110
|
-
|
|
111
|
-
for (const item of CHECKLIST) {
|
|
112
|
-
if (item.category !== currentCategory) {
|
|
113
|
-
currentCategory = item.category;
|
|
114
|
-
console.log(`\n${c.cyan}${c.bold}${currentCategory}${c.reset}`);
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
const answer = await ask(` ${item.name}? [y/n/s] `);
|
|
118
|
-
const status = answer.toLowerCase() === "y" ? "done" :
|
|
119
|
-
answer.toLowerCase() === "s" ? "skip" : "pending";
|
|
120
|
-
|
|
121
|
-
results.push({ ...item, status });
|
|
122
|
-
|
|
123
|
-
if (status === "done") {
|
|
124
|
-
process.stdout.write(`\x1b[1A\x1b[2K ${c.green}✓${c.reset} ${item.name}\n`);
|
|
125
|
-
} else if (status === "skip") {
|
|
126
|
-
process.stdout.write(`\x1b[1A\x1b[2K ${c.yellow}↷${c.reset} ${item.name} ${c.dim}(skipped)${c.reset}\n`);
|
|
127
|
-
} else {
|
|
128
|
-
process.stdout.write(`\x1b[1A\x1b[2K ${c.red}✗${c.reset} ${item.name}\n`);
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
rl.close();
|
|
133
|
-
|
|
134
|
-
// Summary
|
|
135
|
-
const done = results.filter(r => r.status === "done").length;
|
|
136
|
-
const pending = results.filter(r => r.status === "pending").length;
|
|
137
|
-
const skipped = results.filter(r => r.status === "skip").length;
|
|
138
|
-
|
|
139
|
-
console.log(`
|
|
140
|
-
${c.bold}─────────────────────────────────────────${c.reset}
|
|
141
|
-
${c.bold}SUMMARY${c.reset}
|
|
142
|
-
${c.green}✓ Done:${c.reset} ${done}
|
|
143
|
-
${c.red}✗ Pending:${c.reset} ${pending}
|
|
144
|
-
${c.yellow}↷ Skipped:${c.reset} ${skipped}
|
|
145
|
-
`);
|
|
146
|
-
|
|
147
|
-
if (pending === 0) {
|
|
148
|
-
console.log(`${c.bgGreen}${c.white}${c.bold} 🚀 READY TO LAUNCH! ${c.reset}\n`);
|
|
149
|
-
return 0;
|
|
150
|
-
} else {
|
|
151
|
-
console.log(`${c.bgYellow}${c.white}${c.bold} ⚠ ${pending} items remaining ${c.reset}\n`);
|
|
152
|
-
return 1;
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
async function runLaunch(args) {
|
|
157
|
-
const opts = parseArgs(args);
|
|
158
|
-
|
|
159
|
-
if (opts.help) {
|
|
160
|
-
printHelp();
|
|
161
|
-
return 0;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
if (opts.json) {
|
|
165
|
-
console.log(JSON.stringify({ checklist: CHECKLIST }, null, 2));
|
|
166
|
-
return 0;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
if (!opts.interactive) {
|
|
170
|
-
console.log(`\n${c.bold}Pre-Launch Checklist${c.reset}\n`);
|
|
171
|
-
for (const item of CHECKLIST) {
|
|
172
|
-
console.log(` [ ] ${item.name}`);
|
|
173
|
-
}
|
|
174
|
-
console.log(`\n${c.dim}Run 'vibecheck launch' for interactive mode${c.reset}\n`);
|
|
175
|
-
return 0;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
return runInteractiveChecklist();
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
module.exports = { runLaunch };
|
package/bin/runners/runPR.js
DELETED
|
@@ -1,255 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* vibecheck pr - Generate PR Comment from Ship Report
|
|
3
|
-
*
|
|
4
|
-
* ═══════════════════════════════════════════════════════════════════════════════
|
|
5
|
-
* ENTERPRISE EDITION - World-Class Terminal Experience
|
|
6
|
-
* ═══════════════════════════════════════════════════════════════════════════════
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
const fs = require("fs");
|
|
10
|
-
const path = require("path");
|
|
11
|
-
const { shipCore } = require("./runShip");
|
|
12
|
-
|
|
13
|
-
// ═══════════════════════════════════════════════════════════════════════════════
|
|
14
|
-
// ADVANCED TERMINAL - ANSI CODES & UTILITIES
|
|
15
|
-
// ═══════════════════════════════════════════════════════════════════════════════
|
|
16
|
-
|
|
17
|
-
const c = {
|
|
18
|
-
reset: '\x1b[0m',
|
|
19
|
-
bold: '\x1b[1m',
|
|
20
|
-
dim: '\x1b[2m',
|
|
21
|
-
italic: '\x1b[3m',
|
|
22
|
-
underline: '\x1b[4m',
|
|
23
|
-
red: '\x1b[31m',
|
|
24
|
-
green: '\x1b[32m',
|
|
25
|
-
yellow: '\x1b[33m',
|
|
26
|
-
blue: '\x1b[34m',
|
|
27
|
-
magenta: '\x1b[35m',
|
|
28
|
-
cyan: '\x1b[36m',
|
|
29
|
-
white: '\x1b[37m',
|
|
30
|
-
gray: '\x1b[90m',
|
|
31
|
-
clearLine: '\x1b[2K',
|
|
32
|
-
hideCursor: '\x1b[?25l',
|
|
33
|
-
showCursor: '\x1b[?25h',
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
const rgb = (r, g, b) => `\x1b[38;2;${r};${g};${b}m`;
|
|
37
|
-
const bgRgb = (r, g, b) => `\x1b[48;2;${r};${g};${b}m`;
|
|
38
|
-
|
|
39
|
-
const colors = {
|
|
40
|
-
gradient1: rgb(100, 150, 255),
|
|
41
|
-
gradient2: rgb(80, 130, 255),
|
|
42
|
-
gradient3: rgb(60, 110, 255),
|
|
43
|
-
shipGreen: rgb(0, 255, 150),
|
|
44
|
-
warnAmber: rgb(255, 200, 0),
|
|
45
|
-
blockRed: rgb(255, 80, 80),
|
|
46
|
-
accent: rgb(100, 150, 255),
|
|
47
|
-
muted: rgb(120, 120, 140),
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
// ═══════════════════════════════════════════════════════════════════════════════
|
|
51
|
-
// PREMIUM BANNER
|
|
52
|
-
// ═══════════════════════════════════════════════════════════════════════════════
|
|
53
|
-
|
|
54
|
-
const PR_BANNER = `
|
|
55
|
-
${rgb(100, 150, 255)} ██████╗ ██████╗ ${c.reset}
|
|
56
|
-
${rgb(80, 130, 255)} ██╔══██╗██╔══██╗${c.reset}
|
|
57
|
-
${rgb(60, 110, 255)} ██████╔╝██████╔╝${c.reset}
|
|
58
|
-
${rgb(40, 90, 255)} ██╔═══╝ ██╔══██╗${c.reset}
|
|
59
|
-
${rgb(20, 70, 255)} ██║ ██║ ██║${c.reset}
|
|
60
|
-
${rgb(0, 50, 255)} ╚═╝ ╚═╝ ╚═╝${c.reset}
|
|
61
|
-
`;
|
|
62
|
-
|
|
63
|
-
const BANNER_FULL = `
|
|
64
|
-
${rgb(100, 150, 255)} ██╗ ██╗██╗██████╗ ███████╗ ██████╗██╗ ██╗███████╗ ██████╗██╗ ██╗${c.reset}
|
|
65
|
-
${rgb(90, 140, 255)} ██║ ██║██║██╔══██╗██╔════╝██╔════╝██║ ██║██╔════╝██╔════╝██║ ██╔╝${c.reset}
|
|
66
|
-
${rgb(80, 130, 255)} ██║ ██║██║██████╔╝█████╗ ██║ ███████║█████╗ ██║ █████╔╝ ${c.reset}
|
|
67
|
-
${rgb(60, 110, 255)} ╚██╗ ██╔╝██║██╔══██╗██╔══╝ ██║ ██╔══██║██╔══╝ ██║ ██╔═██╗ ${c.reset}
|
|
68
|
-
${rgb(40, 90, 255)} ╚████╔╝ ██║██████╔╝███████╗╚██████╗██║ ██║███████╗╚██████╗██║ ██╗${c.reset}
|
|
69
|
-
${rgb(20, 70, 255)} ╚═══╝ ╚═╝╚═════╝ ╚══════╝ ╚═════╝╚═╝ ╚═╝╚══════╝ ╚═════╝╚═╝ ╚═╝${c.reset}
|
|
70
|
-
|
|
71
|
-
${c.dim} ┌─────────────────────────────────────────────────────────────────────┐${c.reset}
|
|
72
|
-
${c.dim} │${c.reset} ${rgb(100, 150, 255)}📋${c.reset} ${c.bold}PR${c.reset} ${c.dim}•${c.reset} ${rgb(200, 200, 200)}GitHub Comments${c.reset} ${c.dim}•${c.reset} ${rgb(150, 150, 150)}CI Integration${c.reset} ${c.dim}•${c.reset} ${rgb(100, 100, 100)}Ship Verdict${c.reset} ${c.dim}│${c.reset}
|
|
73
|
-
${c.dim} └─────────────────────────────────────────────────────────────────────┘${c.reset}
|
|
74
|
-
`;
|
|
75
|
-
|
|
76
|
-
const ICONS = {
|
|
77
|
-
pr: '📋',
|
|
78
|
-
check: '✓',
|
|
79
|
-
cross: '✗',
|
|
80
|
-
warning: '⚠',
|
|
81
|
-
arrow: '→',
|
|
82
|
-
bullet: '•',
|
|
83
|
-
ship: '🚀',
|
|
84
|
-
github: '🐙',
|
|
85
|
-
file: '📄',
|
|
86
|
-
sparkle: '✨',
|
|
87
|
-
};
|
|
88
|
-
|
|
89
|
-
function printDivider(char = '─', width = 69, color = c.dim) {
|
|
90
|
-
console.log(`${color} ${char.repeat(width)}${c.reset}`);
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
function printSection(title, icon = '◆') {
|
|
94
|
-
console.log();
|
|
95
|
-
console.log(` ${colors.accent}${icon}${c.reset} ${c.bold}${title}${c.reset}`);
|
|
96
|
-
printDivider();
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
function printHelp() {
|
|
100
|
-
console.log(BANNER_FULL);
|
|
101
|
-
console.log(`
|
|
102
|
-
${c.bold}Usage:${c.reset} vibecheck pr [options]
|
|
103
|
-
|
|
104
|
-
${c.bold}PR Comment Generator${c.reset} — Generate GitHub-ready markdown from ship analysis.
|
|
105
|
-
|
|
106
|
-
${c.bold}Options:${c.reset}
|
|
107
|
-
${colors.accent}--fastify-entry <path>${c.reset} Fastify entry file for route extraction
|
|
108
|
-
${colors.accent}--out <file>${c.reset} Write markdown to file
|
|
109
|
-
${colors.accent}--fail-on-warn${c.reset} Exit 1 on WARN verdict
|
|
110
|
-
${colors.accent}--max-findings <n>${c.reset} Max findings to show ${c.dim}(default: 12)${c.reset}
|
|
111
|
-
${colors.accent}--help, -h${c.reset} Show this help
|
|
112
|
-
|
|
113
|
-
${c.bold}What It Does:${c.reset}
|
|
114
|
-
${colors.shipGreen}1.${c.reset} Runs ship analysis (static + runtime)
|
|
115
|
-
${colors.shipGreen}2.${c.reset} Generates GitHub-ready markdown summary
|
|
116
|
-
${colors.shipGreen}3.${c.reset} Outputs PR comment body to stdout
|
|
117
|
-
|
|
118
|
-
${c.bold}Exit Codes:${c.reset}
|
|
119
|
-
${colors.shipGreen}0${c.reset} SHIP or WARN ${c.dim}(without --fail-on-warn)${c.reset}
|
|
120
|
-
${colors.warnAmber}1${c.reset} WARN ${c.dim}(with --fail-on-warn)${c.reset}
|
|
121
|
-
${colors.blockRed}2${c.reset} BLOCK
|
|
122
|
-
|
|
123
|
-
${c.bold}Examples:${c.reset}
|
|
124
|
-
${c.dim}# Print PR comment to stdout${c.reset}
|
|
125
|
-
vibecheck pr
|
|
126
|
-
|
|
127
|
-
${c.dim}# Save to file for GitHub Actions${c.reset}
|
|
128
|
-
vibecheck pr --out pr-comment.md
|
|
129
|
-
|
|
130
|
-
${c.dim}# Strict CI mode (fail on warnings)${c.reset}
|
|
131
|
-
vibecheck pr --fail-on-warn
|
|
132
|
-
`);
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
function ensureDir(p) {
|
|
136
|
-
fs.mkdirSync(p, { recursive: true });
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
function severityCounts(findings) {
|
|
140
|
-
const counts = { BLOCK: 0, WARN: 0, INFO: 0 };
|
|
141
|
-
for (const f of findings || []) {
|
|
142
|
-
if (f.severity === "BLOCK") counts.BLOCK++;
|
|
143
|
-
else if (f.severity === "WARN") counts.WARN++;
|
|
144
|
-
else counts.INFO++;
|
|
145
|
-
}
|
|
146
|
-
return counts;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
function toEmojiVerdict(v) {
|
|
150
|
-
if (v === "SHIP") return "✅ SHIP";
|
|
151
|
-
if (v === "WARN") return "⚠️ WARN";
|
|
152
|
-
return "🛑 BLOCK";
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
function buildPrMarkdown({ report, maxFindings = 12 } = {}) {
|
|
156
|
-
const verdict = report?.meta?.verdict || "unknown";
|
|
157
|
-
const findings = report?.findings || [];
|
|
158
|
-
const counts = severityCounts(findings);
|
|
159
|
-
|
|
160
|
-
const top = findings
|
|
161
|
-
.filter(f => f.severity === "BLOCK" || f.severity === "WARN")
|
|
162
|
-
.slice(0, maxFindings);
|
|
163
|
-
|
|
164
|
-
const lines = [];
|
|
165
|
-
lines.push(`## vibecheck — ${toEmojiVerdict(verdict)}`);
|
|
166
|
-
lines.push(``);
|
|
167
|
-
lines.push(`**Reality summary:** BLOCK=${counts.BLOCK} • WARN=${counts.WARN}`);
|
|
168
|
-
lines.push(``);
|
|
169
|
-
|
|
170
|
-
if (!top.length) {
|
|
171
|
-
lines.push(`✅ No blockers. Ship it.`);
|
|
172
|
-
return lines.join("\n");
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
lines.push(`### Top findings`);
|
|
176
|
-
for (const f of top) {
|
|
177
|
-
lines.push(`- **${f.severity}** \`${f.id}\` — ${f.title}`);
|
|
178
|
-
const ev = (f.evidence || [])[0];
|
|
179
|
-
if (ev?.file) {
|
|
180
|
-
lines.push(` - Evidence: \`${ev.file}:${ev.lines}\` (${ev.reason})`);
|
|
181
|
-
if (ev.snippetHash) lines.push(` - SnipHash: \`${ev.snippetHash}\``);
|
|
182
|
-
}
|
|
183
|
-
const hint = (f.fixHints || [])[0];
|
|
184
|
-
if (hint) lines.push(` - Fix: ${hint}`);
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
if (findings.length > top.length) {
|
|
188
|
-
lines.push(``);
|
|
189
|
-
lines.push(`_…and ${findings.length - top.length} more finding(s). See \`.vibecheck/last_ship.json\` for full details._`);
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
return lines.join("\n");
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
function exitCodeForVerdict(verdict, { failOnWarn = false } = {}) {
|
|
196
|
-
if (verdict === "SHIP") return 0;
|
|
197
|
-
if (verdict === "WARN") return failOnWarn ? 1 : 0;
|
|
198
|
-
return 2; // BLOCK
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
async function runPR(argsOrOpts = {}) {
|
|
202
|
-
// Handle array args from CLI
|
|
203
|
-
if (Array.isArray(argsOrOpts)) {
|
|
204
|
-
if (argsOrOpts.includes("--help") || argsOrOpts.includes("-h")) {
|
|
205
|
-
printHelp();
|
|
206
|
-
return 0;
|
|
207
|
-
}
|
|
208
|
-
const getArg = (flags) => {
|
|
209
|
-
for (const f of flags) {
|
|
210
|
-
const idx = argsOrOpts.indexOf(f);
|
|
211
|
-
if (idx !== -1 && idx < argsOrOpts.length - 1) return argsOrOpts[idx + 1];
|
|
212
|
-
}
|
|
213
|
-
return undefined;
|
|
214
|
-
};
|
|
215
|
-
argsOrOpts = {
|
|
216
|
-
repoRoot: process.cwd(),
|
|
217
|
-
fastifyEntry: getArg(["--fastify-entry"]),
|
|
218
|
-
out: getArg(["--out", "-o"]),
|
|
219
|
-
failOnWarn: argsOrOpts.includes("--fail-on-warn"),
|
|
220
|
-
maxFindings: parseInt(getArg(["--max-findings"]) || "12", 10),
|
|
221
|
-
};
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
// Check for help in object form (from main CLI)
|
|
225
|
-
if (argsOrOpts.help) {
|
|
226
|
-
printHelp();
|
|
227
|
-
return 0;
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
const {
|
|
231
|
-
repoRoot,
|
|
232
|
-
fastifyEntry,
|
|
233
|
-
out,
|
|
234
|
-
failOnWarn = false,
|
|
235
|
-
maxFindings = 12
|
|
236
|
-
} = argsOrOpts;
|
|
237
|
-
|
|
238
|
-
const root = repoRoot || process.cwd();
|
|
239
|
-
|
|
240
|
-
const { report, verdict } = await shipCore({ repoRoot: root, fastifyEntry, noWrite: false });
|
|
241
|
-
|
|
242
|
-
const md = buildPrMarkdown({ report, maxFindings });
|
|
243
|
-
|
|
244
|
-
if (out) {
|
|
245
|
-
const outAbs = path.isAbsolute(out) ? out : path.join(root, out);
|
|
246
|
-
ensureDir(path.dirname(outAbs));
|
|
247
|
-
fs.writeFileSync(outAbs, md, "utf8");
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
console.log(md);
|
|
251
|
-
|
|
252
|
-
process.exitCode = exitCodeForVerdict(verdict, { failOnWarn });
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
module.exports = { runPR };
|