@vibecheckai/cli 3.0.8 → 3.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +77 -484
- package/bin/runners/lib/__tests__/entitlements-v2.test.js +295 -0
- package/bin/runners/lib/entitlements-v2.js +409 -299
- package/bin/runners/runFix.js +20 -0
- package/bin/runners/runMcp.js +58 -0
- package/bin/runners/runPR.js +80 -12
- package/bin/runners/runReality.js +48 -2
- package/bin/runners/runReport.js +40 -0
- package/bin/runners/runShip.js +97 -1
- package/bin/runners/runWatch.js +63 -6
- package/bin/vibecheck.js +158 -59
- package/package.json +1 -1
package/bin/vibecheck.js
CHANGED
|
@@ -230,34 +230,53 @@ function findSimilarCommands(input, commands, maxDistance = 3) {
|
|
|
230
230
|
}
|
|
231
231
|
|
|
232
232
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
233
|
-
//
|
|
233
|
+
// ENTITLEMENTS (v2 - Single Source of Truth)
|
|
234
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
235
|
+
const entitlements = require("./runners/lib/entitlements-v2");
|
|
236
|
+
|
|
237
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
238
|
+
// COMMAND REGISTRY - Tiers match entitlements-v2.js EXACTLY
|
|
234
239
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
235
240
|
const COMMANDS = {
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
prove: { description: "
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
241
|
+
// PROOF LOOP
|
|
242
|
+
scan: { description: "Static analysis - routes, secrets, contracts", tier: "free", category: "proof", aliases: ["s", "check"], runner: () => require("./runners/runScan").runScan },
|
|
243
|
+
ship: { description: "Verdict engine - SHIP / WARN / BLOCK", tier: "free", category: "proof", aliases: ["verdict"], caps: "static-only on FREE", runner: () => require("./runners/runShip").runShip },
|
|
244
|
+
reality: { description: "Runtime proof - Playwright clicks every button", tier: "free", category: "proof", aliases: ["r", "test", "e2e"], caps: "preview mode on FREE (5 pages, no auth)", runner: () => { try { return require("./runners/runReality").runReality; } catch (e) { return async () => { console.error("Reality runner unavailable:", e.message); return 1; }; } } },
|
|
245
|
+
prove: { description: "Full proof loop - ctx → reality → ship → fix", tier: "pro", category: "proof", aliases: ["p", "full", "all"], runner: () => require("./runners/runProve").runProve },
|
|
246
|
+
fix: { description: "AI-powered auto-fix", tier: "free", category: "proof", caps: "--plan-only on FREE/STARTER", aliases: ["f", "repair"], runner: () => require("./runners/runFix").runFix },
|
|
247
|
+
report: { description: "Generate HTML/MD/SARIF reports", tier: "free", category: "proof", caps: "HTML/MD only on FREE", aliases: ["html", "artifact"], runner: () => require("./runners/runReport").runReport },
|
|
248
|
+
|
|
249
|
+
// SETUP & DX
|
|
250
|
+
install: { description: "Zero-friction onboarding", tier: "free", category: "setup", aliases: ["i", "bootstrap"], runner: () => require("./runners/runInstall").runInstall },
|
|
251
|
+
init: { description: "Project setup wizard", tier: "free", category: "setup", aliases: ["setup", "configure"], runner: () => require("./runners/runInit").runInit },
|
|
252
|
+
doctor: { description: "Environment diagnostics", tier: "free", category: "setup", aliases: ["health", "diag"], runner: () => require("./runners/runDoctor").runDoctor },
|
|
253
|
+
status: { description: "Project health dashboard", tier: "free", category: "setup", aliases: ["st"], runner: () => require("./runners/runStatus").runStatus },
|
|
254
|
+
watch: { description: "Continuous mode - re-runs on changes", tier: "free", category: "setup", aliases: ["w", "dev"], runner: () => require("./runners/runWatch").runWatch },
|
|
255
|
+
launch: { description: "Pre-launch checklist wizard", tier: "starter", category: "setup", aliases: ["checklist", "preflight"], runner: () => require("./runners/runLaunch").runLaunch },
|
|
256
|
+
|
|
257
|
+
// AI TRUTH
|
|
258
|
+
ctx: { description: "Generate truthpack for AI agents", tier: "free", category: "truth", aliases: ["truthpack", "tp"], subcommands: ["build", "diff", "guard", "sync", "search"], runner: () => require("./runners/runCtx").runCtx },
|
|
259
|
+
guard: { description: "Validate AI claims against truth", tier: "free", category: "truth", aliases: ["validate", "trust"], runner: () => require("./runners/runGuard").runGuard },
|
|
260
|
+
context: { description: "Generate .cursorrules, .windsurf/rules", tier: "free", category: "truth", aliases: ["rules", "ai-rules"], runner: () => require("./runners/runContext").runContext },
|
|
261
|
+
mdc: { description: "Generate MDC specifications", tier: "free", category: "truth", aliases: [], runner: () => require("./runners/runMdc").runMdc },
|
|
262
|
+
|
|
263
|
+
// CI & COLLABORATION (STARTER+)
|
|
264
|
+
gate: { description: "CI/CD gate - blocks deploys on failures", tier: "starter", category: "ci", aliases: ["ci", "block"], runner: () => require("./runners/runGate").runGate },
|
|
265
|
+
pr: { description: "Generate PR comment with findings", tier: "starter", category: "ci", aliases: ["pull-request"], runner: () => require("./runners/runPR").runPR },
|
|
266
|
+
badge: { description: "Generate ship badge for README", tier: "starter", category: "ci", aliases: ["b"], runner: () => require("./runners/runBadge").runBadge },
|
|
267
|
+
|
|
268
|
+
// AUTOMATION (STARTER+/PRO)
|
|
269
|
+
mcp: { description: "Start MCP server for AI IDEs", tier: "starter", category: "automation", aliases: [], runner: () => require("./runners/runMcp").runMcp },
|
|
270
|
+
share: { description: "Generate share pack for PR/docs", tier: "pro", category: "automation", aliases: [], runner: () => require("./runners/runShare").runShare },
|
|
271
|
+
"ai-test": { description: "AI autonomous test generation", tier: "pro", category: "automation", aliases: ["ai", "agent"], runner: () => require("./runners/runAIAgent").runAIAgent },
|
|
272
|
+
|
|
273
|
+
// ACCOUNT (always free)
|
|
255
274
|
login: { description: "Authenticate with API key", tier: "free", category: "account", aliases: ["auth", "signin"], runner: () => require("./runners/runAuth").runLogin, skipAuth: true },
|
|
256
275
|
logout: { description: "Remove stored credentials", tier: "free", category: "account", aliases: ["signout"], runner: () => require("./runners/runAuth").runLogout, skipAuth: true },
|
|
257
276
|
whoami: { description: "Show current user and plan", tier: "free", category: "account", aliases: ["me", "user"], runner: () => require("./runners/runAuth").runWhoami, skipAuth: true },
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
277
|
+
|
|
278
|
+
// EXTRAS
|
|
279
|
+
labs: { description: "Experimental features", tier: "free", category: "extras", aliases: ["experimental", "beta"], runner: () => require("./runners/runLabs").runLabs },
|
|
261
280
|
};
|
|
262
281
|
|
|
263
282
|
const ALIAS_MAP = {};
|
|
@@ -271,27 +290,62 @@ function getRunner(cmd) {
|
|
|
271
290
|
}
|
|
272
291
|
|
|
273
292
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
274
|
-
// AUTH & ACCESS CONTROL
|
|
293
|
+
// AUTH & ACCESS CONTROL (uses entitlements-v2 - NO BYPASS ALLOWED)
|
|
275
294
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
276
295
|
let authModule = null;
|
|
277
296
|
function getAuthModule() { if (!authModule) authModule = require("./runners/lib/auth"); return authModule; }
|
|
278
297
|
|
|
279
|
-
|
|
298
|
+
/**
|
|
299
|
+
* Check command access using entitlements-v2 module.
|
|
300
|
+
* NO OWNER MODE. NO ENV VAR BYPASS. NO OFFLINE ESCALATION.
|
|
301
|
+
*/
|
|
302
|
+
async function checkCommandAccess(cmd, args, authInfo) {
|
|
280
303
|
const def = COMMANDS[cmd];
|
|
281
304
|
if (!def) return { allowed: true };
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
const
|
|
285
|
-
|
|
286
|
-
|
|
305
|
+
|
|
306
|
+
// Use centralized entitlements enforcement
|
|
307
|
+
const result = await entitlements.enforce(cmd, {
|
|
308
|
+
apiKey: authInfo?.key,
|
|
309
|
+
projectPath: process.cwd(),
|
|
310
|
+
silent: true, // We'll handle messaging ourselves
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
if (result.allowed) {
|
|
314
|
+
return {
|
|
315
|
+
allowed: true,
|
|
316
|
+
tier: result.tier,
|
|
317
|
+
downgrade: result.downgrade,
|
|
318
|
+
limits: result.limits,
|
|
319
|
+
caps: result.caps,
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// Not allowed - return with proper exit code
|
|
324
|
+
return {
|
|
325
|
+
allowed: false,
|
|
326
|
+
tier: result.tier,
|
|
327
|
+
requiredTier: result.requiredTier,
|
|
328
|
+
exitCode: result.exitCode,
|
|
329
|
+
reason: formatAccessDenied(cmd, result.requiredTier, result.tier),
|
|
330
|
+
};
|
|
287
331
|
}
|
|
288
332
|
|
|
289
|
-
function formatAccessDenied(cmd, requiredTier,
|
|
333
|
+
function formatAccessDenied(cmd, requiredTier, currentTier) {
|
|
290
334
|
const tierColors = { starter: c.cyan, pro: c.magenta, enterprise: c.yellow };
|
|
291
335
|
const tierColor = tierColors[requiredTier] || c.white;
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
336
|
+
const tierLabel = entitlements.getTierLabel(requiredTier);
|
|
337
|
+
const currentLabel = entitlements.getTierLabel(currentTier);
|
|
338
|
+
|
|
339
|
+
let msg = `\n${c.red}${c.bold}⛔ Feature Not Available${c.reset}\n\n`;
|
|
340
|
+
msg += ` ${c.yellow}${cmd}${c.reset} requires ${tierColor}${tierLabel}${c.reset} plan.\n`;
|
|
341
|
+
msg += ` Your current plan: ${c.dim}${currentLabel}${c.reset}\n\n`;
|
|
342
|
+
|
|
343
|
+
if (currentTier === "free") {
|
|
344
|
+
msg += ` ${c.cyan}Get started:${c.reset} vibecheck login\n`;
|
|
345
|
+
}
|
|
346
|
+
msg += ` ${c.cyan}Upgrade at:${c.reset} https://vibecheckai.dev/pricing\n`;
|
|
347
|
+
msg += `\n ${c.dim}Exit code: ${entitlements.EXIT_FEATURE_NOT_ALLOWED}${c.reset}\n`;
|
|
348
|
+
|
|
295
349
|
return msg;
|
|
296
350
|
}
|
|
297
351
|
|
|
@@ -309,40 +363,70 @@ ${c.dim}${sym.boxBottomLeft}${sym.boxHorizontal.repeat(60)}${sym.boxBottomRight}
|
|
|
309
363
|
|
|
310
364
|
function printHelp() {
|
|
311
365
|
printBanner();
|
|
366
|
+
|
|
367
|
+
// Categories ordered as specified
|
|
368
|
+
const categoryOrder = ["proof", "setup", "truth", "ci", "automation", "account", "extras"];
|
|
312
369
|
const categories = {
|
|
313
|
-
proof: { name: "
|
|
314
|
-
setup: { name: "SETUP &
|
|
315
|
-
truth: { name: "TRUTH
|
|
316
|
-
|
|
317
|
-
ci: { name: "CI/CD", color: c.cyan, icon: sym.rocket },
|
|
370
|
+
proof: { name: "PROOF LOOP", color: c.green, icon: sym.shield },
|
|
371
|
+
setup: { name: "SETUP & DX", color: c.yellow, icon: sym.gear },
|
|
372
|
+
truth: { name: "AI TRUTH", color: c.magenta, icon: sym.lightning },
|
|
373
|
+
ci: { name: "CI & COLLABORATION", color: c.cyan, icon: sym.rocket },
|
|
318
374
|
automation: { name: "AUTOMATION", color: c.blue, icon: sym.fire },
|
|
319
375
|
account: { name: "ACCOUNT", color: c.dim, icon: sym.key },
|
|
376
|
+
extras: { name: "EXTRAS", color: c.dim, icon: sym.star },
|
|
320
377
|
};
|
|
378
|
+
|
|
379
|
+
// Group commands
|
|
321
380
|
const grouped = {};
|
|
322
381
|
for (const [cmd, def] of Object.entries(COMMANDS)) {
|
|
323
382
|
const cat = def.category || "extras";
|
|
324
383
|
if (!grouped[cat]) grouped[cat] = [];
|
|
325
384
|
grouped[cat].push({ cmd, ...def });
|
|
326
385
|
}
|
|
327
|
-
|
|
328
|
-
|
|
386
|
+
|
|
387
|
+
// Print in order
|
|
388
|
+
for (const catKey of categoryOrder) {
|
|
389
|
+
const commands = grouped[catKey];
|
|
390
|
+
if (!commands || commands.length === 0) continue;
|
|
391
|
+
|
|
392
|
+
const cat = categories[catKey];
|
|
329
393
|
console.log(`\n${cat.color}${cat.icon} ${cat.name}${c.reset}\n`);
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
394
|
+
|
|
395
|
+
for (const { cmd, description, tier, aliases, caps } of commands) {
|
|
396
|
+
// Tier badge with color
|
|
397
|
+
let tierBadge = "";
|
|
398
|
+
if (tier === "free") {
|
|
399
|
+
tierBadge = `${c.green}[FREE]${c.reset} `;
|
|
400
|
+
} else if (tier === "starter") {
|
|
401
|
+
tierBadge = `${c.cyan}[STARTER]${c.reset} `;
|
|
402
|
+
} else if (tier === "pro") {
|
|
403
|
+
tierBadge = `${c.magenta}[PRO]${c.reset} `;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
// Caps info (e.g., "preview mode on FREE")
|
|
407
|
+
const capsStr = caps ? `${c.dim}(${caps})${c.reset}` : "";
|
|
408
|
+
|
|
409
|
+
console.log(` ${c.cyan}${cmd.padEnd(12)}${c.reset} ${tierBadge}${description} ${capsStr}`);
|
|
334
410
|
}
|
|
335
411
|
}
|
|
412
|
+
|
|
336
413
|
console.log(`
|
|
337
414
|
${c.dim}${sym.boxHorizontal.repeat(64)}${c.reset}
|
|
338
415
|
|
|
416
|
+
${c.green}TIERS${c.reset}
|
|
417
|
+
|
|
418
|
+
${c.green}FREE${c.reset} $0 scan, ship, ctx, doctor, report (HTML/MD)
|
|
419
|
+
${c.cyan}STARTER${c.reset} $29/mo + gate, launch, pr, badge, mcp, reality full
|
|
420
|
+
${c.magenta}PRO${c.reset} $99/mo + prove, fix apply, share, ai-test, compliance
|
|
421
|
+
|
|
339
422
|
${c.green}QUICK START${c.reset}
|
|
340
423
|
|
|
341
424
|
${c.bold}"Check my repo"${c.reset} ${c.cyan}vibecheck scan${c.reset}
|
|
342
|
-
${c.bold}"Can I ship?"${c.reset} ${c.cyan}vibecheck
|
|
343
|
-
${c.bold}"
|
|
425
|
+
${c.bold}"Can I ship?"${c.reset} ${c.cyan}vibecheck ship${c.reset}
|
|
426
|
+
${c.bold}"Full proof loop"${c.reset} ${c.cyan}vibecheck prove --url http://localhost:3000${c.reset} ${c.magenta}[PRO]${c.reset}
|
|
344
427
|
|
|
345
428
|
${c.dim}Run 'vibecheck <command> --help' for command-specific help.${c.reset}
|
|
429
|
+
${c.dim}Pricing: https://vibecheckai.dev/pricing${c.reset}
|
|
346
430
|
`);
|
|
347
431
|
}
|
|
348
432
|
|
|
@@ -419,20 +503,35 @@ async function main() {
|
|
|
419
503
|
}
|
|
420
504
|
|
|
421
505
|
const cmdDef = COMMANDS[cmd];
|
|
422
|
-
let authInfo = { key: null
|
|
506
|
+
let authInfo = { key: null };
|
|
423
507
|
|
|
424
508
|
if (!cmdDef.skipAuth) {
|
|
425
509
|
const auth = getAuthModule();
|
|
426
510
|
const { key } = auth.getApiKey();
|
|
511
|
+
authInfo.key = key;
|
|
427
512
|
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
513
|
+
// Use entitlements-v2 for access control (NO BYPASS)
|
|
514
|
+
const access = await checkCommandAccess(cmd, cmdArgs, authInfo);
|
|
515
|
+
|
|
516
|
+
if (!access.allowed) {
|
|
517
|
+
console.log(access.reason);
|
|
518
|
+
// Use proper exit code: 3 = feature not allowed
|
|
519
|
+
process.exit(access.exitCode || entitlements.EXIT_FEATURE_NOT_ALLOWED);
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
// Show downgrade notice if applicable
|
|
523
|
+
if (access.downgrade && !config.quiet) {
|
|
524
|
+
console.log(`${c.yellow}${sym.warning}${c.reset} Running in ${c.yellow}${access.downgrade}${c.reset} mode (upgrade for full access)`);
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
// Show tier badge
|
|
528
|
+
if (!config.quiet) {
|
|
529
|
+
if (access.tier === "starter") console.log(`${c.cyan}${sym.arrowRight} STARTER${c.reset} ${c.dim}feature${c.reset}`);
|
|
530
|
+
else if (access.tier === "pro") console.log(`${c.magenta}${sym.arrowRight} PRO${c.reset} ${c.dim}feature${c.reset}`);
|
|
531
|
+
}
|
|
431
532
|
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
if (access.tier === "starter" && !config.quiet) console.log(`${c.cyan}${sym.arrowRight} STARTER${c.reset} ${c.dim}feature${c.reset}`);
|
|
435
|
-
else if (access.tier === "pro" && !config.quiet) console.log(`${c.magenta}${sym.arrowRight} PRO${c.reset} ${c.dim}feature${c.reset}`);
|
|
533
|
+
// Attach access info for runners to use
|
|
534
|
+
authInfo.access = access;
|
|
436
535
|
}
|
|
437
536
|
|
|
438
537
|
state.runCount++; state.lastRun = Date.now();
|
|
@@ -448,18 +547,18 @@ async function main() {
|
|
|
448
547
|
|
|
449
548
|
switch (cmd) {
|
|
450
549
|
case "prove": exitCode = await runner(cmdArgs); break;
|
|
451
|
-
case "reality": exitCode = await runner(
|
|
452
|
-
case "watch": exitCode = await runner(
|
|
550
|
+
case "reality": exitCode = await runner(cmdArgs); break;
|
|
551
|
+
case "watch": exitCode = await runner(cmdArgs); break;
|
|
453
552
|
case "ctx": case "truthpack":
|
|
454
553
|
if (cmdArgs[0] === "sync") { const { runCtxSync } = require("./runners/runCtxSync"); exitCode = await runCtxSync({ ...context, fastifyEntry: getArgValue(cmdArgs, ["--fastify-entry"]) }); }
|
|
455
554
|
else if (cmdArgs[0] === "guard") { const { runCtxGuard } = require("./runners/runCtxGuard"); exitCode = await runCtxGuard.main(cmdArgs.slice(1)); }
|
|
456
555
|
else if (cmdArgs[0] === "diff") { const { main: ctxDiffMain } = require("./runners/runCtxDiff"); exitCode = await ctxDiffMain(cmdArgs.slice(1)); }
|
|
457
556
|
else if (cmdArgs[0] === "search") { const { runContext } = require("./runners/runContext"); exitCode = await runContext(["--search", ...cmdArgs.slice(1)]); }
|
|
458
|
-
else { exitCode = await runner(
|
|
557
|
+
else { exitCode = await runner(cmdArgs); }
|
|
459
558
|
break;
|
|
460
559
|
case "install": exitCode = await runner(cmdArgs); break;
|
|
461
560
|
case "status": exitCode = await runner({ ...context, json: cmdArgs.includes("--json") }); break;
|
|
462
|
-
case "pr": exitCode = await runner(
|
|
561
|
+
case "pr": exitCode = await runner(cmdArgs); break;
|
|
463
562
|
case "share": exitCode = await runner(cmdArgs); break;
|
|
464
563
|
default: exitCode = await runner(cmdArgs);
|
|
465
564
|
}
|