@vibecheckai/cli 3.0.9 → 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.
@@ -1,4 +1,11 @@
1
- // bin/runners/runInstall.js
1
+ /**
2
+ * vibecheck install - Zero-friction Onboarding
3
+ *
4
+ * ═══════════════════════════════════════════════════════════════════════════════
5
+ * ENTERPRISE EDITION - World-Class Terminal Experience
6
+ * ═══════════════════════════════════════════════════════════════════════════════
7
+ */
8
+
2
9
  const fs = require("fs");
3
10
  const path = require("path");
4
11
  const { buildTruthpack, writeTruthpack } = require("./lib/truth");
@@ -6,34 +13,159 @@ const { detectPackageManager, detectNext, detectFastify, detectFastifyEntry } =
6
13
  const { readPkg, writePkg, upsertScripts } = require("./lib/pkgjson");
7
14
  const { writeEnvTemplateFromTruthpack } = require("./lib/env-template");
8
15
 
16
+ // ═══════════════════════════════════════════════════════════════════════════════
17
+ // ADVANCED TERMINAL - ANSI CODES & UTILITIES
18
+ // ═══════════════════════════════════════════════════════════════════════════════
19
+
20
+ const c = {
21
+ reset: '\x1b[0m',
22
+ bold: '\x1b[1m',
23
+ dim: '\x1b[2m',
24
+ italic: '\x1b[3m',
25
+ red: '\x1b[31m',
26
+ green: '\x1b[32m',
27
+ yellow: '\x1b[33m',
28
+ blue: '\x1b[34m',
29
+ magenta: '\x1b[35m',
30
+ cyan: '\x1b[36m',
31
+ white: '\x1b[37m',
32
+ gray: '\x1b[90m',
33
+ clearLine: '\x1b[2K',
34
+ hideCursor: '\x1b[?25l',
35
+ showCursor: '\x1b[?25h',
36
+ };
37
+
38
+ const rgb = (r, g, b) => `\x1b[38;2;${r};${g};${b}m`;
39
+
40
+ const colors = {
41
+ gradient1: rgb(255, 200, 100),
42
+ gradient2: rgb(255, 180, 80),
43
+ gradient3: rgb(255, 160, 60),
44
+ shipGreen: rgb(0, 255, 150),
45
+ warnAmber: rgb(255, 200, 0),
46
+ blockRed: rgb(255, 80, 80),
47
+ accent: rgb(255, 200, 100),
48
+ muted: rgb(120, 120, 140),
49
+ next: rgb(100, 200, 255),
50
+ fastify: rgb(255, 150, 200),
51
+ };
52
+
53
+ // ═══════════════════════════════════════════════════════════════════════════════
54
+ // PREMIUM BANNER
55
+ // ═══════════════════════════════════════════════════════════════════════════════
56
+
57
+ const INSTALL_BANNER = `
58
+ ${rgb(255, 200, 100)} ██╗███╗ ██╗███████╗████████╗ █████╗ ██╗ ██╗ ${c.reset}
59
+ ${rgb(255, 180, 80)} ██║████╗ ██║██╔════╝╚══██╔══╝██╔══██╗██║ ██║ ${c.reset}
60
+ ${rgb(255, 160, 60)} ██║██╔██╗ ██║███████╗ ██║ ███████║██║ ██║ ${c.reset}
61
+ ${rgb(255, 140, 40)} ██║██║╚██╗██║╚════██║ ██║ ██╔══██║██║ ██║ ${c.reset}
62
+ ${rgb(255, 120, 20)} ██║██║ ╚████║███████║ ██║ ██║ ██║███████╗███████╗${c.reset}
63
+ ${rgb(255, 100, 0)} ╚═╝╚═╝ ╚═══╝╚══════╝ ╚═╝ ╚═╝ ╚═╝╚══════╝╚══════╝${c.reset}
64
+ `;
65
+
66
+ const BANNER_FULL = `
67
+ ${rgb(255, 200, 100)} ██╗ ██╗██╗██████╗ ███████╗ ██████╗██╗ ██╗███████╗ ██████╗██╗ ██╗${c.reset}
68
+ ${rgb(255, 190, 90)} ██║ ██║██║██╔══██╗██╔════╝██╔════╝██║ ██║██╔════╝██╔════╝██║ ██╔╝${c.reset}
69
+ ${rgb(255, 180, 80)} ██║ ██║██║██████╔╝█████╗ ██║ ███████║█████╗ ██║ █████╔╝ ${c.reset}
70
+ ${rgb(255, 160, 60)} ╚██╗ ██╔╝██║██╔══██╗██╔══╝ ██║ ██╔══██║██╔══╝ ██║ ██╔═██╗ ${c.reset}
71
+ ${rgb(255, 140, 40)} ╚████╔╝ ██║██████╔╝███████╗╚██████╗██║ ██║███████╗╚██████╗██║ ██╗${c.reset}
72
+ ${rgb(255, 120, 20)} ╚═══╝ ╚═╝╚═════╝ ╚══════╝ ╚═════╝╚═╝ ╚═╝╚══════╝ ╚═════╝╚═╝ ╚═╝${c.reset}
73
+
74
+ ${c.dim} ┌─────────────────────────────────────────────────────────────────────┐${c.reset}
75
+ ${c.dim} │${c.reset} ${rgb(255, 200, 100)}📥${c.reset} ${c.bold}INSTALL${c.reset} ${c.dim}•${c.reset} ${rgb(200, 200, 200)}Zero-Friction${c.reset} ${c.dim}•${c.reset} ${rgb(150, 150, 150)}Auto-Detect${c.reset} ${c.dim}•${c.reset} ${rgb(100, 100, 100)}Onboarding${c.reset} ${c.dim}│${c.reset}
76
+ ${c.dim} └─────────────────────────────────────────────────────────────────────┘${c.reset}
77
+ `;
78
+
79
+ const ICONS = {
80
+ install: '📥',
81
+ package: '📦',
82
+ file: '📄',
83
+ check: '✓',
84
+ cross: '✗',
85
+ warning: '⚠',
86
+ arrow: '→',
87
+ bullet: '•',
88
+ sparkle: '✨',
89
+ folder: '📁',
90
+ next: '▲',
91
+ fastify: '⚡',
92
+ npm: '📦',
93
+ gear: '⚙️',
94
+ };
95
+
96
+ const SPINNER_DOTS = ['⣾', '⣽', '⣻', '⢿', '⡿', '⣟', '⣯', '⣷'];
97
+ let spinnerIndex = 0;
98
+ let spinnerInterval = null;
99
+ let spinnerStartTime = null;
100
+
101
+ function formatDuration(ms) {
102
+ if (ms < 1000) return `${ms}ms`;
103
+ return `${(ms / 1000).toFixed(1)}s`;
104
+ }
105
+
106
+ function startSpinner(message) {
107
+ spinnerStartTime = Date.now();
108
+ process.stdout.write(c.hideCursor);
109
+ spinnerInterval = setInterval(() => {
110
+ const elapsed = formatDuration(Date.now() - spinnerStartTime);
111
+ process.stdout.write(`\r${c.clearLine} ${colors.accent}${SPINNER_DOTS[spinnerIndex]}${c.reset} ${message} ${c.dim}${elapsed}${c.reset}`);
112
+ spinnerIndex = (spinnerIndex + 1) % SPINNER_DOTS.length;
113
+ }, 80);
114
+ }
115
+
116
+ function stopSpinner(message, success = true) {
117
+ if (spinnerInterval) {
118
+ clearInterval(spinnerInterval);
119
+ spinnerInterval = null;
120
+ }
121
+ const elapsed = spinnerStartTime ? formatDuration(Date.now() - spinnerStartTime) : '';
122
+ const icon = success ? `${colors.shipGreen}${ICONS.check}${c.reset}` : `${colors.blockRed}${ICONS.cross}${c.reset}`;
123
+ process.stdout.write(`\r${c.clearLine} ${icon} ${message} ${c.dim}${elapsed}${c.reset}\n`);
124
+ process.stdout.write(c.showCursor);
125
+ spinnerStartTime = null;
126
+ }
127
+
128
+ function printDivider(char = '─', width = 69, color = c.dim) {
129
+ console.log(`${color} ${char.repeat(width)}${c.reset}`);
130
+ }
131
+
132
+ function printSection(title, icon = '◆') {
133
+ console.log();
134
+ console.log(` ${colors.accent}${icon}${c.reset} ${c.bold}${title}${c.reset}`);
135
+ printDivider();
136
+ }
137
+
9
138
  function printHelp() {
139
+ console.log(BANNER_FULL);
10
140
  console.log(`
11
- vibecheck install - Zero-friction onboarding
12
-
13
- USAGE
14
- vibecheck install [options]
15
-
16
- OPTIONS
17
- --path, -p <dir> Project path (default: current directory)
18
- --help, -h Show this help
19
-
20
- WHAT IT DOES
21
- 1. Detects package manager (npm, yarn, pnpm)
22
- 2. Detects frameworks (Next.js, Fastify)
23
- 3. Builds initial truthpack
24
- 4. Creates .vibecheck/config.json
25
- 5. Generates env template from truthpack
26
- 6. Adds vibecheck scripts to package.json
27
-
28
- CREATED FILES
29
- .vibecheck/config.json - Local configuration
30
- .vibecheck/truthpack.json - Ground truth for AI agents
31
- .env.template - Env vars from truthpack
32
-
33
- EXAMPLES
34
- vibecheck install # Install in current directory
35
- vibecheck install --path ./my-app # Install in specific directory
36
- `);
141
+ ${c.bold}Usage:${c.reset} vibecheck install [options]
142
+
143
+ ${c.bold}Zero-Friction Onboarding${c.reset} — Auto-detect and configure your project.
144
+
145
+ ${c.bold}Options:${c.reset}
146
+ ${colors.accent}--path, -p <dir>${c.reset} Project path ${c.dim}(default: current directory)${c.reset}
147
+ ${colors.accent}--help, -h${c.reset} Show this help
148
+
149
+ ${c.bold}What It Does:${c.reset}
150
+ ${colors.shipGreen}1.${c.reset} Detects package manager ${c.dim}(npm, yarn, pnpm)${c.reset}
151
+ ${colors.shipGreen}2.${c.reset} Detects frameworks ${c.dim}(Next.js, Fastify)${c.reset}
152
+ ${colors.shipGreen}3.${c.reset} Builds initial truthpack
153
+ ${colors.shipGreen}4.${c.reset} Creates .vibecheck/config.json
154
+ ${colors.shipGreen}5.${c.reset} Generates env template from truthpack
155
+ ${colors.shipGreen}6.${c.reset} Adds vibecheck scripts to package.json
156
+
157
+ ${c.bold}Created Files:${c.reset}
158
+ ${ICONS.file} ${colors.accent}.vibecheck/config.json${c.reset} ${c.dim}Local configuration${c.reset}
159
+ ${ICONS.file} ${colors.accent}.vibecheck/truthpack.json${c.reset} ${c.dim}Ground truth for AI agents${c.reset}
160
+ ${ICONS.file} ${colors.accent}.env.template${c.reset} ${c.dim}Env vars from truthpack${c.reset}
161
+
162
+ ${c.bold}Examples:${c.reset}
163
+ ${c.dim}# Install in current directory${c.reset}
164
+ vibecheck install
165
+
166
+ ${c.dim}# Install in specific directory${c.reset}
167
+ vibecheck install --path ./my-app
168
+ `);
37
169
  }
38
170
 
39
171
  function ensureDir(p) {
@@ -52,20 +184,32 @@ async function runInstall(argsOrContext = {}) {
52
184
 
53
185
  const { repoRoot } = argsOrContext;
54
186
  const root = repoRoot || process.cwd();
187
+ const projectName = path.basename(root);
188
+
189
+ // Print banner
190
+ console.log(BANNER_FULL);
191
+ console.log(` ${c.dim}Project:${c.reset} ${c.bold}${projectName}${c.reset}`);
192
+ console.log(` ${c.dim}Path:${c.reset} ${root}`);
193
+ console.log();
55
194
 
56
195
  const { path: pkgPath, json: pkg } = readPkg(root);
57
196
 
197
+ // Detect environment
198
+ startSpinner('Detecting environment...');
58
199
  const pm = detectPackageManager(root);
59
200
  const next = detectNext(root, pkg);
60
201
  const fastify = detectFastify(root, pkg);
61
-
62
202
  const fastifyEntry = fastify.enabled ? await detectFastifyEntry(root) : null;
203
+ stopSpinner('Environment detected', true);
63
204
 
64
- // Build truthpack once (this powers env template + ctx baseline)
205
+ // Build truthpack
206
+ startSpinner('Building truthpack...');
65
207
  const truthpack = await buildTruthpack({ repoRoot: root, fastifyEntry: fastifyEntry || undefined });
66
208
  writeTruthpack(root, truthpack);
209
+ stopSpinner('Truthpack built', true);
67
210
 
68
- // Write minimal local config
211
+ // Write config
212
+ startSpinner('Writing configuration...');
69
213
  const cfgDir = path.join(root, ".vibecheck");
70
214
  ensureDir(cfgDir);
71
215
 
@@ -80,37 +224,54 @@ async function runInstall(argsOrContext = {}) {
80
224
  };
81
225
 
82
226
  fs.writeFileSync(path.join(cfgDir, "config.json"), JSON.stringify(cfg, null, 2), "utf8");
227
+ stopSpinner('Configuration written', true);
83
228
 
84
- // Generate/extend env template from reality
229
+ // Generate env template
230
+ startSpinner('Generating env template...');
85
231
  const envRes = writeEnvTemplateFromTruthpack(root, truthpack);
232
+ stopSpinner(envRes.wrote ? `Env template generated (+${envRes.added.length} vars)` : 'Env template up to date', true);
86
233
 
87
- // Add scripts (vibecoder-friendly)
234
+ // Add scripts
235
+ startSpinner('Updating package.json scripts...');
88
236
  const scriptsToAdd = {
89
237
  "vibecheck:ctx": "vibecheck ctx",
90
238
  "vibecheck:ship": "vibecheck ship",
91
239
  "vibecheck:fix": "vibecheck fix --apply",
92
240
  "vibecheck:pr": "vibecheck pr"
93
241
  };
94
-
95
242
  const changedScripts = upsertScripts(pkg, scriptsToAdd);
96
243
  if (changedScripts.length) writePkg(pkgPath, pkg);
244
+ stopSpinner(changedScripts.length ? `Added ${changedScripts.length} scripts` : 'Scripts up to date', true);
97
245
 
98
- // Output (one next command)
99
- console.log(`\n✅ vibecheck install`);
100
- console.log(`Detected package manager: ${pm}`);
101
- console.log(`Detected Next: ${next.enabled ? "yes" : "no"}`);
102
- console.log(`Detected Fastify: ${fastify.enabled ? "yes" : "no"}`);
103
- if (fastifyEntry) console.log(`Fastify entry: ${fastifyEntry}`);
246
+ // Summary
247
+ printSection('DETECTED', ICONS.gear);
248
+ console.log();
249
+ console.log(` ${ICONS.npm} ${c.bold}Package Manager:${c.reset} ${colors.accent}${pm}${c.reset}`);
250
+ console.log(` ${colors.next}${ICONS.next}${c.reset} ${c.bold}Next.js:${c.reset} ${next.enabled ? `${colors.shipGreen}yes${c.reset}` : `${c.dim}no${c.reset}`}`);
251
+ console.log(` ${colors.fastify}${ICONS.fastify}${c.reset} ${c.bold}Fastify:${c.reset} ${fastify.enabled ? `${colors.shipGreen}yes${c.reset}` : `${c.dim}no${c.reset}`}`);
252
+ if (fastifyEntry) {
253
+ console.log(` ${c.dim} Entry:${c.reset} ${colors.accent}${fastifyEntry}${c.reset}`);
254
+ }
104
255
 
105
- console.log(`\nWrote:`);
106
- console.log(`- .vibecheck/config.json`);
107
- console.log(`- .vibecheck/truthpack.json`);
108
- if (envRes.wrote) console.log(`- ${envRes.outRel} (+${envRes.added.length} var(s))`);
109
- if (changedScripts.length) console.log(`- package.json scripts: ${changedScripts.join(", ")}`);
256
+ printSection('CREATED FILES', ICONS.folder);
257
+ console.log();
258
+ console.log(` ${colors.shipGreen}${ICONS.check}${c.reset} ${ICONS.file} ${colors.accent}.vibecheck/config.json${c.reset}`);
259
+ console.log(` ${colors.shipGreen}${ICONS.check}${c.reset} ${ICONS.file} ${colors.accent}.vibecheck/truthpack.json${c.reset}`);
260
+ if (envRes.wrote) {
261
+ console.log(` ${colors.shipGreen}${ICONS.check}${c.reset} ${ICONS.file} ${colors.accent}${envRes.outRel}${c.reset} ${c.dim}(+${envRes.added.length} vars)${c.reset}`);
262
+ }
263
+ if (changedScripts.length) {
264
+ console.log(` ${colors.shipGreen}${ICONS.check}${c.reset} ${ICONS.file} ${colors.accent}package.json${c.reset} ${c.dim}(${changedScripts.join(', ')})${c.reset}`);
265
+ }
110
266
 
267
+ // Next steps
111
268
  const runner = pm === "pnpm" ? "pnpm" : pm === "yarn" ? "yarn" : "npm run";
112
- console.log(`\nNext (do this):`);
113
- console.log(` ${runner} vibecheck:ship`);
269
+ printSection('NEXT STEP', ICONS.arrow);
270
+ console.log();
271
+ console.log(` ${colors.accent}${runner} vibecheck:ship${c.reset}`);
272
+ console.log();
273
+ console.log(` ${colors.shipGreen}${ICONS.sparkle}${c.reset} Installation complete!`);
274
+ console.log();
114
275
  }
115
276
 
116
277
  module.exports = { runInstall };
@@ -1,44 +1,135 @@
1
- // bin/runners/runPR.js
1
+ /**
2
+ * vibecheck pr - Generate PR Comment from Ship Report
3
+ *
4
+ * ═══════════════════════════════════════════════════════════════════════════════
5
+ * ENTERPRISE EDITION - World-Class Terminal Experience
6
+ * ═══════════════════════════════════════════════════════════════════════════════
7
+ */
8
+
2
9
  const fs = require("fs");
3
10
  const path = require("path");
4
11
  const { shipCore } = require("./runShip");
5
12
 
13
+ // ═══════════════════════════════════════════════════════════════════════════════
14
+ // ADVANCED TERMINAL - ANSI CODES & UTILITIES
15
+ // ═══════════════════════════════════════════════════════════════════════════════
16
+
6
17
  const c = {
7
- reset: "\x1b[0m",
8
- bold: "\x1b[1m",
9
- dim: "\x1b[2m",
10
- cyan: "\x1b[36m",
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',
11
34
  };
12
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
+
13
99
  function printHelp() {
100
+ console.log(BANNER_FULL);
14
101
  console.log(`
15
- ${c.cyan}${c.bold}vibecheck pr${c.reset} - Generate PR comment from ship report
16
-
17
- ${c.bold}USAGE${c.reset}
18
- vibecheck pr [options]
19
-
20
- ${c.bold}OPTIONS${c.reset}
21
- --fastify-entry <path> Fastify entry file for route extraction
22
- --out <file> Write markdown to file
23
- --fail-on-warn Exit 1 on WARN verdict
24
- --max-findings <n> Max findings to show (default: 12)
25
- --help, -h Show this help
26
-
27
- ${c.bold}WHAT IT DOES${c.reset}
28
- 1. Runs ship analysis (static + runtime)
29
- 2. Generates GitHub-ready markdown summary
30
- 3. Outputs PR comment body to stdout
31
-
32
- ${c.bold}EXIT CODES${c.reset}
33
- 0 = SHIP or WARN (without --fail-on-warn)
34
- 1 = WARN (with --fail-on-warn)
35
- 2 = BLOCK
36
-
37
- ${c.bold}EXAMPLES${c.reset}
38
- vibecheck pr # Print PR comment
39
- vibecheck pr --out pr-comment.md # Save to file
40
- vibecheck pr --fail-on-warn # Strict CI mode
41
- `);
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
+ `);
42
133
  }
43
134
 
44
135
  function ensureDir(p) {