@vibecheckai/cli 3.6.1 → 3.8.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.
Files changed (105) hide show
  1. package/README.md +135 -63
  2. package/bin/_deprecations.js +447 -19
  3. package/bin/_router.js +1 -1
  4. package/bin/registry.js +347 -280
  5. package/bin/runners/context/generators/cursor-enhanced.js +2439 -0
  6. package/bin/runners/lib/agent-firewall/enforcement/gateway.js +1059 -0
  7. package/bin/runners/lib/agent-firewall/enforcement/index.js +98 -0
  8. package/bin/runners/lib/agent-firewall/enforcement/mode.js +318 -0
  9. package/bin/runners/lib/agent-firewall/enforcement/orchestrator.js +484 -0
  10. package/bin/runners/lib/agent-firewall/enforcement/proof-artifact.js +418 -0
  11. package/bin/runners/lib/agent-firewall/enforcement/schemas/change-event.schema.json +173 -0
  12. package/bin/runners/lib/agent-firewall/enforcement/schemas/intent.schema.json +181 -0
  13. package/bin/runners/lib/agent-firewall/enforcement/schemas/verdict.schema.json +222 -0
  14. package/bin/runners/lib/agent-firewall/enforcement/verdict-v2.js +333 -0
  15. package/bin/runners/lib/agent-firewall/index.js +200 -0
  16. package/bin/runners/lib/agent-firewall/integration/index.js +20 -0
  17. package/bin/runners/lib/agent-firewall/integration/ship-gate.js +437 -0
  18. package/bin/runners/lib/agent-firewall/intent/alignment-engine.js +622 -0
  19. package/bin/runners/lib/agent-firewall/intent/auto-detect.js +426 -0
  20. package/bin/runners/lib/agent-firewall/intent/index.js +102 -0
  21. package/bin/runners/lib/agent-firewall/intent/schema.js +352 -0
  22. package/bin/runners/lib/agent-firewall/intent/store.js +283 -0
  23. package/bin/runners/lib/agent-firewall/interception/fs-interceptor.js +502 -0
  24. package/bin/runners/lib/agent-firewall/interception/index.js +23 -0
  25. package/bin/runners/lib/agent-firewall/policy/rules/fake-success.js +31 -38
  26. package/bin/runners/lib/agent-firewall/policy/rules/ghost-env.js +68 -3
  27. package/bin/runners/lib/agent-firewall/policy/rules/ghost-route.js +4 -2
  28. package/bin/runners/lib/agent-firewall/risk/thresholds.js +5 -4
  29. package/bin/runners/lib/agent-firewall/session/collector.js +451 -0
  30. package/bin/runners/lib/agent-firewall/session/index.js +26 -0
  31. package/bin/runners/lib/artifact-envelope.js +540 -0
  32. package/bin/runners/lib/auth-shared.js +977 -0
  33. package/bin/runners/lib/checkpoint.js +941 -0
  34. package/bin/runners/lib/cleanup/engine.js +571 -0
  35. package/bin/runners/lib/cleanup/index.js +53 -0
  36. package/bin/runners/lib/cleanup/output.js +375 -0
  37. package/bin/runners/lib/cleanup/rules.js +1060 -0
  38. package/bin/runners/lib/doctor/diagnosis-receipt.js +454 -0
  39. package/bin/runners/lib/doctor/failure-signatures.js +526 -0
  40. package/bin/runners/lib/doctor/fix-script.js +336 -0
  41. package/bin/runners/lib/doctor/modules/build-tools.js +453 -0
  42. package/bin/runners/lib/doctor/modules/index.js +62 -3
  43. package/bin/runners/lib/doctor/modules/os-quirks.js +706 -0
  44. package/bin/runners/lib/doctor/modules/repo-integrity.js +485 -0
  45. package/bin/runners/lib/doctor/safe-repair.js +384 -0
  46. package/bin/runners/lib/engines/attack-detector.js +1192 -0
  47. package/bin/runners/lib/entitlements-v2.js +2 -2
  48. package/bin/runners/lib/error-messages.js +1 -1
  49. package/bin/runners/lib/missions/briefing.js +427 -0
  50. package/bin/runners/lib/missions/checkpoint.js +753 -0
  51. package/bin/runners/lib/missions/hardening.js +851 -0
  52. package/bin/runners/lib/missions/plan.js +421 -32
  53. package/bin/runners/lib/missions/safety-gates.js +645 -0
  54. package/bin/runners/lib/missions/schema.js +478 -0
  55. package/bin/runners/lib/packs/bundle.js +675 -0
  56. package/bin/runners/lib/packs/evidence-pack.js +671 -0
  57. package/bin/runners/lib/packs/pack-factory.js +837 -0
  58. package/bin/runners/lib/packs/permissions-pack.js +686 -0
  59. package/bin/runners/lib/packs/proof-graph-pack.js +779 -0
  60. package/bin/runners/lib/report-output.js +6 -6
  61. package/bin/runners/lib/safelist/index.js +96 -0
  62. package/bin/runners/lib/safelist/integration.js +334 -0
  63. package/bin/runners/lib/safelist/matcher.js +696 -0
  64. package/bin/runners/lib/safelist/schema.js +948 -0
  65. package/bin/runners/lib/safelist/store.js +438 -0
  66. package/bin/runners/lib/schemas/ship-manifest.schema.json +251 -0
  67. package/bin/runners/lib/ship-gate.js +832 -0
  68. package/bin/runners/lib/ship-manifest.js +1153 -0
  69. package/bin/runners/lib/ship-output.js +1 -1
  70. package/bin/runners/lib/unified-cli-output.js +710 -383
  71. package/bin/runners/lib/upsell.js +3 -3
  72. package/bin/runners/lib/why-tree.js +650 -0
  73. package/bin/runners/runAllowlist.js +33 -4
  74. package/bin/runners/runApprove.js +240 -1122
  75. package/bin/runners/runAudit.js +692 -0
  76. package/bin/runners/runAuth.js +325 -29
  77. package/bin/runners/runCheckpoint.js +442 -494
  78. package/bin/runners/runCleanup.js +343 -0
  79. package/bin/runners/runDoctor.js +269 -19
  80. package/bin/runners/runFix.js +411 -32
  81. package/bin/runners/runForge.js +411 -0
  82. package/bin/runners/runIntent.js +906 -0
  83. package/bin/runners/runKickoff.js +878 -0
  84. package/bin/runners/runLaunch.js +2000 -0
  85. package/bin/runners/runLink.js +785 -0
  86. package/bin/runners/runMcp.js +1741 -837
  87. package/bin/runners/runPacks.js +2089 -0
  88. package/bin/runners/runPolish.js +41 -0
  89. package/bin/runners/runSafelist.js +1190 -0
  90. package/bin/runners/runScan.js +21 -9
  91. package/bin/runners/runShield.js +1282 -0
  92. package/bin/runners/runShip.js +395 -16
  93. package/bin/vibecheck.js +34 -6
  94. package/mcp-server/README.md +117 -158
  95. package/mcp-server/handlers/tool-handler.ts +3 -3
  96. package/mcp-server/index.js +16 -0
  97. package/mcp-server/intent-firewall-interceptor.js +529 -0
  98. package/mcp-server/manifest.json +473 -0
  99. package/mcp-server/package.json +1 -1
  100. package/mcp-server/registry/tool-registry.js +315 -523
  101. package/mcp-server/registry/tools.json +442 -428
  102. package/mcp-server/tier-auth.js +164 -16
  103. package/mcp-server/tools-v3.js +70 -16
  104. package/package.json +1 -1
  105. package/bin/runners/runProof.zip +0 -0
