@vibecheckai/cli 3.1.5 → 3.1.6
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/bin/registry.js +1 -1
- package/bin/runners/ENHANCEMENT_GUIDE.md +121 -0
- package/bin/runners/lib/global-flags.js +213 -0
- package/bin/runners/lib/init-wizard.js +566 -273
- package/bin/runners/lib/interactive-menu.js +1496 -0
- package/bin/runners/runCheckpoint.js +503 -502
- package/bin/runners/runContext.js +15 -7
- package/bin/runners/runCtx.js +10 -5
- package/bin/runners/runDoctor.js +20 -32
- package/bin/runners/runFix.js +19 -11
- package/bin/runners/runInit.js +847 -55
- package/bin/runners/runPermissions.js +13 -7
- package/bin/runners/runProve.js +18 -12
- package/bin/runners/runReport.js +31 -18
- package/bin/runners/runScan.js +28 -17
- package/bin/runners/runShip.js +27 -21
- package/bin/runners/runStatus.js +17 -1
- package/bin/vibecheck.js +882 -201
- package/mcp-server/package.json +1 -1
- package/package.json +1 -1
package/bin/vibecheck.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// bin/vibecheck.js - World-Class CLI
|
|
2
|
+
// bin/vibecheck.js - World-Class CLI (Refactored)
|
|
3
3
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
4
4
|
// VibeCheck - Proves your app is real
|
|
5
5
|
// Ship with confidence. Catch fake features before your users do.
|
|
@@ -7,38 +7,71 @@
|
|
|
7
7
|
|
|
8
8
|
"use strict";
|
|
9
9
|
|
|
10
|
-
const readline = require("readline");
|
|
11
|
-
const path = require("path");
|
|
12
|
-
const fs = require("fs");
|
|
13
|
-
const os = require("os");
|
|
14
10
|
const { performance } = require("perf_hooks");
|
|
15
|
-
const
|
|
11
|
+
const STARTUP_TIME = performance.now();
|
|
16
12
|
|
|
17
13
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
18
|
-
//
|
|
14
|
+
// LAZY LOADING - Defer all requires until needed for fast startup
|
|
19
15
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
20
|
-
const
|
|
16
|
+
const lazyModules = {};
|
|
17
|
+
|
|
18
|
+
function lazy(name, loader) {
|
|
19
|
+
return () => {
|
|
20
|
+
if (!lazyModules[name]) {
|
|
21
|
+
lazyModules[name] = loader();
|
|
22
|
+
}
|
|
23
|
+
return lazyModules[name];
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const getFs = lazy("fs", () => require("fs"));
|
|
28
|
+
const getPath = lazy("path", () => require("path"));
|
|
29
|
+
const getOs = lazy("os", () => require("os"));
|
|
30
|
+
const getCrypto = lazy("crypto", () => require("crypto"));
|
|
31
|
+
const getHttps = lazy("https", () => require("https"));
|
|
32
|
+
const getReadline = lazy("readline", () => require("readline"));
|
|
21
33
|
|
|
22
34
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
23
|
-
// VERSION & METADATA
|
|
35
|
+
// VERSION & METADATA (lazy loaded)
|
|
24
36
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
37
|
+
let _version = null;
|
|
25
38
|
function getVersion() {
|
|
39
|
+
if (_version) return _version;
|
|
26
40
|
try {
|
|
41
|
+
const fs = getFs();
|
|
42
|
+
const path = getPath();
|
|
27
43
|
const pkgPath = path.join(__dirname, "..", "package.json");
|
|
28
44
|
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
|
|
29
|
-
|
|
45
|
+
_version = pkg.version || "0.0.0";
|
|
30
46
|
} catch {
|
|
31
|
-
|
|
47
|
+
_version = "0.0.0";
|
|
32
48
|
}
|
|
49
|
+
return _version;
|
|
33
50
|
}
|
|
34
51
|
|
|
35
|
-
const VERSION = getVersion();
|
|
36
52
|
const CLI_NAME = "vibecheck";
|
|
37
53
|
const CONFIG_FILE = ".vibecheckrc";
|
|
38
|
-
const CACHE_DIR = path.join(os.homedir(), ".vibecheck");
|
|
39
|
-
const STATE_FILE = path.join(CACHE_DIR, "state.json");
|
|
40
54
|
const UPDATE_CHECK_INTERVAL = 24 * 60 * 60 * 1000; // 24 hours
|
|
41
55
|
|
|
56
|
+
// Cache/state paths (computed lazily)
|
|
57
|
+
let _cacheDir = null;
|
|
58
|
+
let _stateFile = null;
|
|
59
|
+
function getCacheDir() {
|
|
60
|
+
if (!_cacheDir) {
|
|
61
|
+
const os = getOs();
|
|
62
|
+
const path = getPath();
|
|
63
|
+
_cacheDir = path.join(os.homedir(), ".vibecheck");
|
|
64
|
+
}
|
|
65
|
+
return _cacheDir;
|
|
66
|
+
}
|
|
67
|
+
function getStateFile() {
|
|
68
|
+
if (!_stateFile) {
|
|
69
|
+
const path = getPath();
|
|
70
|
+
_stateFile = path.join(getCacheDir(), "state.json");
|
|
71
|
+
}
|
|
72
|
+
return _stateFile;
|
|
73
|
+
}
|
|
74
|
+
|
|
42
75
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
43
76
|
// ANSI STYLES - Premium terminal styling with gradient support
|
|
44
77
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
@@ -47,7 +80,7 @@ const SUPPORTS_TRUECOLOR = SUPPORTS_COLOR && (
|
|
|
47
80
|
process.env.COLORTERM === "truecolor" ||
|
|
48
81
|
process.env.TERM_PROGRAM === "iTerm.app" ||
|
|
49
82
|
process.env.TERM_PROGRAM === "Apple_Terminal" ||
|
|
50
|
-
process.env.WT_SESSION
|
|
83
|
+
process.env.WT_SESSION
|
|
51
84
|
);
|
|
52
85
|
|
|
53
86
|
const c = SUPPORTS_COLOR ? {
|
|
@@ -93,7 +126,12 @@ const c = SUPPORTS_COLOR ? {
|
|
|
93
126
|
"bgRed", "bgGreen", "bgYellow", "bgBlue", "bgMagenta", "bgCyan", "bgWhite", "bgGray"
|
|
94
127
|
].map(k => [k, ""]));
|
|
95
128
|
|
|
96
|
-
//
|
|
129
|
+
// Add no-op rgb functions for non-color mode
|
|
130
|
+
if (!SUPPORTS_COLOR) {
|
|
131
|
+
c.rgb = () => "";
|
|
132
|
+
c.bgRgb = () => "";
|
|
133
|
+
}
|
|
134
|
+
|
|
97
135
|
function gradient(text, colors = [[0, 255, 255], [255, 0, 255], [255, 255, 0]]) {
|
|
98
136
|
if (!SUPPORTS_TRUECOLOR) return `${c.cyan}${text}${c.reset}`;
|
|
99
137
|
const chars = [...text];
|
|
@@ -114,7 +152,7 @@ function gradient(text, colors = [[0, 255, 255], [255, 0, 255], [255, 255, 0]])
|
|
|
114
152
|
}
|
|
115
153
|
|
|
116
154
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
117
|
-
// UNICODE SYMBOLS
|
|
155
|
+
// UNICODE SYMBOLS
|
|
118
156
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
119
157
|
const SUPPORTS_UNICODE = process.platform !== "win32" || process.env.WT_SESSION || process.env.TERM_PROGRAM;
|
|
120
158
|
|
|
@@ -150,24 +188,48 @@ const sym = SUPPORTS_UNICODE ? {
|
|
|
150
188
|
};
|
|
151
189
|
|
|
152
190
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
153
|
-
//
|
|
191
|
+
// CI DETECTION
|
|
154
192
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
155
193
|
function isCI() {
|
|
156
|
-
return !!(
|
|
157
|
-
process.env.
|
|
158
|
-
process.env.
|
|
159
|
-
process.env.
|
|
160
|
-
process.env.
|
|
194
|
+
return !!(
|
|
195
|
+
process.env.CI ||
|
|
196
|
+
process.env.CONTINUOUS_INTEGRATION ||
|
|
197
|
+
process.env.RAILWAY_ENVIRONMENT ||
|
|
198
|
+
process.env.VERCEL ||
|
|
199
|
+
process.env.NETLIFY ||
|
|
200
|
+
process.env.GITHUB_ACTIONS ||
|
|
201
|
+
process.env.GITLAB_CI ||
|
|
202
|
+
process.env.CIRCLECI ||
|
|
203
|
+
process.env.TRAVIS ||
|
|
204
|
+
process.env.BUILDKITE ||
|
|
205
|
+
process.env.RENDER ||
|
|
206
|
+
process.env.HEROKU ||
|
|
207
|
+
process.env.CODEBUILD_BUILD_ID ||
|
|
208
|
+
process.env.JENKINS_URL ||
|
|
209
|
+
process.env.TEAMCITY_VERSION ||
|
|
210
|
+
process.env.TF_BUILD ||
|
|
211
|
+
!process.stdin.isTTY
|
|
212
|
+
);
|
|
161
213
|
}
|
|
162
214
|
|
|
215
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
216
|
+
// SPINNER CLASS
|
|
217
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
163
218
|
class Spinner {
|
|
164
219
|
constructor(text = "") {
|
|
165
|
-
this.text = text;
|
|
166
|
-
this.
|
|
220
|
+
this.text = text;
|
|
221
|
+
this.frame = 0;
|
|
222
|
+
this.interval = null;
|
|
223
|
+
this.stream = process.stderr;
|
|
224
|
+
this.isCI = isCI();
|
|
167
225
|
}
|
|
226
|
+
|
|
168
227
|
start(text) {
|
|
169
228
|
if (text) this.text = text;
|
|
170
|
-
if (this.isCI) {
|
|
229
|
+
if (this.isCI) {
|
|
230
|
+
this.stream.write(`${c.dim}${sym.pending}${c.reset} ${this.text}\n`);
|
|
231
|
+
return this;
|
|
232
|
+
}
|
|
171
233
|
this.interval = setInterval(() => {
|
|
172
234
|
const spinner = sym.spinner[this.frame % sym.spinner.length];
|
|
173
235
|
this.stream.write(`\r${c.cyan}${spinner}${c.reset} ${this.text}`);
|
|
@@ -175,35 +237,183 @@ class Spinner {
|
|
|
175
237
|
}, 80);
|
|
176
238
|
return this;
|
|
177
239
|
}
|
|
178
|
-
update(text) { this.text = text; if (this.isCI) this.stream.write(` ${c.dim}${sym.arrowRight}${c.reset} ${text}\n`); return this; }
|
|
179
|
-
succeed(text) { this.stop(); this.stream.write(`\r${c.green}${sym.success}${c.reset} ${text || this.text}\n`); return this; }
|
|
180
|
-
fail(text) { this.stop(); this.stream.write(`\r${c.red}${sym.error}${c.reset} ${text || this.text}\n`); return this; }
|
|
181
|
-
warn(text) { this.stop(); this.stream.write(`\r${c.yellow}${sym.warning}${c.reset} ${text || this.text}\n`); return this; }
|
|
182
|
-
info(text) { this.stop(); this.stream.write(`\r${c.blue}${sym.info}${c.reset} ${text || this.text}\n`); return this; }
|
|
183
|
-
stop() { if (this.interval) { clearInterval(this.interval); this.interval = null; this.stream.write("\r\x1b[K"); } return this; }
|
|
184
|
-
}
|
|
185
240
|
|
|
186
|
-
|
|
241
|
+
update(text) {
|
|
242
|
+
this.text = text;
|
|
243
|
+
if (this.isCI) this.stream.write(` ${c.dim}${sym.arrowRight}${c.reset} ${text}\n`);
|
|
244
|
+
return this;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
succeed(text) {
|
|
248
|
+
this.stop();
|
|
249
|
+
this.stream.write(`\r${c.green}${sym.success}${c.reset} ${text || this.text}\n`);
|
|
250
|
+
return this;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
fail(text) {
|
|
254
|
+
this.stop();
|
|
255
|
+
this.stream.write(`\r${c.red}${sym.error}${c.reset} ${text || this.text}\n`);
|
|
256
|
+
return this;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
warn(text) {
|
|
260
|
+
this.stop();
|
|
261
|
+
this.stream.write(`\r${c.yellow}${sym.warning}${c.reset} ${text || this.text}\n`);
|
|
262
|
+
return this;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
info(text) {
|
|
266
|
+
this.stop();
|
|
267
|
+
this.stream.write(`\r${c.blue}${sym.info}${c.reset} ${text || this.text}\n`);
|
|
268
|
+
return this;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
stop() {
|
|
272
|
+
if (this.interval) {
|
|
273
|
+
clearInterval(this.interval);
|
|
274
|
+
this.interval = null;
|
|
275
|
+
this.stream.write("\r\x1b[K");
|
|
276
|
+
}
|
|
277
|
+
return this;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
187
280
|
|
|
188
|
-
|
|
281
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
282
|
+
// STATE MANAGEMENT (lazy file I/O)
|
|
283
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
284
|
+
function ensureCacheDir() {
|
|
285
|
+
const fs = getFs();
|
|
286
|
+
const cacheDir = getCacheDir();
|
|
287
|
+
if (!fs.existsSync(cacheDir)) {
|
|
288
|
+
fs.mkdirSync(cacheDir, { recursive: true });
|
|
289
|
+
}
|
|
290
|
+
}
|
|
189
291
|
|
|
190
292
|
function loadState() {
|
|
191
|
-
try {
|
|
192
|
-
|
|
293
|
+
try {
|
|
294
|
+
const fs = getFs();
|
|
295
|
+
const stateFile = getStateFile();
|
|
296
|
+
if (fs.existsSync(stateFile)) {
|
|
297
|
+
return JSON.parse(fs.readFileSync(stateFile, "utf-8"));
|
|
298
|
+
}
|
|
299
|
+
} catch {}
|
|
300
|
+
return {
|
|
301
|
+
firstRun: Date.now(),
|
|
302
|
+
lastRun: null,
|
|
303
|
+
runCount: 0,
|
|
304
|
+
lastUpdateCheck: null,
|
|
305
|
+
latestVersion: null,
|
|
306
|
+
commandHistory: [],
|
|
307
|
+
favorites: [],
|
|
308
|
+
};
|
|
193
309
|
}
|
|
194
310
|
|
|
195
|
-
function saveState(state) {
|
|
311
|
+
function saveState(state) {
|
|
312
|
+
try {
|
|
313
|
+
const fs = getFs();
|
|
314
|
+
ensureCacheDir();
|
|
315
|
+
fs.writeFileSync(getStateFile(), JSON.stringify(state, null, 2));
|
|
316
|
+
} catch {}
|
|
317
|
+
}
|
|
196
318
|
|
|
197
319
|
function loadConfig() {
|
|
198
|
-
const
|
|
320
|
+
const fs = getFs();
|
|
321
|
+
const path = getPath();
|
|
322
|
+
|
|
323
|
+
const config = {
|
|
324
|
+
debug: false,
|
|
325
|
+
verbose: false,
|
|
326
|
+
quiet: false,
|
|
327
|
+
color: true,
|
|
328
|
+
analytics: true,
|
|
329
|
+
updateCheck: true,
|
|
330
|
+
timeout: 30000,
|
|
331
|
+
maxRetries: 3,
|
|
332
|
+
noBanner: false,
|
|
333
|
+
};
|
|
334
|
+
|
|
199
335
|
const projectConfigPath = path.join(process.cwd(), CONFIG_FILE);
|
|
200
|
-
if (fs.existsSync(projectConfigPath)) {
|
|
336
|
+
if (fs.existsSync(projectConfigPath)) {
|
|
337
|
+
try {
|
|
338
|
+
Object.assign(config, JSON.parse(fs.readFileSync(projectConfigPath, "utf-8")));
|
|
339
|
+
} catch {}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// Environment overrides
|
|
201
343
|
if (process.env.VIBECHECK_DEBUG === "true") config.debug = true;
|
|
202
344
|
if (process.env.VIBECHECK_VERBOSE === "true") config.verbose = true;
|
|
345
|
+
if (process.env.VIBECHECK_QUIET === "true") config.quiet = true;
|
|
346
|
+
if (process.env.VIBECHECK_NO_BANNER === "true") config.noBanner = true;
|
|
203
347
|
if (process.env.NO_COLOR || process.env.VIBECHECK_NO_COLOR) config.color = false;
|
|
348
|
+
|
|
204
349
|
return config;
|
|
205
350
|
}
|
|
206
351
|
|
|
352
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
353
|
+
// UPDATE CHECKER - Actually implemented
|
|
354
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
355
|
+
async function checkForUpdates(state, config) {
|
|
356
|
+
if (!config.updateCheck) return null;
|
|
357
|
+
if (isCI()) return null;
|
|
358
|
+
|
|
359
|
+
const now = Date.now();
|
|
360
|
+
if (state.lastUpdateCheck && (now - state.lastUpdateCheck) < UPDATE_CHECK_INTERVAL) {
|
|
361
|
+
// Use cached version if recent
|
|
362
|
+
if (state.latestVersion && state.latestVersion !== getVersion()) {
|
|
363
|
+
return state.latestVersion;
|
|
364
|
+
}
|
|
365
|
+
return null;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// Async check - don't block CLI startup
|
|
369
|
+
return new Promise((resolve) => {
|
|
370
|
+
const https = getHttps();
|
|
371
|
+
const timeout = setTimeout(() => resolve(null), 3000); // 3s timeout
|
|
372
|
+
|
|
373
|
+
const req = https.get(
|
|
374
|
+
"https://registry.npmjs.org/@vibecheckai/cli/latest",
|
|
375
|
+
{ headers: { "Accept": "application/json", "User-Agent": `vibecheck-cli/${getVersion()}` } },
|
|
376
|
+
(res) => {
|
|
377
|
+
let data = "";
|
|
378
|
+
res.on("data", (chunk) => { data += chunk; });
|
|
379
|
+
res.on("end", () => {
|
|
380
|
+
clearTimeout(timeout);
|
|
381
|
+
try {
|
|
382
|
+
const pkg = JSON.parse(data);
|
|
383
|
+
const latest = pkg.version;
|
|
384
|
+
state.lastUpdateCheck = now;
|
|
385
|
+
state.latestVersion = latest;
|
|
386
|
+
saveState(state);
|
|
387
|
+
|
|
388
|
+
if (latest && latest !== getVersion()) {
|
|
389
|
+
resolve(latest);
|
|
390
|
+
} else {
|
|
391
|
+
resolve(null);
|
|
392
|
+
}
|
|
393
|
+
} catch {
|
|
394
|
+
resolve(null);
|
|
395
|
+
}
|
|
396
|
+
});
|
|
397
|
+
}
|
|
398
|
+
);
|
|
399
|
+
|
|
400
|
+
req.on("error", () => {
|
|
401
|
+
clearTimeout(timeout);
|
|
402
|
+
resolve(null);
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
req.end();
|
|
406
|
+
});
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
function printUpdateNotice(latestVersion) {
|
|
410
|
+
const currentVersion = getVersion();
|
|
411
|
+
console.log();
|
|
412
|
+
console.log(`${c.yellow}${sym.sparkles}${c.reset} Update available: ${c.dim}${currentVersion}${c.reset} ${sym.arrow} ${c.green}${latestVersion}${c.reset}`);
|
|
413
|
+
console.log(` Run ${c.cyan}npm update -g @vibecheckai/cli${c.reset} to update`);
|
|
414
|
+
console.log();
|
|
415
|
+
}
|
|
416
|
+
|
|
207
417
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
208
418
|
// FUZZY MATCHING
|
|
209
419
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
@@ -213,8 +423,15 @@ function levenshtein(a, b) {
|
|
|
213
423
|
for (let j = 0; j <= a.length; j++) matrix[0][j] = j;
|
|
214
424
|
for (let i = 1; i <= b.length; i++) {
|
|
215
425
|
for (let j = 1; j <= a.length; j++) {
|
|
216
|
-
if (b.charAt(i - 1) === a.charAt(j - 1))
|
|
217
|
-
|
|
426
|
+
if (b.charAt(i - 1) === a.charAt(j - 1)) {
|
|
427
|
+
matrix[i][j] = matrix[i - 1][j - 1];
|
|
428
|
+
} else {
|
|
429
|
+
matrix[i][j] = Math.min(
|
|
430
|
+
matrix[i - 1][j - 1] + 1,
|
|
431
|
+
matrix[i][j - 1] + 1,
|
|
432
|
+
matrix[i - 1][j] + 1
|
|
433
|
+
);
|
|
434
|
+
}
|
|
218
435
|
}
|
|
219
436
|
}
|
|
220
437
|
return matrix[b.length][a.length];
|
|
@@ -224,63 +441,89 @@ function findSimilarCommands(input, commands, maxDistance = 3) {
|
|
|
224
441
|
const matches = [];
|
|
225
442
|
for (const cmd of commands) {
|
|
226
443
|
const distance = levenshtein(input.toLowerCase(), cmd.toLowerCase());
|
|
227
|
-
if (distance <= maxDistance)
|
|
228
|
-
|
|
444
|
+
if (distance <= maxDistance) {
|
|
445
|
+
matches.push({ cmd, distance });
|
|
446
|
+
}
|
|
447
|
+
if (cmd.toLowerCase().startsWith(input.toLowerCase()) && input.length >= 2) {
|
|
448
|
+
matches.push({ cmd, distance: 0.5 });
|
|
449
|
+
}
|
|
229
450
|
}
|
|
230
|
-
return matches.sort((a, b) => a.distance - b.distance).map(m => m.cmd).slice(0, 3);
|
|
451
|
+
return [...new Set(matches.sort((a, b) => a.distance - b.distance).map(m => m.cmd))].slice(0, 3);
|
|
231
452
|
}
|
|
232
453
|
|
|
233
454
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
234
|
-
//
|
|
455
|
+
// COMMAND REGISTRY (lazy loaded)
|
|
235
456
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
236
|
-
|
|
457
|
+
let _registry = null;
|
|
458
|
+
function getRegistry() {
|
|
459
|
+
if (!_registry) {
|
|
460
|
+
_registry = require("./registry");
|
|
461
|
+
}
|
|
462
|
+
return _registry;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
function getRunner(cmd) {
|
|
466
|
+
const registry = getRegistry();
|
|
467
|
+
return registry.getRunner(cmd, { red: c.red, reset: c.reset, errorSymbol: sym.error });
|
|
468
|
+
}
|
|
237
469
|
|
|
238
470
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
239
|
-
//
|
|
471
|
+
// ENTITLEMENTS & UPSELL (lazy loaded)
|
|
240
472
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
241
|
-
|
|
473
|
+
let _entitlements = null;
|
|
474
|
+
let _upsell = null;
|
|
475
|
+
|
|
476
|
+
function getEntitlements() {
|
|
477
|
+
if (!_entitlements) {
|
|
478
|
+
_entitlements = require("./runners/lib/entitlements-v2");
|
|
479
|
+
}
|
|
480
|
+
return _entitlements;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
function getUpsell() {
|
|
484
|
+
if (!_upsell) {
|
|
485
|
+
_upsell = require("./runners/lib/upsell");
|
|
486
|
+
}
|
|
487
|
+
return _upsell;
|
|
488
|
+
}
|
|
242
489
|
|
|
243
490
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
244
|
-
// CLI OUTPUT UTILITIES
|
|
491
|
+
// CLI OUTPUT UTILITIES (lazy loaded)
|
|
245
492
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
493
|
+
let _cliOutput = null;
|
|
494
|
+
function getCliOutput() {
|
|
495
|
+
if (!_cliOutput) {
|
|
496
|
+
_cliOutput = require("./runners/lib/cli-output");
|
|
497
|
+
}
|
|
498
|
+
return _cliOutput;
|
|
499
|
+
}
|
|
252
500
|
|
|
253
501
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
254
|
-
//
|
|
502
|
+
// AUTH MODULE (lazy loaded)
|
|
255
503
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
504
|
+
let _authModule = null;
|
|
505
|
+
function getAuthModule() {
|
|
506
|
+
if (!_authModule) {
|
|
507
|
+
_authModule = require("./runners/lib/auth");
|
|
508
|
+
}
|
|
509
|
+
return _authModule;
|
|
261
510
|
}
|
|
262
511
|
|
|
263
512
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
264
|
-
//
|
|
513
|
+
// ACCESS CONTROL
|
|
265
514
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
266
|
-
let authModule = null;
|
|
267
|
-
function getAuthModule() { if (!authModule) authModule = require("./runners/lib/auth"); return authModule; }
|
|
268
|
-
|
|
269
|
-
/**
|
|
270
|
-
* Check command access using entitlements-v2 module.
|
|
271
|
-
* NO OWNER MODE. NO ENV VAR BYPASS. NO OFFLINE ESCALATION.
|
|
272
|
-
*/
|
|
273
515
|
async function checkCommandAccess(cmd, args, authInfo) {
|
|
274
|
-
const
|
|
516
|
+
const registry = getRegistry();
|
|
517
|
+
const def = registry.COMMANDS[cmd];
|
|
275
518
|
if (!def) return { allowed: true };
|
|
276
|
-
|
|
277
|
-
|
|
519
|
+
|
|
520
|
+
const entitlements = getEntitlements();
|
|
278
521
|
const result = await entitlements.enforce(cmd, {
|
|
279
522
|
apiKey: authInfo?.key,
|
|
280
523
|
projectPath: process.cwd(),
|
|
281
|
-
silent: true,
|
|
524
|
+
silent: true,
|
|
282
525
|
});
|
|
283
|
-
|
|
526
|
+
|
|
284
527
|
if (result.allowed) {
|
|
285
528
|
return {
|
|
286
529
|
allowed: true,
|
|
@@ -290,29 +533,272 @@ async function checkCommandAccess(cmd, args, authInfo) {
|
|
|
290
533
|
caps: result.caps,
|
|
291
534
|
};
|
|
292
535
|
}
|
|
293
|
-
|
|
294
|
-
|
|
536
|
+
|
|
537
|
+
const upsell = getUpsell();
|
|
295
538
|
return {
|
|
296
539
|
allowed: false,
|
|
297
540
|
tier: result.tier,
|
|
298
541
|
requiredTier: result.requiredTier,
|
|
299
542
|
exitCode: result.exitCode,
|
|
300
|
-
reason:
|
|
543
|
+
reason: upsell.formatDenied(cmd, {
|
|
544
|
+
currentTier: result.tier,
|
|
545
|
+
requiredTier: result.requiredTier,
|
|
546
|
+
}),
|
|
301
547
|
};
|
|
302
548
|
}
|
|
303
549
|
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
550
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
551
|
+
// SHELL COMPLETIONS
|
|
552
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
553
|
+
function generateBashCompletion() {
|
|
554
|
+
const registry = getRegistry();
|
|
555
|
+
const commands = Object.keys(registry.COMMANDS);
|
|
556
|
+
const aliases = Object.keys(registry.ALIAS_MAP);
|
|
557
|
+
const allCmds = [...commands, ...aliases].join(" ");
|
|
558
|
+
|
|
559
|
+
return `# vibecheck bash completion
|
|
560
|
+
# Add to ~/.bashrc or ~/.bash_profile:
|
|
561
|
+
# eval "$(vibecheck completion bash)"
|
|
562
|
+
|
|
563
|
+
_vibecheck_completions() {
|
|
564
|
+
local cur="\${COMP_WORDS[COMP_CWORD]}"
|
|
565
|
+
local prev="\${COMP_WORDS[COMP_CWORD-1]}"
|
|
566
|
+
|
|
567
|
+
# Main commands
|
|
568
|
+
local commands="${allCmds}"
|
|
569
|
+
|
|
570
|
+
# Global flags
|
|
571
|
+
local global_flags="--help --version --json --ci --quiet --verbose --debug --path --output --no-banner"
|
|
572
|
+
|
|
573
|
+
case "\${prev}" in
|
|
574
|
+
vibecheck|vc)
|
|
575
|
+
COMPREPLY=($(compgen -W "\${commands} \${global_flags}" -- "\${cur}"))
|
|
576
|
+
return 0
|
|
577
|
+
;;
|
|
578
|
+
--path|--output|-p|-o)
|
|
579
|
+
COMPREPLY=($(compgen -d -- "\${cur}"))
|
|
580
|
+
return 0
|
|
581
|
+
;;
|
|
582
|
+
init)
|
|
583
|
+
COMPREPLY=($(compgen -W "--local --connect --quick --enterprise --ci --soc2 --hipaa --gdpr --team --mcp" -- "\${cur}"))
|
|
584
|
+
return 0
|
|
585
|
+
;;
|
|
586
|
+
scan)
|
|
587
|
+
COMPREPLY=($(compgen -W "--fix --autofix --json --strict --path" -- "\${cur}"))
|
|
588
|
+
return 0
|
|
589
|
+
;;
|
|
590
|
+
report)
|
|
591
|
+
COMPREPLY=($(compgen -W "--format --output html md json sarif csv" -- "\${cur}"))
|
|
592
|
+
return 0
|
|
593
|
+
;;
|
|
594
|
+
completion)
|
|
595
|
+
COMPREPLY=($(compgen -W "bash zsh fish" -- "\${cur}"))
|
|
596
|
+
return 0
|
|
597
|
+
;;
|
|
598
|
+
esac
|
|
599
|
+
|
|
600
|
+
if [[ "\${cur}" == -* ]]; then
|
|
601
|
+
COMPREPLY=($(compgen -W "\${global_flags}" -- "\${cur}"))
|
|
602
|
+
fi
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
complete -F _vibecheck_completions vibecheck
|
|
606
|
+
complete -F _vibecheck_completions vc
|
|
607
|
+
`;
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
function generateZshCompletion() {
|
|
611
|
+
const registry = getRegistry();
|
|
612
|
+
const commands = Object.entries(registry.COMMANDS);
|
|
613
|
+
|
|
614
|
+
let cmdList = commands.map(([cmd, def]) => {
|
|
615
|
+
const desc = (def.description || "").replace(/'/g, "\\'").replace(/"/g, '\\"');
|
|
616
|
+
return ` '${cmd}:${desc}'`;
|
|
617
|
+
}).join(" \\\n");
|
|
618
|
+
|
|
619
|
+
return `#compdef vibecheck vc
|
|
620
|
+
# vibecheck zsh completion
|
|
621
|
+
# Add to ~/.zshrc:
|
|
622
|
+
# eval "$(vibecheck completion zsh)"
|
|
623
|
+
|
|
624
|
+
_vibecheck() {
|
|
625
|
+
local -a commands
|
|
626
|
+
commands=(
|
|
627
|
+
${cmdList}
|
|
628
|
+
)
|
|
629
|
+
|
|
630
|
+
local -a global_opts
|
|
631
|
+
global_opts=(
|
|
632
|
+
'--help[Show help]'
|
|
633
|
+
'--version[Show version]'
|
|
634
|
+
'--json[Output as JSON]'
|
|
635
|
+
'--ci[CI mode]'
|
|
636
|
+
'--quiet[Suppress output]'
|
|
637
|
+
'--verbose[Verbose output]'
|
|
638
|
+
'--debug[Debug mode]'
|
|
639
|
+
'--path[Project path]:directory:_directories'
|
|
640
|
+
'--output[Output directory]:directory:_directories'
|
|
641
|
+
'--no-banner[Hide banner]'
|
|
642
|
+
)
|
|
643
|
+
|
|
644
|
+
_arguments -C \\
|
|
645
|
+
"1: :->command" \\
|
|
646
|
+
"*::arg:->args"
|
|
647
|
+
|
|
648
|
+
case "\$state" in
|
|
649
|
+
command)
|
|
650
|
+
_describe -t commands 'vibecheck commands' commands
|
|
651
|
+
_describe -t options 'options' global_opts
|
|
652
|
+
;;
|
|
653
|
+
args)
|
|
654
|
+
case "\$words[1]" in
|
|
655
|
+
init)
|
|
656
|
+
_arguments \\
|
|
657
|
+
'--local[Local setup only]' \\
|
|
658
|
+
'--connect[GitHub integration]' \\
|
|
659
|
+
'--quick[Quick setup]' \\
|
|
660
|
+
'--enterprise[Enterprise mode]' \\
|
|
661
|
+
'--ci[Setup CI/CD]' \\
|
|
662
|
+
'--soc2[SOC2 compliance]' \\
|
|
663
|
+
'--hipaa[HIPAA compliance]'
|
|
664
|
+
;;
|
|
665
|
+
scan)
|
|
666
|
+
_arguments \\
|
|
667
|
+
'--fix[Apply fixes]' \\
|
|
668
|
+
'--autofix[Auto-fix issues]' \\
|
|
669
|
+
'--json[JSON output]' \\
|
|
670
|
+
'--strict[Strict mode]'
|
|
671
|
+
;;
|
|
672
|
+
report)
|
|
673
|
+
_arguments \\
|
|
674
|
+
'--format[Output format]:format:(html md json sarif csv)' \\
|
|
675
|
+
'--output[Output file]:file:_files'
|
|
676
|
+
;;
|
|
677
|
+
completion)
|
|
678
|
+
_arguments '1:shell:(bash zsh fish)'
|
|
679
|
+
;;
|
|
680
|
+
esac
|
|
681
|
+
;;
|
|
682
|
+
esac
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
compdef _vibecheck vibecheck
|
|
686
|
+
compdef _vibecheck vc
|
|
687
|
+
`;
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
function generateFishCompletion() {
|
|
691
|
+
const registry = getRegistry();
|
|
692
|
+
const commands = Object.entries(registry.COMMANDS);
|
|
693
|
+
|
|
694
|
+
let completions = `# vibecheck fish completion
|
|
695
|
+
# Add to ~/.config/fish/completions/vibecheck.fish
|
|
696
|
+
# Or run: vibecheck completion fish > ~/.config/fish/completions/vibecheck.fish
|
|
697
|
+
|
|
698
|
+
# Disable file completion by default
|
|
699
|
+
complete -c vibecheck -f
|
|
700
|
+
complete -c vc -f
|
|
701
|
+
|
|
702
|
+
# Global flags
|
|
703
|
+
complete -c vibecheck -l help -d 'Show help'
|
|
704
|
+
complete -c vibecheck -l version -d 'Show version'
|
|
705
|
+
complete -c vibecheck -l json -d 'Output as JSON'
|
|
706
|
+
complete -c vibecheck -l ci -d 'CI mode'
|
|
707
|
+
complete -c vibecheck -l quiet -d 'Suppress output'
|
|
708
|
+
complete -c vibecheck -l verbose -d 'Verbose output'
|
|
709
|
+
complete -c vibecheck -l debug -d 'Debug mode'
|
|
710
|
+
complete -c vibecheck -l path -d 'Project path' -r -a '(__fish_complete_directories)'
|
|
711
|
+
complete -c vibecheck -l output -d 'Output directory' -r -a '(__fish_complete_directories)'
|
|
712
|
+
complete -c vibecheck -l no-banner -d 'Hide banner'
|
|
713
|
+
|
|
714
|
+
# Commands
|
|
715
|
+
`;
|
|
716
|
+
|
|
717
|
+
for (const [cmd, def] of commands) {
|
|
718
|
+
const desc = (def.description || "").replace(/'/g, "\\'");
|
|
719
|
+
completions += `complete -c vibecheck -n '__fish_use_subcommand' -a '${cmd}' -d '${desc}'\n`;
|
|
720
|
+
completions += `complete -c vc -n '__fish_use_subcommand' -a '${cmd}' -d '${desc}'\n`;
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
// Add aliases
|
|
724
|
+
for (const [alias, target] of Object.entries(registry.ALIAS_MAP)) {
|
|
725
|
+
const def = registry.COMMANDS[target];
|
|
726
|
+
if (def) {
|
|
727
|
+
const desc = `Alias for ${target}`;
|
|
728
|
+
completions += `complete -c vibecheck -n '__fish_use_subcommand' -a '${alias}' -d '${desc}'\n`;
|
|
729
|
+
completions += `complete -c vc -n '__fish_use_subcommand' -a '${alias}' -d '${desc}'\n`;
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
completions += `
|
|
734
|
+
# init subcommand options
|
|
735
|
+
complete -c vibecheck -n '__fish_seen_subcommand_from init' -l local -d 'Local setup only'
|
|
736
|
+
complete -c vibecheck -n '__fish_seen_subcommand_from init' -l connect -d 'GitHub integration'
|
|
737
|
+
complete -c vibecheck -n '__fish_seen_subcommand_from init' -l quick -d 'Quick setup'
|
|
738
|
+
complete -c vibecheck -n '__fish_seen_subcommand_from init' -l enterprise -d 'Enterprise mode'
|
|
739
|
+
|
|
740
|
+
# scan subcommand options
|
|
741
|
+
complete -c vibecheck -n '__fish_seen_subcommand_from scan' -l fix -d 'Apply fixes'
|
|
742
|
+
complete -c vibecheck -n '__fish_seen_subcommand_from scan' -l autofix -d 'Auto-fix issues'
|
|
743
|
+
complete -c vibecheck -n '__fish_seen_subcommand_from scan' -l strict -d 'Strict mode'
|
|
744
|
+
|
|
745
|
+
# report subcommand options
|
|
746
|
+
complete -c vibecheck -n '__fish_seen_subcommand_from report' -l format -d 'Output format' -r -a 'html md json sarif csv'
|
|
747
|
+
|
|
748
|
+
# completion subcommand
|
|
749
|
+
complete -c vibecheck -n '__fish_seen_subcommand_from completion' -a 'bash zsh fish' -d 'Shell type'
|
|
750
|
+
`;
|
|
751
|
+
|
|
752
|
+
return completions;
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
function runCompletion(args) {
|
|
756
|
+
const shell = args[0];
|
|
757
|
+
|
|
758
|
+
if (!shell || shell === "--help" || shell === "-h") {
|
|
759
|
+
console.log(`
|
|
760
|
+
${c.bold}Usage:${c.reset} vibecheck completion <shell>
|
|
761
|
+
|
|
762
|
+
${c.bold}Shells:${c.reset}
|
|
763
|
+
bash Generate bash completions
|
|
764
|
+
zsh Generate zsh completions
|
|
765
|
+
fish Generate fish completions
|
|
766
|
+
|
|
767
|
+
${c.bold}Installation:${c.reset}
|
|
768
|
+
${c.dim}# Bash (add to ~/.bashrc)${c.reset}
|
|
769
|
+
eval "$(vibecheck completion bash)"
|
|
770
|
+
|
|
771
|
+
${c.dim}# Zsh (add to ~/.zshrc)${c.reset}
|
|
772
|
+
eval "$(vibecheck completion zsh)"
|
|
773
|
+
|
|
774
|
+
${c.dim}# Fish${c.reset}
|
|
775
|
+
vibecheck completion fish > ~/.config/fish/completions/vibecheck.fish
|
|
776
|
+
`);
|
|
777
|
+
return 0;
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
switch (shell.toLowerCase()) {
|
|
781
|
+
case "bash":
|
|
782
|
+
console.log(generateBashCompletion());
|
|
783
|
+
return 0;
|
|
784
|
+
case "zsh":
|
|
785
|
+
console.log(generateZshCompletion());
|
|
786
|
+
return 0;
|
|
787
|
+
case "fish":
|
|
788
|
+
console.log(generateFishCompletion());
|
|
789
|
+
return 0;
|
|
790
|
+
default:
|
|
791
|
+
console.error(`${c.red}${sym.error}${c.reset} Unknown shell: ${shell}`);
|
|
792
|
+
console.error(`Supported shells: bash, zsh, fish`);
|
|
793
|
+
return 1;
|
|
794
|
+
}
|
|
310
795
|
}
|
|
311
796
|
|
|
312
797
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
313
798
|
// HELP SYSTEM
|
|
314
799
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
315
800
|
function printBanner() {
|
|
801
|
+
const VERSION = getVersion();
|
|
316
802
|
console.log(`
|
|
317
803
|
${c.dim}${sym.boxTopLeft}${sym.boxHorizontal.repeat(60)}${sym.boxTopRight}${c.reset}
|
|
318
804
|
${c.dim}${sym.boxVertical}${c.reset} ${gradient("VIBECHECK", [[0, 255, 255], [138, 43, 226], [255, 20, 147]])} ${c.dim}v${VERSION}${c.reset}${" ".repeat(60 - 13 - VERSION.length - 4)}${c.dim}${sym.boxVertical}${c.reset}
|
|
@@ -321,9 +807,19 @@ ${c.dim}${sym.boxBottomLeft}${sym.boxHorizontal.repeat(60)}${sym.boxBottomRight}
|
|
|
321
807
|
`);
|
|
322
808
|
}
|
|
323
809
|
|
|
324
|
-
function printHelp() {
|
|
325
|
-
printBanner();
|
|
326
|
-
|
|
810
|
+
function printHelp(showBanner = true) {
|
|
811
|
+
if (showBanner) printBanner();
|
|
812
|
+
|
|
813
|
+
const registry = getRegistry();
|
|
814
|
+
const { COMMANDS, ALIAS_MAP } = registry;
|
|
815
|
+
|
|
816
|
+
// Build reverse alias map for display
|
|
817
|
+
const reverseAliases = {};
|
|
818
|
+
for (const [alias, target] of Object.entries(ALIAS_MAP)) {
|
|
819
|
+
if (!reverseAliases[target]) reverseAliases[target] = [];
|
|
820
|
+
reverseAliases[target].push(alias);
|
|
821
|
+
}
|
|
822
|
+
|
|
327
823
|
// Categories ordered as specified
|
|
328
824
|
const categoryOrder = ["setup", "analysis", "proof", "quality", "truth", "output", "ci", "automation", "account", "extras"];
|
|
329
825
|
const categories = {
|
|
@@ -338,7 +834,7 @@ function printHelp() {
|
|
|
338
834
|
account: { name: "ACCOUNT", color: c.dim, icon: sym.key },
|
|
339
835
|
extras: { name: "EXTRAS", color: c.dim, icon: sym.starEmpty },
|
|
340
836
|
};
|
|
341
|
-
|
|
837
|
+
|
|
342
838
|
// Group commands
|
|
343
839
|
const grouped = {};
|
|
344
840
|
for (const [cmd, def] of Object.entries(COMMANDS)) {
|
|
@@ -346,33 +842,35 @@ function printHelp() {
|
|
|
346
842
|
if (!grouped[cat]) grouped[cat] = [];
|
|
347
843
|
grouped[cat].push({ cmd, ...def });
|
|
348
844
|
}
|
|
349
|
-
|
|
845
|
+
|
|
350
846
|
// Print in order
|
|
351
847
|
for (const catKey of categoryOrder) {
|
|
352
848
|
const commands = grouped[catKey];
|
|
353
849
|
if (!commands || commands.length === 0) continue;
|
|
354
|
-
|
|
850
|
+
|
|
355
851
|
const cat = categories[catKey];
|
|
356
852
|
console.log(`\n${cat.color}${cat.icon} ${cat.name}${c.reset}\n`);
|
|
357
|
-
|
|
358
|
-
for (const { cmd, description, tier,
|
|
359
|
-
// Tier badge
|
|
853
|
+
|
|
854
|
+
for (const { cmd, description, tier, caps } of commands) {
|
|
855
|
+
// Tier badge
|
|
360
856
|
let tierBadge = "";
|
|
361
|
-
if (tier === "free") {
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
857
|
+
if (tier === "free") tierBadge = `${c.green}[FREE]${c.reset} `;
|
|
858
|
+
else if (tier === "starter") tierBadge = `${c.cyan}[STARTER]${c.reset} `;
|
|
859
|
+
else if (tier === "pro") tierBadge = `${c.magenta}[PRO]${c.reset} `;
|
|
860
|
+
|
|
861
|
+
// Aliases (from reverseAliases map built earlier)
|
|
862
|
+
const aliasList = reverseAliases[cmd] || [];
|
|
863
|
+
const aliasStr = aliasList.length > 0
|
|
864
|
+
? `${c.dim}(${aliasList.slice(0, 3).join(", ")})${c.reset} `
|
|
865
|
+
: "";
|
|
866
|
+
|
|
867
|
+
// Caps info
|
|
370
868
|
const capsStr = caps ? `${c.dim}(${caps})${c.reset}` : "";
|
|
371
|
-
|
|
372
|
-
console.log(` ${c.cyan}${cmd.padEnd(12)}${c.reset} ${tierBadge}${description} ${capsStr}`);
|
|
869
|
+
|
|
870
|
+
console.log(` ${c.cyan}${cmd.padEnd(12)}${c.reset} ${aliasStr}${tierBadge}${description} ${capsStr}`);
|
|
373
871
|
}
|
|
374
872
|
}
|
|
375
|
-
|
|
873
|
+
|
|
376
874
|
console.log(`
|
|
377
875
|
${c.dim}${sym.boxHorizontal.repeat(64)}${c.reset}
|
|
378
876
|
|
|
@@ -390,6 +888,12 @@ ${c.green}QUICK START - The 5-Step Journey${c.reset}
|
|
|
390
888
|
4. ${c.bold}Prove${c.reset} ${c.cyan}vibecheck prove${c.reset} ${c.magenta}[PRO]${c.reset}
|
|
391
889
|
5. ${c.bold}Ship${c.reset} ${c.cyan}vibecheck ship${c.reset}
|
|
392
890
|
|
|
891
|
+
${c.bold}SHELL COMPLETIONS${c.reset}
|
|
892
|
+
|
|
893
|
+
${c.cyan}vibecheck completion bash${c.reset} ${c.dim}# Add to ~/.bashrc${c.reset}
|
|
894
|
+
${c.cyan}vibecheck completion zsh${c.reset} ${c.dim}# Add to ~/.zshrc${c.reset}
|
|
895
|
+
${c.cyan}vibecheck completion fish${c.reset} ${c.dim}# Save to completions dir${c.reset}
|
|
896
|
+
|
|
393
897
|
${c.dim}Run 'vibecheck <command> --help' for command-specific help.${c.reset}
|
|
394
898
|
${c.dim}Pricing: https://vibecheckai.dev/pricing${c.reset}
|
|
395
899
|
`);
|
|
@@ -406,58 +910,165 @@ function getArgValue(args, flags) {
|
|
|
406
910
|
return undefined;
|
|
407
911
|
}
|
|
408
912
|
|
|
913
|
+
function hasFlag(args, flags) {
|
|
914
|
+
for (const flag of flags) {
|
|
915
|
+
if (args.includes(flag)) return true;
|
|
916
|
+
}
|
|
917
|
+
return false;
|
|
918
|
+
}
|
|
919
|
+
|
|
409
920
|
function formatError(error, config) {
|
|
410
921
|
const lines = [`${c.red}${sym.error} Error:${c.reset} ${error.message}`];
|
|
411
|
-
if (config.debug && error.stack) {
|
|
922
|
+
if (config.debug && error.stack) {
|
|
923
|
+
lines.push("", `${c.dim}Stack trace:${c.reset}`, c.dim + error.stack.split("\n").slice(1).join("\n") + c.reset);
|
|
924
|
+
}
|
|
412
925
|
return lines.join("\n");
|
|
413
926
|
}
|
|
414
927
|
|
|
928
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
929
|
+
// FLAG PARSING
|
|
930
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
931
|
+
function parseGlobalFlags(rawArgs) {
|
|
932
|
+
const flags = {
|
|
933
|
+
help: false,
|
|
934
|
+
version: false,
|
|
935
|
+
json: false,
|
|
936
|
+
ci: false,
|
|
937
|
+
quiet: false,
|
|
938
|
+
verbose: false,
|
|
939
|
+
debug: false,
|
|
940
|
+
strict: false,
|
|
941
|
+
noBanner: false,
|
|
942
|
+
path: process.cwd(),
|
|
943
|
+
output: null,
|
|
944
|
+
};
|
|
945
|
+
|
|
946
|
+
const cleanArgs = [];
|
|
947
|
+
let i = 0;
|
|
948
|
+
|
|
949
|
+
while (i < rawArgs.length) {
|
|
950
|
+
const arg = rawArgs[i];
|
|
951
|
+
|
|
952
|
+
switch (arg) {
|
|
953
|
+
case "--help":
|
|
954
|
+
case "-h":
|
|
955
|
+
flags.help = true;
|
|
956
|
+
break;
|
|
957
|
+
case "--version":
|
|
958
|
+
case "-v":
|
|
959
|
+
flags.version = true;
|
|
960
|
+
break;
|
|
961
|
+
case "--json":
|
|
962
|
+
flags.json = true;
|
|
963
|
+
break;
|
|
964
|
+
case "--ci":
|
|
965
|
+
flags.ci = true;
|
|
966
|
+
break;
|
|
967
|
+
case "--quiet":
|
|
968
|
+
case "-q":
|
|
969
|
+
flags.quiet = true;
|
|
970
|
+
break;
|
|
971
|
+
case "--verbose":
|
|
972
|
+
flags.verbose = true;
|
|
973
|
+
break;
|
|
974
|
+
case "--debug":
|
|
975
|
+
flags.debug = true;
|
|
976
|
+
break;
|
|
977
|
+
case "--strict":
|
|
978
|
+
flags.strict = true;
|
|
979
|
+
break;
|
|
980
|
+
case "--no-banner":
|
|
981
|
+
flags.noBanner = true;
|
|
982
|
+
break;
|
|
983
|
+
case "--path":
|
|
984
|
+
case "-p":
|
|
985
|
+
flags.path = rawArgs[++i] || process.cwd();
|
|
986
|
+
break;
|
|
987
|
+
case "--output":
|
|
988
|
+
case "-o":
|
|
989
|
+
flags.output = rawArgs[++i] || null;
|
|
990
|
+
break;
|
|
991
|
+
default:
|
|
992
|
+
if (arg.startsWith("--path=")) {
|
|
993
|
+
flags.path = arg.split("=")[1];
|
|
994
|
+
} else if (arg.startsWith("--output=")) {
|
|
995
|
+
flags.output = arg.split("=")[1];
|
|
996
|
+
} else {
|
|
997
|
+
cleanArgs.push(arg);
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
i++;
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
return { flags, cleanArgs };
|
|
1004
|
+
}
|
|
1005
|
+
|
|
415
1006
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
416
1007
|
// MAIN ENTRY POINT
|
|
417
1008
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
418
1009
|
async function main() {
|
|
419
1010
|
const startTime = performance.now();
|
|
420
1011
|
const rawArgs = process.argv.slice(2);
|
|
1012
|
+
|
|
1013
|
+
// Parse global flags
|
|
1014
|
+
const { flags: globalFlags, cleanArgs } = parseGlobalFlags(rawArgs);
|
|
1015
|
+
|
|
1016
|
+
// Handle version (fast path - no config loading)
|
|
1017
|
+
if (globalFlags.version) {
|
|
1018
|
+
console.log(`vibecheck v${getVersion()}`);
|
|
1019
|
+
process.exit(0);
|
|
1020
|
+
}
|
|
1021
|
+
|
|
1022
|
+
// Load config and state
|
|
421
1023
|
const config = loadConfig();
|
|
422
1024
|
const state = loadState();
|
|
423
|
-
|
|
424
|
-
//
|
|
425
|
-
const { flags: globalFlags, parsed: cleanArgs } = parseStandardFlags(rawArgs);
|
|
426
|
-
|
|
427
|
-
// Update config based on flags
|
|
1025
|
+
|
|
1026
|
+
// Apply flag overrides to config
|
|
428
1027
|
if (globalFlags.debug) config.debug = true;
|
|
429
1028
|
if (globalFlags.verbose) config.verbose = true;
|
|
430
1029
|
if (globalFlags.quiet) config.quiet = true;
|
|
431
|
-
if (globalFlags.
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
console.log(`vibecheck v${VERSION}`);
|
|
436
|
-
process.exit(0);
|
|
1030
|
+
if (globalFlags.noBanner) config.noBanner = true;
|
|
1031
|
+
if (globalFlags.ci) {
|
|
1032
|
+
config.quiet = true;
|
|
1033
|
+
config.noBanner = true;
|
|
437
1034
|
}
|
|
438
|
-
|
|
1035
|
+
|
|
439
1036
|
// Handle no command
|
|
440
|
-
if (!cleanArgs[0]) {
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
if (globalFlags.help && cleanArgs[0] && COMMANDS[cleanArgs[0]]) {
|
|
444
|
-
// Pass --help to the command runner
|
|
445
|
-
} else if (globalFlags.help && !cleanArgs[0]) {
|
|
446
|
-
printHelp(); process.exit(0);
|
|
1037
|
+
if (!cleanArgs[0]) {
|
|
1038
|
+
printHelp(!config.noBanner);
|
|
1039
|
+
process.exit(0);
|
|
447
1040
|
}
|
|
448
|
-
|
|
1041
|
+
|
|
1042
|
+
// Handle global help
|
|
1043
|
+
if (globalFlags.help && !cleanArgs[0]) {
|
|
1044
|
+
printHelp(!config.noBanner);
|
|
1045
|
+
process.exit(0);
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
// Handle completion command (special - doesn't need registry loaded normally)
|
|
1049
|
+
if (cleanArgs[0] === "completion") {
|
|
1050
|
+
process.exit(runCompletion(cleanArgs.slice(1)));
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1053
|
+
// Load registry for command resolution
|
|
1054
|
+
const registry = getRegistry();
|
|
1055
|
+
const { COMMANDS, ALIAS_MAP, ALL_COMMANDS } = registry;
|
|
1056
|
+
|
|
449
1057
|
let cmd = cleanArgs[0];
|
|
450
|
-
//
|
|
451
|
-
if (!COMMANDS[cmd] && ALIAS_MAP[cmd]) {
|
|
1058
|
+
// Prefer exact match, then alias
|
|
1059
|
+
if (!COMMANDS[cmd] && ALIAS_MAP[cmd]) {
|
|
1060
|
+
cmd = ALIAS_MAP[cmd];
|
|
1061
|
+
}
|
|
452
1062
|
let cmdArgs = cleanArgs.slice(1);
|
|
453
|
-
|
|
454
|
-
//
|
|
1063
|
+
|
|
1064
|
+
// Pass --help to runner if specified with command
|
|
455
1065
|
if (globalFlags.help) cmdArgs = ["--help", ...cmdArgs];
|
|
456
|
-
|
|
1066
|
+
|
|
457
1067
|
// Pass standard flags to runners
|
|
458
|
-
cmdArgs = [...cmdArgs];
|
|
459
1068
|
if (globalFlags.json) cmdArgs.push("--json");
|
|
460
1069
|
if (globalFlags.ci) cmdArgs.push("--ci");
|
|
1070
|
+
if (globalFlags.noBanner) cmdArgs.push("--no-banner");
|
|
1071
|
+
if (globalFlags.quiet) cmdArgs.push("--quiet");
|
|
461
1072
|
if (globalFlags.path && globalFlags.path !== process.cwd()) {
|
|
462
1073
|
cmdArgs.push("--path", globalFlags.path);
|
|
463
1074
|
}
|
|
@@ -466,114 +1077,149 @@ async function main() {
|
|
|
466
1077
|
}
|
|
467
1078
|
if (globalFlags.verbose) cmdArgs.push("--verbose");
|
|
468
1079
|
if (globalFlags.strict) cmdArgs.push("--strict");
|
|
469
|
-
|
|
1080
|
+
|
|
1081
|
+
// Unknown command
|
|
470
1082
|
if (!COMMANDS[cmd]) {
|
|
471
1083
|
const suggestions = findSimilarCommands(cmd, ALL_COMMANDS);
|
|
472
1084
|
console.log(`\n${c.red}${sym.error}${c.reset} Unknown command: ${c.yellow}${cmd}${c.reset}`);
|
|
473
|
-
|
|
474
|
-
// Check for specific common misses
|
|
1085
|
+
|
|
475
1086
|
if (cmd === "replay" || cmd === "record") {
|
|
476
1087
|
console.log(`\n${c.dim}replay is a PRO feature for session recording.${c.reset}`);
|
|
477
1088
|
console.log(`${c.dim}Free alternative:${c.reset} ${c.cyan}vibecheck reality${c.reset} ${c.dim}(one-time runtime proof)${c.reset}`);
|
|
478
1089
|
} else if (suggestions.length > 0) {
|
|
479
1090
|
console.log(`\n${c.dim}Did you mean:${c.reset}`);
|
|
480
|
-
suggestions.forEach(s => {
|
|
1091
|
+
suggestions.forEach((s) => {
|
|
1092
|
+
const actual = ALIAS_MAP[s] || s;
|
|
1093
|
+
const def = COMMANDS[actual];
|
|
1094
|
+
console.log(` ${c.cyan}vibecheck ${s}${c.reset} ${c.dim}${def?.description || ""}${c.reset}`);
|
|
1095
|
+
});
|
|
481
1096
|
}
|
|
482
1097
|
console.log(`\n${c.dim}Run 'vibecheck --help' for available commands.${c.reset}\n`);
|
|
483
|
-
process.exit(4);
|
|
1098
|
+
process.exit(4);
|
|
484
1099
|
}
|
|
485
|
-
|
|
486
|
-
// Generate runId
|
|
487
|
-
const
|
|
1100
|
+
|
|
1101
|
+
// Generate runId
|
|
1102
|
+
const cliOutput = getCliOutput();
|
|
1103
|
+
const runId = cliOutput.generateRunId();
|
|
488
1104
|
const runStart = new Date().toISOString();
|
|
489
|
-
|
|
1105
|
+
|
|
490
1106
|
const cmdDef = COMMANDS[cmd];
|
|
491
1107
|
let authInfo = { key: null };
|
|
492
|
-
|
|
1108
|
+
|
|
1109
|
+
// Auth check (unless skipAuth)
|
|
493
1110
|
if (!cmdDef.skipAuth) {
|
|
494
1111
|
const auth = getAuthModule();
|
|
495
1112
|
const { key } = auth.getApiKey();
|
|
496
1113
|
authInfo.key = key;
|
|
497
|
-
|
|
498
|
-
// Use entitlements-v2 for access control (NO BYPASS)
|
|
1114
|
+
|
|
499
1115
|
const access = await checkCommandAccess(cmd, cmdArgs, authInfo);
|
|
500
|
-
|
|
1116
|
+
|
|
501
1117
|
if (!access.allowed) {
|
|
502
1118
|
console.log(access.reason);
|
|
503
|
-
// Use proper exit code: 3 = feature not allowed
|
|
504
1119
|
process.exit(access.exitCode || 3);
|
|
505
1120
|
}
|
|
506
|
-
|
|
507
|
-
//
|
|
1121
|
+
|
|
1122
|
+
// Downgrade notice
|
|
508
1123
|
if (access.downgrade && !config.quiet) {
|
|
1124
|
+
const upsell = getUpsell();
|
|
509
1125
|
console.log(upsell.formatDowngrade(cmd, {
|
|
510
1126
|
currentTier: access.tier,
|
|
511
1127
|
effectiveMode: access.downgrade,
|
|
512
1128
|
caps: access.caps,
|
|
513
1129
|
}));
|
|
514
1130
|
}
|
|
515
|
-
|
|
516
|
-
//
|
|
517
|
-
if (!config.quiet) {
|
|
518
|
-
if (access.tier === "pro")
|
|
519
|
-
|
|
1131
|
+
|
|
1132
|
+
// Tier badge
|
|
1133
|
+
if (!config.quiet && !config.noBanner) {
|
|
1134
|
+
if (access.tier === "pro") {
|
|
1135
|
+
console.log(`${c.magenta}${sym.arrowRight} PRO${c.reset} ${c.dim}feature${c.reset}`);
|
|
1136
|
+
} else if (access.tier === "complete") {
|
|
1137
|
+
console.log(`${c.yellow}${sym.arrowRight} COMPLETE${c.reset} ${c.dim}feature${c.reset}`);
|
|
1138
|
+
}
|
|
520
1139
|
}
|
|
521
|
-
|
|
522
|
-
// Attach access info for runners to use
|
|
1140
|
+
|
|
523
1141
|
authInfo.access = access;
|
|
524
1142
|
}
|
|
525
|
-
|
|
526
|
-
|
|
1143
|
+
|
|
1144
|
+
// Update state
|
|
1145
|
+
state.runCount++;
|
|
1146
|
+
state.lastRun = Date.now();
|
|
527
1147
|
state.commandHistory = [...(state.commandHistory || []).slice(-99), { cmd, timestamp: Date.now() }];
|
|
528
1148
|
saveState(state);
|
|
529
|
-
|
|
1149
|
+
|
|
1150
|
+
// Check for updates (async, non-blocking for startup)
|
|
1151
|
+
let updatePromise = null;
|
|
1152
|
+
if (!config.quiet && !config.noBanner && !isCI()) {
|
|
1153
|
+
updatePromise = checkForUpdates(state, config);
|
|
1154
|
+
}
|
|
1155
|
+
|
|
530
1156
|
let exitCode = 0;
|
|
1157
|
+
|
|
531
1158
|
try {
|
|
532
1159
|
const runner = getRunner(cmd);
|
|
533
|
-
if (!runner) {
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
1160
|
+
if (!runner) {
|
|
1161
|
+
console.error(`${c.red}${sym.error}${c.reset} Failed to load runner for: ${cmd}`);
|
|
1162
|
+
process.exit(1);
|
|
1163
|
+
}
|
|
1164
|
+
|
|
1165
|
+
const context = {
|
|
1166
|
+
repoRoot: globalFlags.path,
|
|
1167
|
+
config,
|
|
1168
|
+
state,
|
|
1169
|
+
authInfo,
|
|
1170
|
+
version: getVersion(),
|
|
541
1171
|
isCI: isCI(),
|
|
542
1172
|
runId,
|
|
543
|
-
runStart
|
|
1173
|
+
runStart,
|
|
544
1174
|
};
|
|
545
|
-
|
|
546
|
-
//
|
|
1175
|
+
|
|
1176
|
+
// Execute command
|
|
547
1177
|
switch (cmd) {
|
|
548
|
-
case "prove":
|
|
549
|
-
case "reality":
|
|
550
|
-
case "watch":
|
|
551
|
-
case "ship":
|
|
552
|
-
case "ctx": case "truthpack":
|
|
553
|
-
if (cmdArgs[0] === "sync") { const { runCtxSync } = require("./runners/runCtxSync"); exitCode = await runCtxSync({ ...context, fastifyEntry: getArgValue(cmdArgs, ["--fastify-entry"]) }); }
|
|
554
|
-
else if (cmdArgs[0] === "guard") { const { runCtxGuard } = require("./runners/runCtxGuard"); exitCode = await runCtxGuard.main(cmdArgs.slice(1)); }
|
|
555
|
-
else if (cmdArgs[0] === "diff") { const { main: ctxDiffMain } = require("./runners/runCtxDiff"); exitCode = await ctxDiffMain(cmdArgs.slice(1)); }
|
|
556
|
-
else if (cmdArgs[0] === "search") { const { runContext } = require("./runners/runContext"); exitCode = await runContext(["--search", ...cmdArgs.slice(1)]); }
|
|
557
|
-
else { exitCode = await runner(cmdArgs, context); }
|
|
558
|
-
break;
|
|
1178
|
+
case "prove":
|
|
1179
|
+
case "reality":
|
|
1180
|
+
case "watch":
|
|
1181
|
+
case "ship":
|
|
559
1182
|
case "runtime":
|
|
560
|
-
exitCode = await runner(cmdArgs, context);
|
|
561
|
-
break;
|
|
562
1183
|
case "export":
|
|
563
|
-
exitCode = await runner(cmdArgs, context);
|
|
564
|
-
break;
|
|
565
1184
|
case "security":
|
|
1185
|
+
case "install":
|
|
1186
|
+
case "pr":
|
|
1187
|
+
case "share":
|
|
566
1188
|
exitCode = await runner(cmdArgs, context);
|
|
567
1189
|
break;
|
|
568
|
-
|
|
569
|
-
case "
|
|
570
|
-
case "
|
|
571
|
-
|
|
572
|
-
|
|
1190
|
+
|
|
1191
|
+
case "ctx":
|
|
1192
|
+
case "truthpack":
|
|
1193
|
+
if (cmdArgs[0] === "sync") {
|
|
1194
|
+
const { runCtxSync } = require("./runners/runCtxSync");
|
|
1195
|
+
exitCode = await runCtxSync({ ...context, fastifyEntry: getArgValue(cmdArgs, ["--fastify-entry"]) });
|
|
1196
|
+
} else if (cmdArgs[0] === "guard") {
|
|
1197
|
+
const { runCtxGuard } = require("./runners/runCtxGuard");
|
|
1198
|
+
exitCode = await runCtxGuard.main(cmdArgs.slice(1));
|
|
1199
|
+
} else if (cmdArgs[0] === "diff") {
|
|
1200
|
+
const { main: ctxDiffMain } = require("./runners/runCtxDiff");
|
|
1201
|
+
exitCode = await ctxDiffMain(cmdArgs.slice(1));
|
|
1202
|
+
} else if (cmdArgs[0] === "search") {
|
|
1203
|
+
const { runContext } = require("./runners/runContext");
|
|
1204
|
+
exitCode = await runContext(["--search", ...cmdArgs.slice(1)]);
|
|
1205
|
+
} else {
|
|
1206
|
+
exitCode = await runner(cmdArgs, context);
|
|
1207
|
+
}
|
|
1208
|
+
break;
|
|
1209
|
+
|
|
1210
|
+
case "status":
|
|
1211
|
+
exitCode = await runner({ ...context, json: cmdArgs.includes("--json") });
|
|
1212
|
+
break;
|
|
1213
|
+
|
|
1214
|
+
default:
|
|
1215
|
+
exitCode = await runner(cmdArgs);
|
|
573
1216
|
}
|
|
574
|
-
} catch (error) {
|
|
575
|
-
|
|
576
|
-
|
|
1217
|
+
} catch (error) {
|
|
1218
|
+
console.error(formatError(error, config));
|
|
1219
|
+
exitCode = 1;
|
|
1220
|
+
}
|
|
1221
|
+
|
|
1222
|
+
// Create receipt for paid commands
|
|
577
1223
|
if (cmdDef.tier !== "free" && runId) {
|
|
578
1224
|
try {
|
|
579
1225
|
const { createManifestAndReceipt } = require("./runners/lib/receipts");
|
|
@@ -585,24 +1231,59 @@ async function main() {
|
|
|
585
1231
|
exitCode,
|
|
586
1232
|
startTime: runStart,
|
|
587
1233
|
endTime: new Date().toISOString(),
|
|
588
|
-
projectPath:
|
|
1234
|
+
projectPath: globalFlags.path,
|
|
589
1235
|
});
|
|
590
1236
|
} catch (e) {
|
|
591
|
-
// Don't fail the command if receipt creation fails
|
|
592
1237
|
if (config.debug) console.error(`Failed to create receipt: ${e.message}`);
|
|
593
1238
|
}
|
|
594
1239
|
}
|
|
595
|
-
|
|
596
|
-
|
|
1240
|
+
|
|
1241
|
+
// Show update notice after command (if available)
|
|
1242
|
+
if (updatePromise) {
|
|
1243
|
+
const latestVersion = await updatePromise;
|
|
1244
|
+
if (latestVersion) {
|
|
1245
|
+
printUpdateNotice(latestVersion);
|
|
1246
|
+
}
|
|
1247
|
+
}
|
|
1248
|
+
|
|
1249
|
+
// Debug timing
|
|
1250
|
+
if (config.debug) {
|
|
1251
|
+
console.log(`\n${c.dim}${sym.clock} Total: ${(performance.now() - startTime).toFixed(0)}ms${c.reset}`);
|
|
1252
|
+
}
|
|
1253
|
+
|
|
597
1254
|
process.exit(exitCode);
|
|
598
1255
|
}
|
|
599
1256
|
|
|
600
1257
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
601
1258
|
// GRACEFUL SHUTDOWN
|
|
602
1259
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
603
|
-
process.on("SIGINT", () => {
|
|
604
|
-
|
|
605
|
-
process.
|
|
606
|
-
|
|
1260
|
+
process.on("SIGINT", () => {
|
|
1261
|
+
console.log(`\n${c.yellow}${sym.warning}${c.reset} Interrupted`);
|
|
1262
|
+
process.exit(130);
|
|
1263
|
+
});
|
|
1264
|
+
|
|
1265
|
+
process.on("SIGTERM", () => {
|
|
1266
|
+
console.log(`\n${c.yellow}${sym.warning}${c.reset} Terminated`);
|
|
1267
|
+
process.exit(143);
|
|
1268
|
+
});
|
|
1269
|
+
|
|
1270
|
+
process.on("uncaughtException", (error) => {
|
|
1271
|
+
console.error(`\n${c.red}${sym.error} Uncaught Exception:${c.reset} ${error.message}`);
|
|
1272
|
+
if (process.env.VIBECHECK_DEBUG === "true") {
|
|
1273
|
+
console.error(c.dim + error.stack + c.reset);
|
|
1274
|
+
}
|
|
1275
|
+
process.exit(1);
|
|
1276
|
+
});
|
|
1277
|
+
|
|
1278
|
+
process.on("unhandledRejection", (reason) => {
|
|
1279
|
+
console.error(`\n${c.red}${sym.error} Unhandled Rejection:${c.reset} ${reason}`);
|
|
1280
|
+
process.exit(1);
|
|
1281
|
+
});
|
|
607
1282
|
|
|
608
|
-
|
|
1283
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
1284
|
+
// RUN
|
|
1285
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
1286
|
+
main().catch((error) => {
|
|
1287
|
+
console.error(`\n${c.red}${sym.error} Fatal:${c.reset} ${error.message}`);
|
|
1288
|
+
process.exit(1);
|
|
1289
|
+
});
|