@vibecheckai/cli 3.0.10 → 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.
package/bin/vibecheck.js CHANGED
@@ -12,6 +12,7 @@ const path = require("path");
12
12
  const fs = require("fs");
13
13
  const os = require("os");
14
14
  const { performance } = require("perf_hooks");
15
+ const crypto = require("crypto");
15
16
 
16
17
  // ═══════════════════════════════════════════════════════════════════════════════
17
18
  // PERFORMANCE: Track startup time
@@ -235,58 +236,28 @@ function findSimilarCommands(input, commands, maxDistance = 3) {
235
236
  const entitlements = require("./runners/lib/entitlements-v2");
236
237
 
237
238
  // ═══════════════════════════════════════════════════════════════════════════════
238
- // COMMAND REGISTRY - Tiers match entitlements-v2.js EXACTLY
239
+ // UPSELL COPY MODULE - Central copy generator for all upgrade messaging
239
240
  // ═══════════════════════════════════════════════════════════════════════════════
240
- const COMMANDS = {
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)
274
- login: { description: "Authenticate with API key", tier: "free", category: "account", aliases: ["auth", "signin"], runner: () => require("./runners/runAuth").runLogin, skipAuth: true },
275
- logout: { description: "Remove stored credentials", tier: "free", category: "account", aliases: ["signout"], runner: () => require("./runners/runAuth").runLogout, skipAuth: true },
276
- whoami: { description: "Show current user and plan", tier: "free", category: "account", aliases: ["me", "user"], runner: () => require("./runners/runAuth").runWhoami, skipAuth: true },
277
-
278
- // EXTRAS
279
- labs: { description: "Experimental features", tier: "free", category: "extras", aliases: ["experimental", "beta"], runner: () => require("./runners/runLabs").runLabs },
280
- };
241
+ const upsell = require("./runners/lib/upsell");
281
242
 
282
- const ALIAS_MAP = {};
283
- for (const [cmd, def] of Object.entries(COMMANDS)) { for (const alias of def.aliases || []) ALIAS_MAP[alias] = cmd; }
284
- const ALL_COMMANDS = [...Object.keys(COMMANDS), ...Object.values(COMMANDS).flatMap(c => c.aliases || [])];
243
+ // ═══════════════════════════════════════════════════════════════════════════════
244
+ // CLI OUTPUT UTILITIES
245
+ // ═══════════════════════════════════════════════════════════════════════════════
246
+ const {
247
+ generateRunId,
248
+ withStandardOutput,
249
+ parseStandardFlags,
250
+ exitCodeToVerdict
251
+ } = require("./runners/lib/cli-output");
252
+
253
+ // ═══════════════════════════════════════════════════════════════════════════════
254
+ // COMMAND REGISTRY - Imported from bin/registry.js (single source of truth)
255
+ // ═══════════════════════════════════════════════════════════════════════════════
256
+ const { COMMANDS, ALIAS_MAP, ALL_COMMANDS, getRunner: _getRunner } = require("./registry");
285
257
 
258
+ // Wrap getRunner to pass styling
286
259
  function getRunner(cmd) {
287
- const def = COMMANDS[cmd];
288
- if (!def) return null;
289
- try { return def.runner(); } catch (e) { return async () => { console.error(`${c.red}${sym.error}${c.reset} Failed to load ${cmd}: ${e.message}`); return 1; }; }
260
+ return _getRunner(cmd, { red: c.red, reset: c.reset, errorSymbol: sym.error });
290
261
  }
291
262
 
292
263
  // ═══════════════════════════════════════════════════════════════════════════════
@@ -331,22 +302,11 @@ async function checkCommandAccess(cmd, args, authInfo) {
331
302
  }
332
303
 
333
304
  function formatAccessDenied(cmd, requiredTier, currentTier) {
334
- const tierColors = { starter: c.cyan, pro: c.magenta, enterprise: c.yellow };
335
- const tierColor = tierColors[requiredTier] || c.white;
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
-
349
- return msg;
305
+ // Use centralized upsell copy module
306
+ return upsell.formatDenied(cmd, {
307
+ currentTier,
308
+ requiredTier,
309
+ });
350
310
  }
351
311
 
352
312
  // ═══════════════════════════════════════════════════════════════════════════════