@@ -22,6 +22,46 @@ const {
22
22
  } = require("./lib/cli-output");
23
23
  const { EXIT, verdictToExitCode, exitCodeToVerdict } = require("./lib/exit-codes");
24
24
 
25
+ // ═══════════════════════════════════════════════════════════════════════════════
26
+ // NEW: Ship Gate System (v2) - Deterministic, Evidence-backed
27
+ // ═══════════════════════════════════════════════════════════════════════════════
28
+ let _shipGate = null;
29
+ let _shipManifest = null;
30
+ let _whyTree = null;
31
+
32
+ function getShipGate() {
33
+ if (!_shipGate) {
34
+ try {
35
+ _shipGate = require("./lib/ship-gate");
36
+ } catch {
37
+ _shipGate = false;
38
+ }
39
+ }
40
+ return _shipGate;
41
+ }
42
+
43
+ function getShipManifest() {
44
+ if (!_shipManifest) {
45
+ try {
46
+ _shipManifest = require("./lib/ship-manifest");
47
+ } catch {
48
+ _shipManifest = false;
49
+ }
50
+ }
51
+ return _shipManifest;
52
+ }
53
+
54
+ function getWhyTree() {
55
+ if (!_whyTree) {
56
+ try {
57
+ _whyTree = require("./lib/why-tree");
58
+ } catch {
59
+ _whyTree = false;
60
+ }
61
+ }
62
+ return _whyTree;
63
+ }
64
+
25
65
  // Route Truth - Fake endpoint detection (V2 engine-aware)
26
66
  const { buildTruthpackSmart, writeTruthpack, detectFastifyEntry } = require("./lib/truth");
