@vibecheckai/cli 3.0.9 → 3.1.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.
@@ -0,0 +1,272 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * runVerify.js - CLI Verify Command
4
+ * Verifies AI-generated agent output against vibecheck-v1 protocol
5
+ *
6
+ * Usage:
7
+ * vibecheck verify [options]
8
+ * vibecheck verify --input response.json
9
+ * vibecheck verify --stdin
10
+ * vibecheck verify --mode ship --strict
11
+ */
12
+
13
+ "use strict";
14
+
15
+ const fs = require('fs');
16
+ const path = require('path');
17
+ const { parseArgs } = require('util');
18
+
19
+ const HELP = `
20
+ vibecheck verify - Verify AI-generated agent output
21
+
22
+ Usage:
23
+ vibecheck verify [options]
24
+
25
+ Options:
26
+ --input, -i <file> Input file containing agent response JSON
27
+ --stdin Read agent response from stdin
28
+ --root, -r <dir> Project root directory (default: cwd)
29
+ --mode, -m <mode> Verification mode: explore|build|ship (default: build)
30
+ --strict Enable strict mode (fail on warnings)
31
+ --json Output results as JSON
32
+ --help, -h Show this help message
33
+
34
+ Examples:
35
+ vibecheck verify --input agent-response.json
36
+ echo '{"format":"vibecheck-v1",...}' | vibecheck verify --stdin
37
+ vibecheck verify --input response.json --mode ship --strict
38
+ `;
39
+
40
+ async function main() {
41
+ const { values, positionals } = parseArgs({
42
+ options: {
43
+ input: { type: 'string', short: 'i' },
44
+ stdin: { type: 'boolean', default: false },
45
+ root: { type: 'string', short: 'r' },
46
+ mode: { type: 'string', short: 'm', default: 'build' },
47
+ strict: { type: 'boolean', default: false },
48
+ json: { type: 'boolean', default: false },
49
+ help: { type: 'boolean', short: 'h' },
50
+ },
51
+ allowPositionals: true,
52
+ strict: false,
53
+ });
54
+
55
+ if (values.help) {
56
+ console.log(HELP);
57
+ process.exit(0);
58
+ }
59
+
60
+ const projectRoot = values.root ? path.resolve(values.root) : process.cwd();
61
+ const mode = values.mode || 'build';
62
+ const strict = values.strict || false;
63
+ const outputJson = values.json || false;
64
+
65
+ // Validate mode
66
+ if (!['explore', 'build', 'ship'].includes(mode)) {
67
+ console.error(`Error: Invalid mode "${mode}". Must be one of: explore, build, ship`);
68
+ process.exit(2);
69
+ }
70
+
71
+ // Get input
72
+ let rawInput = '';
73
+
74
+ if (values.stdin) {
75
+ rawInput = await readStdin();
76
+ } else if (values.input) {
77
+ const inputPath = path.resolve(values.input);
78
+ if (!fs.existsSync(inputPath)) {
79
+ console.error(`Error: Input file not found: ${inputPath}`);
80
+ process.exit(2);
81
+ }
82
+ rawInput = fs.readFileSync(inputPath, 'utf-8');
83
+ } else if (positionals.length > 0) {
84
+ // Allow positional argument as input file
85
+ const inputPath = path.resolve(positionals[0]);
86
+ if (!fs.existsSync(inputPath)) {
87
+ console.error(`Error: Input file not found: ${inputPath}`);
88
+ process.exit(2);
89
+ }
90
+ rawInput = fs.readFileSync(inputPath, 'utf-8');
91
+ } else {
92
+ console.error('Error: No input provided. Use --input <file> or --stdin');
93
+ console.log(HELP);
94
+ process.exit(2);
95
+ }
96
+
97
+ if (!rawInput.trim()) {
98
+ console.error('Error: Empty input');
99
+ process.exit(2);
100
+ }
101
+
102
+ // Load the verification module
103
+ let validateFormat, verifyAgentOutput, formatCheckResults, buildJsonReport;
104
+ try {
105
+ // Try loading from the built TypeScript output
106
+ const verification = require('../../dist/lib/verification/index.js');
107
+ validateFormat = verification.validateFormat;
108
+ verifyAgentOutput = verification.verifyAgentOutput;
109
+ formatCheckResults = verification.formatCheckResults;
110
+ buildJsonReport = verification.buildJsonReport;
111
+ } catch (err) {
112
+ // Fallback: try loading directly (for development)
113
+ try {
114
+ const verification = require('../../src/lib/verification/index.ts');
115
+ validateFormat = verification.validateFormat;
116
+ verifyAgentOutput = verification.verifyAgentOutput;
117
+ formatCheckResults = verification.formatCheckResults;
118
+ buildJsonReport = verification.buildJsonReport;
119
+ } catch (err2) {
120
+ console.error('Error loading verification module:', err.message);
121
+ console.error('Make sure the project is built: pnpm build');
122
+ process.exit(2);
123
+ }
124
+ }
125
+
126
+ // Step 1: Validate format
127
+ if (!outputJson) {
128
+ console.log('🔍 Validating agent output format...');
129
+ }
130
+
131
+ const formatResult = validateFormat(rawInput);
132
+
133
+ if (!formatResult.valid) {
134
+ if (outputJson) {
135
+ console.log(JSON.stringify({
136
+ status: 'fail',
137
+ phase: 'format-validation',
138
+ error: formatResult.error,
139
+ retryPrompt: formatResult.retryPrompt,
140
+ }, null, 2));
141
+ } else {
142
+ console.error('❌ Format validation failed:', formatResult.error);
143
+ console.error('\n📝 Retry prompt:');
144
+ console.error(formatResult.retryPrompt);
145
+ }
146
+ process.exit(2);
147
+ }
148
+
149
+ // Check if it's an error response
150
+ if ('error' in formatResult && formatResult.error) {
151
+ if (outputJson) {
152
+ console.log(JSON.stringify({
153
+ status: 'error',
154
+ phase: 'agent-error',
155
+ error: formatResult.error.error,
156
+ }, null, 2));
157
+ } else {
158
+ console.log('⚠️ Agent returned an error response:');
159
+ console.log(formatResult.error.error);
160
+ }
161
+ process.exit(1);
162
+ }
163
+
164
+ const agentOutput = formatResult.output;
165
+
166
+ if (!outputJson) {
167
+ console.log('✅ Format valid');
168
+ console.log(` Files in diff: ${countFilesInDiff(agentOutput.diff)}`);
169
+ console.log(` Commands: ${agentOutput.commands?.length || 0}`);
170
+ console.log(` Tests: ${agentOutput.tests?.length || 0}`);
171
+ console.log('');
172
+ }
173
+
174
+ // Step 2: Run verification pipeline
175
+ if (!outputJson) {
176
+ console.log('🔬 Running verification pipeline...');
177
+ }
178
+
179
+ const context = {
180
+ projectRoot,
181
+ mode,
182
+ runTests: mode === 'ship',
183
+ strictMode: strict,
184
+ maxFilesChanged: mode === 'ship' ? 10 : mode === 'build' ? 20 : 50,
185
+ };
186
+
187
+ let result;
188
+ try {
189
+ result = await verifyAgentOutput(agentOutput, context);
190
+ } catch (err) {
191
+ if (outputJson) {
192
+ console.log(JSON.stringify({
193
+ status: 'fail',
194
+ phase: 'verification',
195
+ error: err.message,
196
+ }, null, 2));
197
+ } else {
198
+ console.error('❌ Verification error:', err.message);
199
+ }
200
+ process.exit(2);
201
+ }
202
+
203
+ // Output results
204
+ if (outputJson) {
205
+ console.log(JSON.stringify(buildJsonReport(result), null, 2));
206
+ } else {
207
+ console.log('');
208
+ console.log(formatCheckResults(result.checks));
209
+ console.log('');
210
+
211
+ if (result.status === 'pass') {
212
+ console.log('✅ VERIFICATION PASSED');
213
+ } else if (result.status === 'warn') {
214
+ console.log('⚠️ VERIFICATION PASSED WITH WARNINGS');
215
+ } else {
216
+ console.log('❌ VERIFICATION FAILED');
217
+ if (result.failureContext) {
218
+ console.log('\n📝 Failure context for retry:');
219
+ console.log(result.failureContext);
220
+ }
221
+ }
222
+ }
223
+
224
+ // Exit codes: 0 = pass, 1 = warn (if strict), 2 = fail
225
+ if (result.status === 'fail') {
226
+ process.exit(2);
227
+ } else if (result.status === 'warn' && strict) {
228
+ process.exit(1);
229
+ } else {
230
+ process.exit(0);
231
+ }
232
+ }
233
+
234
+ /**
235
+ * Read all input from stdin
236
+ */
237
+ function readStdin() {
238
+ return new Promise((resolve, reject) => {
239
+ let data = '';
240
+ process.stdin.setEncoding('utf-8');
241
+ process.stdin.on('data', chunk => { data += chunk; });
242
+ process.stdin.on('end', () => resolve(data));
243
+ process.stdin.on('error', reject);
244
+
245
+ // Timeout after 5 seconds if no input
246
+ setTimeout(() => {
247
+ if (data === '') {
248
+ reject(new Error('No input received from stdin'));
249
+ }
250
+ }, 5000);
251
+ });
252
+ }
253
+
254
+ /**
255
+ * Count files in a diff string
256
+ */
257
+ function countFilesInDiff(diff) {
258
+ if (!diff) return 0;
259
+ const matches = diff.match(/^diff --git/gm);
260
+ return matches ? matches.length : 0;
261
+ }
262
+
263
+ // Export for CLI registry
264
+ module.exports = { main };
265
+
266
+ // Run if executed directly
267
+ if (require.main === module) {
268
+ main().catch(err => {
269
+ console.error('Fatal error:', err.message);
270
+ process.exit(2);
271
+ });
272
+ }
@@ -1,6 +1,10 @@
1
1
  /**
2
2
  * vibecheck watch - Continuous Dev Mode
3
3
  *
4
+ * ═══════════════════════════════════════════════════════════════════════════════
5
+ * ENTERPRISE EDITION - World-Class Terminal Experience
6
+ * ═══════════════════════════════════════════════════════════════════════════════
7
+ *
4
8
  * Watches for file changes and re-runs ship automatically.
5
9
  * Shows a persistent status line with current verdict.
6
10
  * Perfect for development workflow.
@@ -12,16 +16,92 @@ const fs = require("fs");
12
16
  const path = require("path");
13
17
  const { shipCore } = require("./runShip");
14
18
 
19
+ // ═══════════════════════════════════════════════════════════════════════════════
20
+ // ADVANCED TERMINAL - ANSI CODES & UTILITIES
21
+ // ═══════════════════════════════════════════════════════════════════════════════
22
+
15
23
  const c = {
16
- reset: "\x1b[0m",
17
- bold: "\x1b[1m",
18
- dim: "\x1b[2m",
19
- red: "\x1b[31m",
20
- green: "\x1b[32m",
21
- yellow: "\x1b[33m",
22
- cyan: "\x1b[36m",
23
- blue: "\x1b[34m",
24
- clear: "\x1b[2J\x1b[H",
24
+ reset: '\x1b[0m',
25
+ bold: '\x1b[1m',
26
+ dim: '\x1b[2m',
27
+ italic: '\x1b[3m',
28
+ underline: '\x1b[4m',
29
+ red: '\x1b[31m',
30
+ green: '\x1b[32m',
31
+ yellow: '\x1b[33m',
32
+ blue: '\x1b[34m',
33
+ magenta: '\x1b[35m',
34
+ cyan: '\x1b[36m',
35
+ white: '\x1b[37m',
36
+ gray: '\x1b[90m',
37
+ clear: '\x1b[2J\x1b[H',
38
+ clearLine: '\x1b[2K',
39
+ hideCursor: '\x1b[?25l',
40
+ showCursor: '\x1b[?25h',
41
+ };
42
+
43
+ const rgb = (r, g, b) => `\x1b[38;2;${r};${g};${b}m`;
44
+ const bgRgb = (r, g, b) => `\x1b[48;2;${r};${g};${b}m`;
45
+
46
+ const colors = {
47
+ gradient1: rgb(150, 100, 255),
48
+ gradient2: rgb(130, 80, 255),
49
+ gradient3: rgb(110, 60, 255),
50
+ shipGreen: rgb(0, 255, 150),
51
+ warnAmber: rgb(255, 200, 0),
52
+ blockRed: rgb(255, 80, 80),
53
+ accent: rgb(150, 100, 255),
54
+ muted: rgb(120, 120, 140),
55
+ watching: rgb(100, 200, 255),
56
+ };
57
+
58
+ // ═══════════════════════════════════════════════════════════════════════════════
59
+ // PREMIUM BANNER
60
+ // ═══════════════════════════════════════════════════════════════════════════════
61
+
62
+ const WATCH_BANNER = `
63
+ ${rgb(150, 100, 255)} ██╗ ██╗ █████╗ ████████╗ ██████╗██╗ ██╗${c.reset}
64
+ ${rgb(130, 80, 255)} ██║ ██║██╔══██╗╚══██╔══╝██╔════╝██║ ██║${c.reset}
65
+ ${rgb(110, 60, 255)} ██║ █╗ ██║███████║ ██║ ██║ ███████║${c.reset}
66
+ ${rgb(90, 40, 255)} ██║███╗██║██╔══██║ ██║ ██║ ██╔══██║${c.reset}
67
+ ${rgb(70, 20, 255)} ╚███╔███╔╝██║ ██║ ██║ ╚██████╗██║ ██║${c.reset}
68
+ ${rgb(50, 0, 255)} ╚══╝╚══╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝╚═╝ ╚═╝${c.reset}
69
+ `;
70
+
71
+ const BANNER_FULL = `
72
+ ${rgb(150, 100, 255)} ██╗ ██╗██╗██████╗ ███████╗ ██████╗██╗ ██╗███████╗ ██████╗██╗ ██╗${c.reset}
73
+ ${rgb(140, 90, 255)} ██║ ██║██║██╔══██╗██╔════╝██╔════╝██║ ██║██╔════╝██╔════╝██║ ██╔╝${c.reset}
74
+ ${rgb(130, 80, 255)} ██║ ██║██║██████╔╝█████╗ ██║ ███████║█████╗ ██║ █████╔╝ ${c.reset}
75
+ ${rgb(110, 60, 255)} ╚██╗ ██╔╝██║██╔══██╗██╔══╝ ██║ ██╔══██║██╔══╝ ██║ ██╔═██╗ ${c.reset}
76
+ ${rgb(90, 40, 255)} ╚████╔╝ ██║██████╔╝███████╗╚██████╗██║ ██║███████╗╚██████╗██║ ██╗${c.reset}
77
+ ${rgb(70, 20, 255)} ╚═══╝ ╚═╝╚═════╝ ╚══════╝ ╚═════╝╚═╝ ╚═╝╚══════╝ ╚═════╝╚═╝ ╚═╝${c.reset}
78
+
79
+ ${c.dim} ┌─────────────────────────────────────────────────────────────────────┐${c.reset}
80
+ ${c.dim} │${c.reset} ${rgb(150, 100, 255)}👁️${c.reset} ${c.bold}WATCH${c.reset} ${c.dim}•${c.reset} ${rgb(200, 200, 200)}Live Reload${c.reset} ${c.dim}•${c.reset} ${rgb(150, 150, 150)}Auto Analysis${c.reset} ${c.dim}•${c.reset} ${rgb(100, 100, 100)}Dev Mode${c.reset} ${c.dim}│${c.reset}
81
+ ${c.dim} └─────────────────────────────────────────────────────────────────────┘${c.reset}
82
+ `;
83
+
84
+ const BOX = {
85
+ topLeft: '╭', topRight: '╮', bottomLeft: '╰', bottomRight: '╯',
86
+ horizontal: '─', vertical: '│',
87
+ dTopLeft: '╔', dTopRight: '╗', dBottomLeft: '╚', dBottomRight: '╝',
88
+ dHorizontal: '═', dVertical: '║',
89
+ };
90
+
91
+ const ICONS = {
92
+ eye: '👁️',
93
+ check: '✓',
94
+ cross: '✗',
95
+ warning: '⚠',
96
+ arrow: '→',
97
+ bullet: '•',
98
+ ship: '🚀',
99
+ clock: '⏱',
100
+ file: '📄',
101
+ sparkle: '✨',
102
+ lightning: '⚡',
103
+ refresh: '🔄',
104
+ watching: '◉',
25
105
  };
26
106
 
27
107
  const IGNORE_PATTERNS = [
@@ -58,29 +138,63 @@ function formatDuration(ms) {
58
138
  return `${(ms / 1000).toFixed(1)}s`;
59
139
  }
60
140
 
141
+ function progressBar(percent, width = 20) {
142
+ const filled = Math.round((percent / 100) * width);
143
+ const empty = width - filled;
144
+ const color = percent >= 80 ? colors.shipGreen : percent >= 50 ? colors.warnAmber : colors.blockRed;
145
+ return `${color}${'█'.repeat(filled)}${c.dim}${'░'.repeat(empty)}${c.reset}`;
146
+ }
147
+
148
+ function printDivider(char = '─', width = 62) {
149
+ console.log(` ${c.dim}${char.repeat(width)}${c.reset}`);
150
+ }
151
+
61
152
  function printStatus({ verdict, findings, duration, lastFile, runCount }) {
62
- const verdictColor = verdict === "SHIP" ? c.green : verdict === "WARN" ? c.yellow : c.red;
153
+ const verdictColor = verdict === "SHIP" ? colors.shipGreen : verdict === "WARN" ? colors.warnAmber : colors.blockRed;
63
154
  const blocks = findings.filter(f => f.severity === "BLOCK").length;
64
155
  const warns = findings.filter(f => f.severity === "WARN").length;
65
-
66
- console.log(`
67
- ${c.cyan}${c.bold}╔════════════════════════════════════════════════════════════╗
68
- 👁️ vibecheck watch ║
69
- ╚════════════════════════════════════════════════════════════╝${c.reset}
70
-
71
- ${c.dim}Time:${c.reset} ${formatTime()}
72
- ${c.dim}Runs:${c.reset} ${runCount}
73
- ${c.dim}Duration:${c.reset} ${formatDuration(duration)}
74
- ${c.dim}Changed:${c.reset} ${lastFile || "(initial)"}
75
-
76
- ${c.bold} VERDICT: ${verdictColor}${verdict}${c.reset}
156
+ const total = blocks + warns;
157
+ const health = total === 0 ? 100 : Math.max(0, 100 - (blocks * 20) - (warns * 5));
158
+
159
+ console.log();
160
+ console.log(` ${c.dim}${BOX.dTopLeft}${BOX.dHorizontal.repeat(62)}${BOX.dTopRight}${c.reset}`);
161
+ console.log(` ${c.dim}${BOX.dVertical}${c.reset} ${colors.watching}${ICONS.eye}${c.reset} ${c.bold}VIBECHECK WATCH${c.reset} ${c.dim}${BOX.dVertical}${c.reset}`);
162
+ console.log(` ${c.dim}${BOX.dVertical}${BOX.dHorizontal.repeat(62)}${BOX.dVertical}${c.reset}`);
163
+ console.log(` ${c.dim}${BOX.dVertical}${c.reset} ${c.dim}${BOX.dVertical}${c.reset}`);
77
164
 
78
- ${c.dim}Findings:${c.reset} ${blocks} BLOCK, ${warns} WARN
79
-
80
- ${verdict === "SHIP" ? ` ${c.green}✓ Ready to ship${c.reset}` : ` ${c.yellow}⚠ Run "vibecheck fix" to resolve${c.reset}`}
81
-
82
- ${c.dim} Watching for changes... (Ctrl+C to stop)${c.reset}
83
- `);
165
+ // Stats row
166
+ console.log(` ${c.dim}${BOX.dVertical}${c.reset} ${c.dim}${ICONS.clock} Time:${c.reset} ${formatTime()} ${c.dim}${ICONS.refresh} Runs:${c.reset} ${runCount} ${c.dim}${ICONS.lightning} Duration:${c.reset} ${formatDuration(duration)} ${c.dim}${BOX.dVertical}${c.reset}`);
167
+
168
+ // Changed file
169
+ const changedDisplay = lastFile ? lastFile.slice(0, 40) : '(initial scan)';
170
+ console.log(` ${c.dim}${BOX.dVertical}${c.reset} ${c.dim}${ICONS.file} Changed:${c.reset} ${colors.accent}${changedDisplay}${c.reset}${' '.repeat(Math.max(0, 45 - changedDisplay.length))}${c.dim}${BOX.dVertical}${c.reset}`);
171
+
172
+ console.log(` ${c.dim}${BOX.dVertical}${c.reset} ${c.dim}${BOX.dVertical}${c.reset}`);
173
+ console.log(` ${c.dim}${BOX.dVertical}${BOX.dHorizontal.repeat(62)}${BOX.dVertical}${c.reset}`);
174
+
175
+ // Verdict
176
+ const verdictIcon = verdict === "SHIP" ? ICONS.ship : verdict === "WARN" ? ICONS.warning : ICONS.cross;
177
+ console.log(` ${c.dim}${BOX.dVertical}${c.reset} ${c.dim}${BOX.dVertical}${c.reset}`);
178
+ console.log(` ${c.dim}${BOX.dVertical}${c.reset} ${c.bold}VERDICT:${c.reset} ${verdictColor}${c.bold}${verdictIcon} ${verdict}${c.reset} ${c.dim}${BOX.dVertical}${c.reset}`);
179
+ console.log(` ${c.dim}${BOX.dVertical}${c.reset} ${c.dim}${BOX.dVertical}${c.reset}`);
180
+
181
+ // Health bar
182
+ console.log(` ${c.dim}${BOX.dVertical}${c.reset} ${c.dim}Health:${c.reset} ${progressBar(health)} ${health}% ${c.dim}${BOX.dVertical}${c.reset}`);
183
+ console.log(` ${c.dim}${BOX.dVertical}${c.reset} ${c.dim}Findings:${c.reset} ${colors.blockRed}${blocks}${c.reset} ${c.dim}BLOCK${c.reset} ${colors.warnAmber}${warns}${c.reset} ${c.dim}WARN${c.reset} ${c.dim}${BOX.dVertical}${c.reset}`);
184
+
185
+ console.log(` ${c.dim}${BOX.dVertical}${c.reset} ${c.dim}${BOX.dVertical}${c.reset}`);
186
+
187
+ // Status message
188
+ const statusMsg = verdict === "SHIP"
189
+ ? `${colors.shipGreen}${ICONS.check}${c.reset} Ready to ship`
190
+ : `${colors.warnAmber}${ICONS.arrow}${c.reset} Run ${colors.accent}vibecheck fix${c.reset} to resolve`;
191
+ console.log(` ${c.dim}${BOX.dVertical}${c.reset} ${statusMsg} ${c.dim}${BOX.dVertical}${c.reset}`);
192
+
193
+ console.log(` ${c.dim}${BOX.dVertical}${c.reset} ${c.dim}${BOX.dVertical}${c.reset}`);
194
+ console.log(` ${c.dim}${BOX.dBottomLeft}${BOX.dHorizontal.repeat(62)}${BOX.dBottomRight}${c.reset}`);
195
+ console.log();
196
+ console.log(` ${c.dim}${ICONS.watching} Watching for changes... (Ctrl+C to stop)${c.reset}`);
197
+ console.log();
84
198
  }
85
199
 
86
200
  function printTopFindings(findings, max = 5) {
@@ -90,12 +204,13 @@ function printTopFindings(findings, max = 5) {
90
204
 
91
205
  if (!top.length) return;
92
206
 
93
- console.log(`${c.dim} Top findings:${c.reset}`);
207
+ console.log(` ${c.bold}Top findings:${c.reset}`);
94
208
  for (const f of top) {
95
- const icon = f.severity === "BLOCK" ? c.red + "●" : c.yellow + "●";
96
- console.log(` ${icon}${c.reset} ${f.title}`);
209
+ const icon = f.severity === "BLOCK" ? `${colors.blockRed}●` : `${colors.warnAmber}●`;
210
+ const title = f.title?.length > 50 ? f.title.slice(0, 47) + '...' : f.title;
211
+ console.log(` ${icon}${c.reset} ${title}`);
97
212
  }
98
- console.log("");
213
+ console.log();
99
214
  }
100
215
 
101
216
  async function runShipQuiet(root, fastifyEntry) {
@@ -159,37 +274,42 @@ function watchDirectory(dir, callback) {
159
274
  }
160
275
 
161
276
  function printHelp() {
277
+ console.log(BANNER_FULL);
162
278
  console.log(`
163
- ${c.cyan}${c.bold}vibecheck watch${c.reset} - Continuous Dev Mode
279
+ ${c.bold}Usage:${c.reset} vibecheck watch [options]
280
+
281
+ ${c.bold}Continuous Dev Mode${c.reset} — Live reload with automatic ship analysis.
282
+
283
+ ${c.bold}Options:${c.reset}
284
+ ${colors.accent}--fastify-entry <path>${c.reset} Fastify entry file for route extraction
285
+ ${colors.accent}--debounce <ms>${c.reset} Debounce delay in ms ${c.dim}(default: 500)${c.reset}
286
+ ${colors.accent}--no-clear${c.reset} Don't clear screen between runs
287
+ ${colors.accent}--help, -h${c.reset} Show this help
164
288
 
165
- ${c.bold}USAGE${c.reset}
166
- vibecheck watch [options]
289
+ ${c.bold}What It Does:${c.reset}
290
+ ${colors.shipGreen}1.${c.reset} Runs initial ship analysis
291
+ ${colors.shipGreen}2.${c.reset} Watches for file changes in your project
292
+ ${colors.shipGreen}3.${c.reset} Re-runs analysis on each change
293
+ ${colors.shipGreen}4.${c.reset} Shows live verdict dashboard
167
294
 
168
- ${c.bold}OPTIONS${c.reset}
169
- --fastify-entry <path> Fastify entry file for route extraction
170
- --debounce <ms> Debounce delay in ms (default: 500)
171
- --no-clear Don't clear screen between runs
172
- --help, -h Show this help
295
+ ${c.bold}Watched Files:${c.reset}
296
+ ${colors.accent}.ts${c.reset} ${colors.accent}.tsx${c.reset} ${colors.accent}.js${c.reset} ${colors.accent}.jsx${c.reset} ${colors.accent}.json${c.reset} ${colors.accent}.env${c.reset} ${colors.accent}.md${c.reset} ${colors.accent}.yml${c.reset} ${colors.accent}.yaml${c.reset}
173
297
 
174
- ${c.bold}WHAT IT DOES${c.reset}
175
- 1. Runs initial ship analysis
176
- 2. Watches for file changes in your project
177
- 3. Re-runs analysis on each change
178
- 4. Shows live verdict dashboard
298
+ ${c.bold}Ignored:${c.reset}
299
+ ${c.dim}node_modules, .next, .vibecheck, dist, build, .git${c.reset}
179
300
 
180
- ${c.bold}WATCHED FILES${c.reset}
181
- .ts, .tsx, .js, .jsx, .json, .env, .md, .yml, .yaml
301
+ ${c.bold}Examples:${c.reset}
302
+ ${c.dim}# Start watching${c.reset}
303
+ vibecheck watch
182
304
 
183
- ${c.bold}IGNORED${c.reset}
184
- node_modules, .next, .vibecheck, dist, build, .git
305
+ ${c.dim}# 1 second debounce${c.reset}
306
+ vibecheck watch --debounce 1000
185
307
 
186
- ${c.bold}EXAMPLES${c.reset}
187
- vibecheck watch # Start watching
188
- vibecheck watch --debounce 1000 # 1s debounce
189
- vibecheck watch --no-clear # Keep history
308
+ ${c.dim}# Keep history (don't clear)${c.reset}
309
+ vibecheck watch --no-clear
190
310
 
191
- ${c.dim}Press Ctrl+C to stop watching${c.reset}
192
- `);
311
+ ${c.dim}Press Ctrl+C to stop watching${c.reset}
312
+ `);
193
313
  }
194
314
 
195
315
  async function runWatch(argsOrOpts = {}) {