@@ -456,52 +416,71 @@ async function main() {
456
416
  const config = loadConfig();
457
417
  const state = loadState();
458
418
 
459
- const globalFlags = {
460
- debug: rawArgs.includes("--debug") || rawArgs.includes("-d"),
461
- verbose: rawArgs.includes("--verbose") || rawArgs.includes("-v"),
462
- quiet: rawArgs.includes("--quiet") || rawArgs.includes("-q"),
463
- help: rawArgs.includes("--help") || rawArgs.includes("-h"),
464
- version: rawArgs.includes("--version") || rawArgs.includes("-V"),
465
- };
419
+ // Parse standard flags
420
+ const { flags: globalFlags, parsed: cleanArgs } = parseStandardFlags(rawArgs);
466
421
 
422
+ // Update config based on flags
467
423
  if (globalFlags.debug) config.debug = true;
468
424
  if (globalFlags.verbose) config.verbose = true;
469
425
  if (globalFlags.quiet) config.quiet = true;
426
+ if (globalFlags.ci) config.quiet = true; // CI mode implies quiet
470
427
 
471
- const args = rawArgs.filter(a => !["--debug", "-d", "--verbose", "-v", "--quiet", "-q", "--help", "-h", "--version", "-V"].includes(a));
472
-
473
- if (globalFlags.version || args[0] === "version") {
428
+ // Handle version
429
+ if (globalFlags.version) {
474
430
  console.log(`vibecheck v${VERSION}`);
475
431
  process.exit(0);
476
432
  }
477
433
 
478
- if (!args[0]) { printHelp(); process.exit(0); }
434
+ // Handle no command
435
+ if (!cleanArgs[0]) { printHelp(); process.exit(0); }
479
436
 
480
437
  // Handle command-specific help (vibecheck <cmd> --help)
481
- if (globalFlags.help && args[0] && COMMANDS[args[0]]) {
438
+ if (globalFlags.help && cleanArgs[0] && COMMANDS[cleanArgs[0]]) {
482
439
  // Pass --help to the command runner
483
- } else if (globalFlags.help && !args[0]) {
440
+ } else if (globalFlags.help && !cleanArgs[0]) {
484
441
  printHelp(); process.exit(0);
485
442
  }
486
443
 
487
- let cmd = args[0];
444
+ let cmd = cleanArgs[0];
488
445
  if (ALIAS_MAP[cmd]) { cmd = ALIAS_MAP[cmd]; }
489
- let cmdArgs = args.slice(1);
446
+ let cmdArgs = cleanArgs.slice(1);
490
447
 
491
448
  // Add --help back to cmdArgs if it was passed with a command
492
449
  if (globalFlags.help) cmdArgs = ["--help", ...cmdArgs];
493
450
 
451
+ // Pass standard flags to runners
452
+ cmdArgs = [...cmdArgs];
453
+ if (globalFlags.json) cmdArgs.push("--json");
454
+ if (globalFlags.ci) cmdArgs.push("--ci");
455
+ if (globalFlags.path && globalFlags.path !== process.cwd()) {
456
+ cmdArgs.push("--path", globalFlags.path);
457
+ }
458
+ if (globalFlags.output) {
459
+ cmdArgs.push("--output", globalFlags.output);
460
+ }
461
+ if (globalFlags.verbose) cmdArgs.push("--verbose");
462
+ if (globalFlags.strict) cmdArgs.push("--strict");
463
+
494
464
  if (!COMMANDS[cmd]) {
495
465
  const suggestions = findSimilarCommands(cmd, ALL_COMMANDS);
496
466
  console.log(`\n${c.red}${sym.error}${c.reset} Unknown command: ${c.yellow}${cmd}${c.reset}`);
497
- if (suggestions.length > 0) {
467
+
468
+ // Check for specific common misses
469
+ if (cmd === "replay" || cmd === "record") {
470
+ console.log(`\n${c.dim}replay is a PRO feature for session recording.${c.reset}`);
471
+ console.log(`${c.dim}Free alternative:${c.reset} ${c.cyan}vibecheck reality${c.reset} ${c.dim}(one-time runtime proof)${c.reset}`);
472
+ } else if (suggestions.length > 0) {
498
473
  console.log(`\n${c.dim}Did you mean:${c.reset}`);
499
474
  suggestions.forEach(s => { const actual = ALIAS_MAP[s] || s; const def = COMMANDS[actual]; console.log(` ${c.cyan}vibecheck ${s}${c.reset} ${c.dim}${def?.description || ""}${c.reset}`); });
500
475
  }
501
476
  console.log(`\n${c.dim}Run 'vibecheck --help' for available commands.${c.reset}\n`);
502
- process.exit(1);
477
+ process.exit(4); // INVALID_INPUT
503
478
  }
504
479
 
480
+ // Generate runId for tracking
481
+ const runId = generateRunId();
482
+ const runStart = new Date().toISOString();
483
+
505
484
  const cmdDef = COMMANDS[cmd];
506
485
  let authInfo = { key: null };
507
486
 
@@ -516,18 +495,22 @@ async function main() {
516
495
  if (!access.allowed) {
517
496
  console.log(access.reason);
518
497
  // Use proper exit code: 3 = feature not allowed
519
- process.exit(access.exitCode || entitlements.EXIT_FEATURE_NOT_ALLOWED);
498
+ process.exit(access.exitCode || 3);
520
499
  }
521
500
 
522
- // Show downgrade notice if applicable
501
+ // Show downgrade notice if applicable (single-line at start)
523
502
  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)`);
503
+ console.log(upsell.formatDowngrade(cmd, {
504
+ currentTier: access.tier,
505
+ effectiveMode: access.downgrade,
506
+ caps: access.caps,
507
+ }));
525
508
  }
526
509
 
527
510
  // Show tier badge
528
511
  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}`);
512
+ if (access.tier === "pro") console.log(`${c.magenta}${sym.arrowRight} PRO${c.reset} ${c.dim}feature${c.reset}`);
513
+ else if (access.tier === "complete") console.log(`${c.yellow}${sym.arrowRight} COMPLETE${c.reset} ${c.dim}feature${c.reset}`);
531
514
  }
532
515
 
533
516
  // Attach access info for runners to use
@@ -543,27 +526,58 @@ async function main() {
543
526
  const runner = getRunner(cmd);
544
527
  if (!runner) { console.error(`${c.red}${sym.error}${c.reset} Failed to load runner for: ${cmd}`); process.exit(1); }
545
528
 
546
- const context = { repoRoot: process.cwd(), config, state, authInfo, version: VERSION, isCI: isCI() };
529
+ const context = {
530
+ repoRoot: process.cwd(),
531
+ config,
532
+ state,
533
+ authInfo,
534
+ version: VERSION,
535
+ isCI: isCI(),
536
+ runId,
537
+ runStart
538
+ };
547
539
 
540
+ // Pass context to runners that support it
548
541
  switch (cmd) {
549
- case "prove": exitCode = await runner(cmdArgs); break;
550
- case "reality": exitCode = await runner(cmdArgs); break;
551
- case "watch": exitCode = await runner(cmdArgs); break;
542
+ case "prove": exitCode = await runner(cmdArgs, context); break;
543
+ case "reality": exitCode = await runner(cmdArgs, context); break;
544
+ case "watch": exitCode = await runner(cmdArgs, context); break;
545
+ case "ship": exitCode = await runner(cmdArgs, context); break;
552
546
  case "ctx": case "truthpack":
553
547
  if (cmdArgs[0] === "sync") { const { runCtxSync } = require("./runners/runCtxSync"); exitCode = await runCtxSync({ ...context, fastifyEntry: getArgValue(cmdArgs, ["--fastify-entry"]) }); }
554
548
  else if (cmdArgs[0] === "guard") { const { runCtxGuard } = require("./runners/runCtxGuard"); exitCode = await runCtxGuard.main(cmdArgs.slice(1)); }
555
549
  else if (cmdArgs[0] === "diff") { const { main: ctxDiffMain } = require("./runners/runCtxDiff"); exitCode = await ctxDiffMain(cmdArgs.slice(1)); }
556
550
  else if (cmdArgs[0] === "search") { const { runContext } = require("./runners/runContext"); exitCode = await runContext(["--search", ...cmdArgs.slice(1)]); }
557
- else { exitCode = await runner(cmdArgs); }
551
+ else { exitCode = await runner(cmdArgs, context); }
558
552
  break;
559
- case "install": exitCode = await runner(cmdArgs); break;
553
+ case "install": exitCode = await runner(cmdArgs, context); break;
560
554
  case "status": exitCode = await runner({ ...context, json: cmdArgs.includes("--json") }); break;
561
- case "pr": exitCode = await runner(cmdArgs); break;
562
- case "share": exitCode = await runner(cmdArgs); break;
555
+ case "pr": exitCode = await runner(cmdArgs, context); break;
556
+ case "share": exitCode = await runner(cmdArgs, context); break;
563
557
  default: exitCode = await runner(cmdArgs);
564
558
  }
565
559
  } catch (error) { console.error(formatError(error, config)); exitCode = 1; }
566
560
 
561
+ // Create manifest and receipt for paid commands
562
+ if (cmdDef.tier !== "free" && runId) {
563
+ try {
564
+ const { createManifestAndReceipt } = require("./runners/lib/receipts");
565
+ await createManifestAndReceipt({
566
+ runId,
567
+ command: cmd,
568
+ args: cmdArgs,
569
+ tier: authInfo.access?.tier || "free",
570
+ exitCode,
571
+ startTime: runStart,
572
+ endTime: new Date().toISOString(),
573
+ projectPath: process.cwd(),
574
+ });
575
+ } catch (e) {
576
+ // Don't fail the command if receipt creation fails
577
+ if (config.debug) console.error(`Failed to create receipt: ${e.message}`);
578
+ }
579
+ }
580
+
567
581
  if (config.debug) console.log(`\n${c.dim}${sym.clock} Total: ${(performance.now() - startTime).toFixed(0)}ms${c.reset}`);
568
582
  process.exit(exitCode);
569
583
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vibecheck-mcp-server",
3
- "version": "3.0.9",
3
+ "version": "3.1.0",
4
4
  "description": "Professional MCP server for vibecheck - Intelligent development environment vibechecks",
5
5
  "type": "module",
6
6
  "main": "index.js",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vibecheckai/cli",
3
- "version": "3.0.10",
3
+ "version": "3.1.0",
4
4
  "description": "Vibecheck CLI - Ship with confidence. One verdict: SHIP | WARN | BLOCK.",
5
5
  "main": "bin/vibecheck.js",
6
6
  "bin": {