27
67
  const {
@@ -73,20 +113,23 @@ const {
73
113
  } = require("./lib/ship-output-enterprise");
74
114
 
75
115
  // ═══════════════════════════════════════════════════════════════════════════════
76
- // PREMIUM BANNER
116
+ // UNIFIED CLI OUTPUT
77
117
  // ═══════════════════════════════════════════════════════════════════════════════
78
118
 
79
- const BANNER = `
119
+ let _cliOutput = null;
120
+ function getCliOutput() {
121
+ if (!_cliOutput) _cliOutput = require("./lib/unified-cli-output");
122
+ return _cliOutput;
123
+ }
124
+
125
+ // Fallback banner if unified not available
126
+ const BANNER_FALLBACK = `
80
127
  ${ansi.rgb(0, 255, 200)} ██╗ ██╗██╗██████╗ ███████╗ ██████╗██╗ ██╗███████╗ ██████╗██╗ ██╗${ansi.reset}
81
128
  ${ansi.rgb(0, 230, 220)} ██║ ██║██║██╔══██╗██╔════╝██╔════╝██║ ██║██╔════╝██╔════╝██║ ██╔╝${ansi.reset}
82
129
  ${ansi.rgb(0, 200, 255)} ██║ ██║██║██████╔╝█████╗ ██║ ███████║█████╗ ██║ █████╔╝ ${ansi.reset}
83
130
  ${ansi.rgb(50, 150, 255)} ╚██╗ ██╔╝██║██╔══██╗██╔══╝ ██║ ██╔══██║██╔══╝ ██║ ██╔═██╗ ${ansi.reset}
84
131
  ${ansi.rgb(100, 100, 255)} ╚████╔╝ ██║██████╔╝███████╗╚██████╗██║ ██║███████╗╚██████╗██║ ██╗${ansi.reset}
85
132
  ${ansi.rgb(150, 50, 255)} ╚═══╝ ╚═╝╚═════╝ ╚══════╝ ╚═════╝╚═╝ ╚═╝╚══════╝ ╚═════╝╚═╝ ╚═╝${ansi.reset}
86
-
87
- ${ansi.dim} ┌─────────────────────────────────────────────────────────────────────┐${ansi.reset}
88
- ${ansi.dim} │${ansi.reset} ${ansi.rgb(0, 255, 200)}🚀${ansi.reset} ${ansi.bold}SHIP${ansi.reset} ${ansi.dim}•${ansi.reset} ${ansi.rgb(200, 200, 200)}The One Command${ansi.reset} ${ansi.dim}•${ansi.reset} ${ansi.rgb(150, 150, 150)}Zero Config${ansi.reset} ${ansi.dim}•${ansi.reset} ${ansi.rgb(100, 100, 100)}Plain English${ansi.reset} ${ansi.dim}│${ansi.reset}
89
- ${ansi.dim} └─────────────────────────────────────────────────────────────────────┘${ansi.reset}
90
133
  `;
91
134
 
92
135
  // ═══════════════════════════════════════════════════════════════════════════════
@@ -210,8 +253,8 @@ function stopSpinner(message, success = true) {
210
253
  spinnerStartTime = null;
211
254
  }
212
255
 
213
- // Compact banner for fix mode
214
- const SHIP_BANNER = `
256
+ // Compact banner for fix mode (fallback)
257
+ const SHIP_BANNER_FALLBACK = `
215
258
  ${ansi.rgb(0, 255, 200)} ███████╗██╗ ██╗██╗██████╗ ${ansi.reset}
216
259
  ${ansi.rgb(0, 230, 220)} ██╔════╝██║ ██║██║██╔══██╗${ansi.reset}
217
260
  ${ansi.rgb(0, 200, 255)} ███████╗███████║██║██████╔╝${ansi.reset}
@@ -220,8 +263,23 @@ ${ansi.rgb(100, 100, 255)} ███████║██║ ██║██
220
263
  ${ansi.rgb(150, 50, 255)} ╚══════╝╚═╝ ╚═╝╚═╝╚═╝ ${ansi.reset}
221
264
  `;
222
265
 
223
- function printBanner(compact = false) {
224
- console.log(compact ? SHIP_BANNER : BANNER);
266
+ function printBanner(compact = false, projectRoot = process.cwd()) {
267
+ if (compact) {
268
+ console.log(SHIP_BANNER_FALLBACK);
269
+ return;
270
+ }
271
+
272
+ try {
273
+ const cli = getCliOutput();
274
+ console.log(cli.renderFullHeader("ship", {
275
+ version: "4.0.0",
276
+ tier: "FREE",
277
+ target: projectRoot,
278
+ sessionId: cli.generateSessionId(),
279
+ }));
280
+ } catch {
281
+ console.log(BANNER_FALLBACK);
282
+ }
225
283
  }
226
284
 
227
285
  function printDivider(char = '─', width = 69, color = c.dim) {
@@ -627,15 +685,26 @@ function printHelp(showBanner = true) {
627
685
 
628
686
  ${ansi.bold}The One Command${ansi.reset} — Get a ship verdict: ${colors.success}SHIP${ansi.reset} | ${colors.warning}WARN${ansi.reset} | ${colors.error}BLOCK${ansi.reset}
629
687
 
630
- ${ansi.bold}Options:${ansi.reset}
688
+ ${ansi.bold}Verdict Options:${ansi.reset}
689
+ ${colors.accent}--strict${ansi.reset} Treat warnings as blockers
690
+ ${colors.accent}--why${ansi.reset} Show detailed "why tree" (top blockers + evidence)
691
+ ${colors.accent}--cached${ansi.reset} Use cached verdict if repo unchanged
692
+
693
+ ${ansi.bold}Output Options:${ansi.reset}
694
+ ${colors.accent}--json${ansi.reset} Output full manifest as JSON
695
+ ${colors.accent}--ci${ansi.reset} Machine output (KEY=VALUE format for CI/CD)
696
+ ${colors.accent}--receipt${ansi.reset} Single-line receipt (parseable)
697
+ ${colors.accent}--manifest${ansi.reset} Output full signed manifest
698
+ ${colors.accent}--badge, -b${ansi.reset} Generate embeddable badge for README
699
+
700
+ ${ansi.bold}Fix Options:${ansi.reset}
631
701
  ${colors.accent}--fix, -f${ansi.reset} Try safe mechanical fixes ${ansi.dim}(shows plan first)${ansi.reset}
632
702
  ${colors.accent}--assist${ansi.reset} Generate AI mission prompts for complex issues
633
- ${colors.accent}--badge, -b${ansi.reset} Generate embeddable badge for README
634
- ${colors.accent}--strict${ansi.reset} Treat warnings as blockers
635
- ${colors.accent}--ci${ansi.reset} Machine output for CI/CD pipelines
636
- ${colors.accent}--json${ansi.reset} Output results as JSON
703
+
704
+ ${ansi.bold}General Options:${ansi.reset}
637
705
  ${colors.accent}--path, -p${ansi.reset} Project path ${ansi.dim}(default: current directory)${ansi.reset}
638
706
  ${colors.accent}--verbose, -v${ansi.reset} Show detailed progress
707
+ ${colors.accent}--legacy${ansi.reset} Use legacy (non-gate) evaluation
639
708
  ${colors.accent}--help, -h${ansi.reset} Show this help
640
709
 
641
710
  ${ansi.bold}Exit Codes:${ansi.reset}
@@ -643,10 +712,23 @@ function printHelp(showBanner = true) {
643
712
  ${colors.warning}1${ansi.reset} WARN — Warnings found, review recommended
644
713
  ${colors.error}2${ansi.reset} BLOCK — Blockers found, must fix before shipping
645
714
 
715
+ ${ansi.bold}CI Integration:${ansi.reset}
716
+ ${ansi.dim}# GitHub Actions - set output variables${ansi.reset}
717
+ vibecheck ship --ci >> $$GITHUB_OUTPUT
718
+
719
+ ${ansi.dim}# Get receipt for artifact storage${ansi.reset}
720
+ vibecheck ship --receipt > .vibecheck/receipt.txt
721
+
722
+ ${ansi.dim}# Cache-aware evaluation (fast re-runs)${ansi.reset}
723
+ vibecheck ship --cached
724
+
646
725
  ${ansi.bold}Examples:${ansi.reset}
647
726
  ${ansi.dim}# Quick ship check${ansi.reset}
648
727
  vibecheck ship
649
728
 
729
+ ${ansi.dim}# Show why a verdict was reached${ansi.reset}
730
+ vibecheck ship --why
731
+
650
732
  ${ansi.dim}# Auto-fix what can be fixed${ansi.reset}
651
733
  vibecheck ship --fix
652
734
 
@@ -655,6 +737,11 @@ function printHelp(showBanner = true) {
655
737
 
656
738
  ${ansi.dim}# Strict CI mode (warnings = failure)${ansi.reset}
657
739
  vibecheck ship --strict --ci
740
+
741
+ ${ansi.bold}Artifacts Generated:${ansi.reset}
742
+ ${ansi.dim}.vibecheck/ship/manifest.json${ansi.reset} Full signed manifest
743
+ ${ansi.dim}.vibecheck/ship/receipt.txt${ansi.reset} CI-friendly receipt
744
+ ${ansi.dim}.vibecheck/ship/github-output.txt${ansi.reset} GitHub Actions output
658
745
  `);
659
746
  }
660
747
 
@@ -784,6 +871,12 @@ function parseArgs(args) {
784
871
  help: globalFlags.help || false,
785
872
  noBanner: globalFlags.noBanner || false,
786
873
  quiet: globalFlags.quiet || false,
874
+ // NEW: Gate mode options
875
+ useGate: true, // Use new gate system by default
876
+ cached: false, // Use cached verdict if valid
877
+ manifest: false, // Output full manifest
878
+ receipt: false, // Output CI receipt
879
+ whyTree: false, // Show detailed why tree
787
880
  };
788
881
 
789
882
  // Parse command-specific args
@@ -799,6 +892,12 @@ function parseArgs(args) {
799
892
  }
800
893
  else if (a.startsWith("--path=")) opts.path = a.split("=")[1];
801
894
  else if (a === "--path" || a === "-p") opts.path = args[++i];
895
+ // NEW: Gate options
896
+ else if (a === "--legacy") opts.useGate = false;
897
+ else if (a === "--cached") opts.cached = true;
898
+ else if (a === "--manifest") opts.manifest = true;
899
+ else if (a === "--receipt") opts.receipt = true;
900
+ else if (a === "--why") opts.whyTree = true;
802
901
  }
803
902
 
804
903
  return opts;
@@ -822,6 +921,24 @@ async function runShip(args, context = {}) {
822
921
  return 0;
823
922
  }
824
923
 
924
+ // ═══════════════════════════════════════════════════════════════════════════
925
+ // NEW: Use Ship Gate (v2) system for deterministic, evidence-backed verdicts
926
+ // ═══════════════════════════════════════════════════════════════════════════
927
+ if (opts.useGate && !opts.fix && !opts.assist) {
928
+ try {
929
+ const gateResult = await runShipGate(opts, context);
930
+ if (gateResult !== null) {
931
+ return gateResult;
932
+ }
933
+ // If null returned, fall through to legacy mode
934
+ } catch (err) {
935
+ if (opts.verbose) {
936
+ console.error(` Gate evaluation failed: ${err.message}, using legacy mode`);
937
+ }
938
+ // Fall through to legacy mode
939
+ }
940
+ }
941
+
825
942
  // Entitlement check
826
943
  try {
827
944
  await enforceLimit('scans');
@@ -1204,6 +1321,141 @@ async function runAutoFix(projectPath, results, outputDir, findings) {
1204
1321
  return fixResults;
1205
1322
  }
1206
1323
 
1324
+ // ═══════════════════════════════════════════════════════════════════════════════
1325
+ // SHIP GATE (v2) - Deterministic, Evidence-backed Verdict
1326
+ // ═══════════════════════════════════════════════════════════════════════════════
1327
+
1328
+ /**
1329
+ * Run ship using the new gate system.
1330
+ *
1331
+ * The gate system provides:
1332
+ * - Deterministic verdicts (same repo state → same verdict)
1333
+ * - Evidence-backed decisions (why tree)
1334
+ * - CI-ready artifacts (manifest, receipt, badge)
1335
+ *
1336
+ * @param {Object} opts - Options from parseArgs
1337
+ * @param {Object} context - Execution context
1338
+ * @returns {number} Exit code
1339
+ */
1340
+ async function runShipGate(opts, context = {}) {
1341
+ const shipGate = getShipGate();
1342
+ const shipManifest = getShipManifest();
1343
+ const whyTreeModule = getWhyTree();
1344
+
1345
+ if (!shipGate || !shipManifest) {
1346
+ // Fall back to legacy mode if gate modules not available
1347
+ console.error(" Ship gate modules not available, using legacy mode");
1348
+ return null; // Signal to use legacy
1349
+ }
1350
+
1351
+ const projectPath = path.resolve(opts.path);
1352
+ const startTime = Date.now();
1353
+
1354
+ // Check cache first if requested
1355
+ if (opts.cached) {
1356
+ const cacheResult = shipGate.checkVerdictCache(projectPath);
1357
+ if (cacheResult.valid) {
1358
+ const manifest = cacheResult.manifest;
1359
+
1360
+ if (opts.json || opts.manifest) {
1361
+ console.log(JSON.stringify(manifest, null, 2));
1362
+ } else if (opts.receipt) {
1363
+ console.log(shipManifest.generateCIReceipt(manifest));
1364
+ } else if (!opts.quiet) {
1365
+ console.log(` Using cached verdict: ${manifest.verdict.status} (${cacheResult.age}ms old)`);
1366
+ console.log(` Fingerprint: ${manifest.repo.fingerprint}`);
1367
+ }
1368
+
1369
+ return manifest.verdict.exitCode;
1370
+ }
1371
+
1372
+ if (!opts.quiet && !opts.json) {
1373
+ console.log(` Cache invalid: ${cacheResult.reason}`);
1374
+ }
1375
+ }
1376
+
1377
+ // Run full gate evaluation
1378
+ const gate = new shipGate.ShipGate(projectPath, {
1379
+ strict: opts.strict,
1380
+ });
1381
+
1382
+ const result = await gate.evaluate({
1383
+ skipReality: !opts.withRuntime,
1384
+ });
1385
+
1386
+ // Output based on mode
1387
+ if (opts.json || opts.manifest) {
1388
+ console.log(JSON.stringify(result.manifest, null, 2));
1389
+ return result.exitCode;
1390
+ }
1391
+
1392
+ if (opts.receipt) {
1393
+ console.log(shipManifest.generateCIReceipt(result.manifest));
1394
+ return result.exitCode;
1395
+ }
1396
+
1397
+ if (opts.ci) {
1398
+ // CI mode: minimal key=value output
1399
+ const envVars = shipManifest.generateCIEnvVars(result.manifest);
1400
+ for (const [key, value] of Object.entries(envVars)) {
1401
+ console.log(`${key}=${value}`);
1402
+ }
1403
+ return result.exitCode;
1404
+ }
1405
+
1406
+ // Human-readable output
1407
+ if (!opts.quiet) {
1408
+ // Print verdict card
1409
+ printVerdictCard(
1410
+ result.verdict,
1411
+ result.score,
1412
+ result.evidence.audit.blockers,
1413
+ result.evidence.audit.warnings,
1414
+ result.durationMs
1415
+ );
1416
+
1417
+ // Print why tree if requested or if blocked
1418
+ if (opts.whyTree || result.verdict !== "SHIP") {
1419
+ if (whyTreeModule) {
1420
+ const whyTree = whyTreeModule.buildWhyTree(result.findings);
1421
+ console.log(whyTreeModule.renderWhyTreeTerminal(whyTree));
1422
+ } else {
1423
+ // Fallback: simple why tree from manifest
1424
+ const why = result.manifest.evidence.whyTree;
1425
+ console.log(`\n ${ansi.bold}WHY: ${result.verdict}${ansi.reset}`);
1426
+ console.log(` ${ansi.dim}${why.summary}${ansi.reset}\n`);
1427
+
1428
+ for (const issue of why.topIssues || []) {
1429
+ console.log(` ${colors.blockRed}•${ansi.reset} ${issue.title}`);
1430
+ if (issue.evidence?.file) {
1431
+ console.log(` ${ansi.dim}→ ${issue.evidence.file}:${issue.evidence.lines || ""}${ansi.reset}`);
1432
+ }
1433
+ if (issue.fixHint) {
1434
+ console.log(` ${colors.shipGreen}Fix: ${issue.fixHint}${ansi.reset}`);
1435
+ }
1436
+ console.log();
1437
+ }
1438
+ }
1439
+ }
1440
+
1441
+ // Print artifact paths
1442
+ console.log(` ${ansi.dim}Artifacts:${ansi.reset}`);
1443
+ console.log(` ${ansi.dim}Manifest: ${result.artifactPaths.manifest}${ansi.reset}`);
1444
+ console.log(` ${ansi.dim}Receipt: ${result.artifactPaths.receipt}${ansi.reset}`);
1445
+ console.log();
1446
+
1447
+ // Badge info if requested
1448
+ if (opts.badge) {
1449
+ const badgeInfo = shipManifest.generateBadgeInfo(result.manifest);
1450
+ console.log(` ${ansi.bold}Badge:${ansi.reset}`);
1451
+ console.log(` ${badgeInfo.markdown}`);
1452
+ console.log();
1453
+ }
1454
+ }
1455
+
1456
+ return result.exitCode;
1457
+ }
1458
+
1207
1459
  // ═══════════════════════════════════════════════════════════════════════════════
1208
1460
  // SHIP CORE (for programmatic use)
1209
1461
  // ═══════════════════════════════════════════════════════════════════════════════
@@ -1275,7 +1527,134 @@ async function shipCore({ repoRoot, fastifyEntry, jsonOut, noWrite } = {}) {
1275
1527
  // EXPORTS
1276
1528
  // ═══════════════════════════════════════════════════════════════════════════════
1277
1529
 
1530
+ // ═══════════════════════════════════════════════════════════════════════════════
1531
+ // SEAL COMMAND - Badge and Attestation Generator
1532
+ // ═══════════════════════════════════════════════════════════════════════════════
1533
+
1534
+ /**
1535
+ * Generate seal (badge) and attestation from the latest ship manifest.
1536
+ *
1537
+ * @param {string[]} args - Command arguments
1538
+ * @param {Object} context - Execution context
1539
+ * @returns {number} Exit code
1540
+ */
1541
+ async function runSeal(args = [], context = {}) {
1542
+ const { parseGlobalFlags } = require("./lib/global-flags");
1543
+ const { EXIT } = require("./lib/exit-codes");
1544
+
1545
+ const { flags: globalFlags } = parseGlobalFlags(args);
1546
+ const quiet = globalFlags.quiet || args.includes("--quiet") || args.includes("-q");
1547
+ const json = globalFlags.json || args.includes("--json");
1548
+ const projectRoot = context.repoRoot || globalFlags.path || process.cwd();
1549
+
1550
+ const shipManifest = getShipManifest();
1551
+
1552
+ if (!shipManifest) {
1553
+ if (!quiet) {
1554
+ console.error(" Seal module not available");
1555
+ }
1556
+ return EXIT.INTERNAL_ERROR;
1557
+ }
1558
+
1559
+ // Load latest manifest
1560
+ const manifest = shipManifest.loadShipManifest(projectRoot);
1561
+
1562
+ if (!manifest) {
1563
+ if (json) {
1564
+ console.log(JSON.stringify({
1565
+ success: false,
1566
+ error: "No ship manifest found. Run 'vibecheck ship' first."
1567
+ }));
1568
+ } else if (!quiet) {
1569
+ console.error(" No ship manifest found. Run 'vibecheck ship' first.");
1570
+ }
1571
+ return EXIT.NOT_FOUND;
1572
+ }
1573
+
1574
+ // Verify signature
1575
+ const verification = shipManifest.verifyManifestSignature(manifest);
1576
+
1577
+ // Generate badge info
1578
+ const badge = shipManifest.generateBadgeInfo(manifest);
1579
+
1580
+ // Build seal output
1581
+ const seal = {
1582
+ verdict: manifest.verdict.status,
1583
+ score: manifest.verdict.score,
1584
+ canShip: manifest.verdict.canShip,
1585
+
1586
+ // Repo info
1587
+ repo: {
1588
+ name: manifest.repo.name,
1589
+ commit: manifest.repo.commit,
1590
+ branch: manifest.repo.branch,
1591
+ fingerprint: manifest.repo.fingerprint,
1592
+ },
1593
+
1594
+ // Badge URLs
1595
+ badge: {
1596
+ shields: badge.url.shields,
1597
+ badgen: badge.url.badgen,
1598
+ markdown: badge.markdown,
1599
+ },
1600
+
1601
+ // Attestation
1602
+ attestation: {
1603
+ signatureValid: verification.valid,
1604
+ digest: manifest.signature.digest,
1605
+ algorithm: manifest.signature.algorithm,
1606
+ generatedAt: manifest.meta.generatedAt,
1607
+ },
1608
+
1609
+ // Evidence summary
1610
+ evidence: {
1611
+ blockers: manifest.evidence.summary.blockers,
1612
+ warnings: manifest.evidence.summary.warnings,
1613
+ totalFindings: manifest.evidence.summary.total,
1614
+ },
1615
+ };
1616
+
1617
+ if (json) {
1618
+ console.log(JSON.stringify(seal, null, 2));
1619
+ } else if (!quiet) {
1620
+ console.log(`
1621
+ ${ansi.bold}${ansi.cyan}╔═══════════════════════════════════════════════════════════════════╗
1622
+ ║ ║
1623
+ ║ SHIP SEAL ║
1624
+ ║ ║
1625
+ ╚═══════════════════════════════════════════════════════════════════╝${ansi.reset}
1626
+
1627
+ ${ansi.bold}Verdict:${ansi.reset} ${seal.verdict === "SHIP" ? colors.shipGreen : seal.verdict === "WARN" ? colors.warnAmber : colors.blockRed}${seal.verdict}${ansi.reset}
1628
+ ${ansi.bold}Score:${ansi.reset} ${seal.score}/100
1629
+
1630
+ ${ansi.bold}Repository${ansi.reset}
1631
+ ${ansi.dim}Name:${ansi.reset} ${seal.repo.name}
1632
+ ${ansi.dim}Commit:${ansi.reset} ${seal.repo.commit}
1633
+ ${ansi.dim}Branch:${ansi.reset} ${seal.repo.branch}
1634
+ ${ansi.dim}Fingerprint:${ansi.reset} ${seal.repo.fingerprint}
1635
+
1636
+ ${ansi.bold}Attestation${ansi.reset}
1637
+ ${ansi.dim}Signature:${ansi.reset} ${verification.valid ? colors.shipGreen + "✓ Valid" : colors.blockRed + "✗ Invalid"}${ansi.reset}
1638
+ ${ansi.dim}Digest:${ansi.reset} ${seal.attestation.digest}
1639
+ ${ansi.dim}Generated:${ansi.reset} ${seal.attestation.generatedAt}
1640
+
1641
+ ${ansi.bold}Badge (copy to README.md):${ansi.reset}
1642
+ ${colors.shipGreen}${seal.badge.markdown}${ansi.reset}
1643
+
1644
+ ${ansi.dim}Evidence: ${seal.evidence.blockers} blockers, ${seal.evidence.warnings} warnings${ansi.reset}
1645
+ `);
1646
+ }
1647
+
1648
+ return manifest.verdict.exitCode;
1649
+ }
1650
+
1651
+ // ═══════════════════════════════════════════════════════════════════════════════
1652
+ // EXPORTS
1653
+ // ═══════════════════════════════════════════════════════════════════════════════
1654
+
1278
1655
  module.exports = {
1279
1656
  runShip: withErrorHandling(runShip, "Ship check failed"),
1280
- shipCore
1657
+ runSeal: withErrorHandling(runSeal, "Seal generation failed"),
1658
+ runShipGate,
1659
+ shipCore,
1281
1660
  };
package/bin/vibecheck.js CHANGED
@@ -946,7 +946,7 @@ ${c.dim}${sym.boxHorizontal.repeat(64)}${c.reset}
946
946
  ${c.green}${sym.star} PRICING TIERS${c.reset}
947
947
 
948
948
  ${c.green}FREE${c.reset} ${c.dim}$0${c.reset} scan, ship, report, context, doctor, polish - full visibility
949
- ${c.magenta}PRO${c.reset} ${c.dim}$69/mo${c.reset} + autofix, prove, reality, mcp, guard, ai-test, PR comments
949
+ ${c.magenta}PRO${c.reset} ${c.dim}$49/mo${c.reset} + autofix, prove, reality, mcp, guard, ai-test, PR comments
950
950
 
951
951
  ${c.green}${sym.rocket} QUICK START (2 minutes to first proof)${c.reset}
952
952
 
@@ -1247,14 +1247,42 @@ async function main() {
1247
1247
 
1248
1248
  // Load registry for command resolution
1249
1249
  const registry = getRegistry();
1250
- const { COMMANDS, ALIAS_MAP, ALL_COMMANDS } = registry;
1250
+ const { COMMANDS, ALIAS_MAP, ALL_COMMANDS, isDeprecated, getDeprecationTarget } = registry;
1251
1251
 
1252
1252
  let cmd = cleanArgs[0];
1253
+ const originalCmd = cmd; // Save original for deprecation warning
1254
+
1253
1255
  // Prefer exact match, then alias
1256
+ let prependSubcommand = null;
1254
1257
  if (!COMMANDS[cmd] && ALIAS_MAP[cmd]) {
1255
- cmd = ALIAS_MAP[cmd];
1258
+ const target = ALIAS_MAP[cmd];
1259
+ // For auth aliases (login, logout, whoami, me, signin, signout),
1260
+ // prepend the original command as a subcommand to runAuth
1261
+ const authAliases = ['login', 'logout', 'whoami', 'me', 'signin', 'signout'];
1262
+ if (target === 'auth' && authAliases.includes(cmd)) {
1263
+ prependSubcommand = cmd === 'signin' ? 'login' : cmd === 'signout' ? 'logout' : cmd;
1264
+ }
1265
+ cmd = target;
1266
+ }
1267
+ let cmdArgs = prependSubcommand ? [prependSubcommand, ...cleanArgs.slice(1)] : cleanArgs.slice(1);
1268
+
1269
+ // ═══════════════════════════════════════════════════════════════════════════════
1270
+ // DEPRECATION WARNING
1271
+ // Show warning if user used a deprecated command name
1272
+ // ═══════════════════════════════════════════════════════════════════════════════
1273
+ if (cmd && COMMANDS[cmd]) {
1274
+ const cmdDef = COMMANDS[cmd];
1275
+ // Check if original command is deprecated (either via alias or direct deprecated command)
1276
+ if (cmdDef.deprecated && cmdDef.deprecatedBy) {
1277
+ // Direct deprecated command
1278
+ const { warnDeprecationOnce } = require("./_deprecations");
1279
+ warnDeprecationOnce(originalCmd, cmdDef.deprecatedBy, getVersion());
1280
+ } else if (originalCmd !== cmd) {
1281
+ // Used an alias - check if it's in the deprecation map
1282
+ const { warnIfDeprecated } = require("./_deprecations");
1283
+ warnIfDeprecated(originalCmd, getVersion());
1284
+ }
1256
1285
  }
1257
- let cmdArgs = cleanArgs.slice(1);
1258
1286
 
1259
1287
  // Handle command-specific help (vibecheck <cmd> --help)
1260
1288
  if (globalFlags.help && cmd && COMMANDS[cmd]) {
@@ -1319,8 +1347,8 @@ async function main() {
1319
1347
  isDevProAllowed;
1320
1348
 
1321
1349
  // Auth check (unless skipAuth or offline mode)
1322
- // Only allow login, logout, whoami, and help without auth
1323
- const authExemptCommands = ['login', 'logout', 'whoami', 'help', '--help', '-h', 'version', '--version'];
1350
+ // Allow auth commands and help without prior auth
1351
+ const authExemptCommands = ['auth', 'login', 'logout', 'whoami', 'me', 'signin', 'signout', 'help', '--help', '-h', 'version', '--version'];
1324
1352
  const needsAuth = !authExemptCommands.includes(cmd) && !cmdDef.skipAuth && !isOffline;
1325
1353
 
1326
1354
  if (needsAuth) {