@vibecheckai/cli 3.1.2 → 3.1.4

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 (47) hide show
  1. package/README.md +60 -33
  2. package/bin/registry.js +319 -34
  3. package/bin/runners/CLI_REFACTOR_SUMMARY.md +229 -0
  4. package/bin/runners/REPORT_AUDIT.md +64 -0
  5. package/bin/runners/lib/entitlements-v2.js +97 -28
  6. package/bin/runners/lib/entitlements.js +3 -6
  7. package/bin/runners/lib/init-wizard.js +1 -1
  8. package/bin/runners/lib/report-engine.js +459 -280
  9. package/bin/runners/lib/report-html.js +1154 -1423
  10. package/bin/runners/lib/report-output.js +187 -0
  11. package/bin/runners/lib/report-templates.js +848 -850
  12. package/bin/runners/lib/scan-output.js +545 -0
  13. package/bin/runners/lib/server-usage.js +0 -12
  14. package/bin/runners/lib/ship-output.js +641 -0
  15. package/bin/runners/lib/status-output.js +253 -0
  16. package/bin/runners/lib/terminal-ui.js +853 -0
  17. package/bin/runners/runCheckpoint.js +502 -0
  18. package/bin/runners/runContracts.js +105 -0
  19. package/bin/runners/runExport.js +93 -0
  20. package/bin/runners/runFix.js +31 -24
  21. package/bin/runners/runInit.js +377 -112
  22. package/bin/runners/runInstall.js +1 -5
  23. package/bin/runners/runLabs.js +3 -3
  24. package/bin/runners/runPolish.js +2452 -0
  25. package/bin/runners/runProve.js +2 -2
  26. package/bin/runners/runReport.js +251 -200
  27. package/bin/runners/runRuntime.js +110 -0
  28. package/bin/runners/runScan.js +477 -379
  29. package/bin/runners/runSecurity.js +92 -0
  30. package/bin/runners/runShip.js +137 -207
  31. package/bin/runners/runStatus.js +16 -68
  32. package/bin/runners/utils.js +5 -5
  33. package/bin/vibecheck.js +25 -11
  34. package/mcp-server/index.js +150 -18
  35. package/mcp-server/package.json +2 -2
  36. package/mcp-server/premium-tools.js +13 -13
  37. package/mcp-server/tier-auth.js +292 -27
  38. package/mcp-server/vibecheck-tools.js +9 -9
  39. package/package.json +1 -1
  40. package/bin/runners/runClaimVerifier.js +0 -483
  41. package/bin/runners/runContextCompiler.js +0 -385
  42. package/bin/runners/runGate.js +0 -17
  43. package/bin/runners/runInitGha.js +0 -164
  44. package/bin/runners/runInteractive.js +0 -388
  45. package/bin/runners/runMdc.js +0 -204
  46. package/bin/runners/runMissionGenerator.js +0 -282
  47. package/bin/runners/runTruthpack.js +0 -636
@@ -13,17 +13,7 @@
13
13
 
14
14
  const fs = require("fs");
15
15
  const path = require("path");
16
-
17
- const c = {
18
- reset: "\x1b[0m",
19
- bold: "\x1b[1m",
20
- dim: "\x1b[2m",
21
- red: "\x1b[31m",
22
- green: "\x1b[32m",
23
- yellow: "\x1b[33m",
24
- cyan: "\x1b[36m",
25
- blue: "\x1b[34m",
26
- };
16
+ const { formatStatusOutput } = require("./lib/status-output");
27
17
 
28
18
  function readJsonSafe(p) {
29
19
  try {
@@ -34,29 +24,6 @@ function readJsonSafe(p) {
34
24
  }
35
25
  }
36
26
 
37
- function timeAgo(dateStr) {
38
- if (!dateStr) return "never";
39
- const d = new Date(dateStr);
40
- const now = Date.now();
41
- const diff = now - d.getTime();
42
-
43
- if (diff < 60000) return "just now";
44
- if (diff < 3600000) return `${Math.round(diff / 60000)}m ago`;
45
- if (diff < 86400000) return `${Math.round(diff / 3600000)}h ago`;
46
- return `${Math.round(diff / 86400000)}d ago`;
47
- }
48
-
49
- function verdictIcon(v) {
50
- if (v === "SHIP") return `${c.green}✓ SHIP${c.reset}`;
51
- if (v === "WARN") return `${c.yellow}⚠ WARN${c.reset}`;
52
- if (v === "BLOCK") return `${c.red}✗ BLOCK${c.reset}`;
53
- return `${c.dim}—${c.reset}`;
54
- }
55
-
56
- function checkExists(p) {
57
- return fs.existsSync(p) ? `${c.green}✓${c.reset}` : `${c.dim}—${c.reset}`;
58
- }
59
-
60
27
  async function runStatus({ repoRoot, json = false } = {}) {
61
28
  const root = repoRoot || process.cwd();
62
29
 
@@ -95,40 +62,21 @@ async function runStatus({ repoRoot, json = false } = {}) {
95
62
  return 0;
96
63
  }
97
64
 
98
- console.log(`
99
- ${c.cyan}${c.bold}╔════════════════════════════════════════════════════════════╗
100
- ║ 📊 vibecheck status ║
101
- ╚════════════════════════════════════════════════════════════╝${c.reset}
102
-
103
- ${c.bold}Verdict${c.reset}
104
- Current: ${verdictIcon(lastShip?.meta?.verdict)}
105
- Last ship: ${c.dim}${timeAgo(lastShip?.meta?.generatedAt)}${c.reset}
106
-
107
- ${c.bold}Findings${c.reset}
108
- BLOCK: ${blocks ? c.red + blocks + c.reset : c.green + "0" + c.reset}
109
- WARN: ${warns ? c.yellow + warns + c.reset : c.green + "0" + c.reset}
110
-
111
- ${c.bold}Truth Pack${c.reset}
112
- Routes: ${routes}
113
- Env vars: ${envVars}
114
- Gaps: ${gaps ? c.yellow + gaps + c.reset : c.green + "0" + c.reset}
115
-
116
- ${c.bold}Reality${c.reset}
117
- Last run: ${c.dim}${timeAgo(lastReality?.meta?.startedAt)}${c.reset}
118
- Coverage: ${coverage != null ? coverage + "%" : c.dim + "—" + c.reset}
119
-
120
- ${c.bold}Files${c.reset}
121
- .vibecheck/config.json: ${checkExists(path.join(root, ".vibecheck", "config.json"))}
122
- .vibecheck/truth/truthpack: ${checkExists(path.join(root, ".vibecheck", "truth", "truthpack.json"))}
123
- .vibecheck/last_ship.json: ${checkExists(path.join(root, ".vibecheck", "last_ship.json"))}
124
- .env.example: ${checkExists(path.join(root, ".env.example")) || checkExists(path.join(root, ".env.template"))}
125
-
126
- ${c.bold}Quick Actions${c.reset}
127
- ${lastShip?.meta?.verdict === "BLOCK" ? `${c.yellow}→ Run: vibecheck fix --apply${c.reset}` : ""}
128
- ${!truthpack ? `${c.yellow}→ Run: vibecheck install${c.reset}` : ""}
129
- ${!lastReality ? `${c.yellow}→ Run: vibecheck reality --url <your-app>${c.reset}` : ""}
130
- ${lastShip?.meta?.verdict === "SHIP" ? `${c.green}✓ Ready to deploy!${c.reset}` : ""}
131
- `);
65
+ const status = {
66
+ verdict: lastShip?.meta?.verdict || null,
67
+ lastShipAt: lastShip?.meta?.generatedAt || null,
68
+ blockers: blocks,
69
+ warnings: warns,
70
+ routes,
71
+ envVars,
72
+ gaps,
73
+ lastRealityAt: lastReality?.meta?.startedAt || null,
74
+ coverage: coverage || null,
75
+ hasTruthpack: !!truthpack,
76
+ hasReality: !!lastReality,
77
+ };
78
+
79
+ console.log(formatStatusOutput(status, { root }));
132
80
 
133
81
  // Exit 0 when showing status successfully (even if no ship yet)
134
82
  // Only return non-zero if explicitly checking for BLOCK
@@ -114,10 +114,11 @@ function detectProjectFeatures(projectPath) {
114
114
  return features;
115
115
  }
116
116
 
117
- function writeArtifacts(outputDir, results) {
117
+ function writeArtifacts(outputDir, results, options = {}) {
118
118
  ensureOutputDir(outputDir);
119
119
 
120
120
  const timestamp = new Date().toISOString();
121
+ const skipHtmlReport = options.skipHtmlReport || false;
121
122
 
122
123
  // Always write summary.json (machine-readable)
123
124
  const summary = {
@@ -138,9 +139,8 @@ function writeArtifacts(outputDir, results) {
138
139
  const md = generateMarkdownSummary(results);
139
140
  fs.writeFileSync(path.join(outputDir, "summary.md"), md);
140
141
 
141
- // Write report.html (shareable)
142
- const html = generateHtmlReport(results);
143
- fs.writeFileSync(path.join(outputDir, "report.html"), html);
142
+ // Note: HTML reports are generated by 'vibecheck report' command, not here
143
+ // This function only writes JSON and Markdown artifacts
144
144
 
145
145
  // Write per-module artifacts
146
146
  const artifactsDir = path.join(outputDir, "artifacts");
@@ -341,7 +341,7 @@ function printResults(results, format) {
341
341
  ` ${c.dim}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${c.reset}`,
342
342
  );
343
343
  console.log(
344
- ` ${c.dim}📄 Full report:${c.reset} ${c.cyan}${results.outputDir}/report.html${c.reset}`,
344
+ ` ${c.dim}📄 Generate report:${c.reset} ${c.cyan}vibecheck report${c.reset}`,
345
345
  );
346
346
  console.log(
347
347
  ` ${c.dim}🔧 Fix issues:${c.reset} ${c.cyan}vibecheck fix${c.reset}`,
package/bin/vibecheck.js CHANGED
@@ -325,15 +325,18 @@ function printHelp() {
325
325
  printBanner();
326
326
 
327
327
  // Categories ordered as specified
328
- const categoryOrder = ["proof", "setup", "truth", "ci", "automation", "account", "extras"];
328
+ const categoryOrder = ["setup", "analysis", "proof", "quality", "truth", "output", "ci", "automation", "account", "extras"];
329
329
  const categories = {
330
+ setup: { name: "SETUP", color: c.yellow, icon: sym.gear },
331
+ analysis: { name: "ANALYSIS", color: c.cyan, icon: sym.chart },
330
332
  proof: { name: "PROOF LOOP", color: c.green, icon: sym.shield },
331
- setup: { name: "SETUP & DX", color: c.yellow, icon: sym.gear },
333
+ quality: { name: "QUALITY", color: c.brightGreen, icon: sym.star },
332
334
  truth: { name: "AI TRUTH", color: c.magenta, icon: sym.lightning },
335
+ output: { name: "OUTPUT", color: c.blue, icon: sym.file },
333
336
  ci: { name: "CI & COLLABORATION", color: c.cyan, icon: sym.rocket },
334
- automation: { name: "AUTOMATION", color: c.blue, icon: sym.fire },
337
+ automation: { name: "AUTOMATION", color: c.brightBlue, icon: sym.fire },
335
338
  account: { name: "ACCOUNT", color: c.dim, icon: sym.key },
336
- extras: { name: "EXTRAS", color: c.dim, icon: sym.star },
339
+ extras: { name: "EXTRAS", color: c.dim, icon: sym.starEmpty },
337
340
  };
338
341
 
339
342
  // Group commands
@@ -375,15 +378,17 @@ ${c.dim}${sym.boxHorizontal.repeat(64)}${c.reset}
375
378
 
376
379
  ${c.green}TIERS${c.reset}
377
380
 
378
- ${c.green}FREE${c.reset} $0 scan, ship, ctx, doctor, report (HTML/MD)
379
- ${c.cyan}STARTER${c.reset} $29/mo + gate, launch, pr, badge, mcp, reality full
380
- ${c.magenta}PRO${c.reset} $99/mo + prove, fix apply, share, ai-test, compliance
381
+ ${c.green}FREE${c.reset} $0 init --local, scan, ship (static), report (HTML/MD), doctor, polish
382
+ ${c.cyan}STARTER${c.reset} $39/mo + init --connect, scan --autofix, report (SARIF/CSV), mcp, PR comments
383
+ ${c.magenta}PRO${c.reset} $99/mo + prove, fix --apply, checkpoint (hallucination), reality (advanced)
381
384
 
382
- ${c.green}QUICK START${c.reset}
385
+ ${c.green}QUICK START - The 5-Step Journey${c.reset}
383
386
 
384
- ${c.bold}"Check my repo"${c.reset} ${c.cyan}vibecheck scan${c.reset}
385
- ${c.bold}"Can I ship?"${c.reset} ${c.cyan}vibecheck ship${c.reset}
386
- ${c.bold}"Full proof loop"${c.reset} ${c.cyan}vibecheck prove --url http://localhost:3000${c.reset} ${c.magenta}[PRO]${c.reset}
387
+ 1. ${c.bold}Setup${c.reset} ${c.cyan}vibecheck init --local${c.reset}
388
+ 2. ${c.bold}Scan${c.reset} ${c.cyan}vibecheck scan${c.reset}
389
+ 3. ${c.bold}Fix${c.reset} ${c.cyan}vibecheck scan --autofix${c.reset} ${c.cyan}[STARTER]${c.reset}
390
+ 4. ${c.bold}Prove${c.reset} ${c.cyan}vibecheck prove${c.reset} ${c.magenta}[PRO]${c.reset}
391
+ 5. ${c.bold}Ship${c.reset} ${c.cyan}vibecheck ship${c.reset}
387
392
 
388
393
  ${c.dim}Run 'vibecheck <command> --help' for command-specific help.${c.reset}
389
394
  ${c.dim}Pricing: https://vibecheckai.dev/pricing${c.reset}
@@ -551,6 +556,15 @@ async function main() {
551
556
  else if (cmdArgs[0] === "search") { const { runContext } = require("./runners/runContext"); exitCode = await runContext(["--search", ...cmdArgs.slice(1)]); }
552
557
  else { exitCode = await runner(cmdArgs, context); }
553
558
  break;
559
+ case "runtime":
560
+ exitCode = await runner(cmdArgs, context);
561
+ break;
562
+ case "export":
563
+ exitCode = await runner(cmdArgs, context);
564
+ break;
565
+ case "security":
566
+ exitCode = await runner(cmdArgs, context);
567
+ break;
554
568
  case "install": exitCode = await runner(cmdArgs, context); break;
555
569
  case "status": exitCode = await runner({ ...context, json: cmdArgs.includes("--json") }); break;
556
570
  case "pr": exitCode = await runner(cmdArgs, context); break;
@@ -89,6 +89,9 @@ import { TRUTH_FIREWALL_TOOLS, handleTruthFirewallTool } from "./truth-firewall-
89
89
  // Import Consolidated Tools (15 focused tools - recommended surface)
90
90
  import { CONSOLIDATED_TOOLS, handleConsolidatedTool } from "./consolidated-tools.js";
91
91
 
92
+ // Import tier auth for entitlement checking
93
+ import { checkFeatureAccess } from "./tier-auth.js";
94
+
92
95
  // ============================================================================
93
96
  // TOOL DEFINITIONS - Public Tools (Clean Product Surface)
94
97
  // ============================================================================
@@ -300,7 +303,7 @@ const TOOLS = USE_CONSOLIDATED_TOOLS ? [
300
303
  // 3. GATE - Enforce truth in CI
301
304
  {
302
305
  name: "vibecheck.gate",
303
- description: "🚦 Enforce truth in CI — fail builds on policy violations",
306
+ description: "🚦 Enforce truth in CI — fail builds on policy violations (STARTER tier)",
304
307
  inputSchema: {
305
308
  type: "object",
306
309
  properties: {
@@ -327,7 +330,7 @@ const TOOLS = USE_CONSOLIDATED_TOOLS ? [
327
330
  {
328
331
  name: "vibecheck.fix",
329
332
  description:
330
- "🔧 Fix Missions v1 — AI-powered surgical fixes with proof verification loop",
333
+ "🔧 Fix Missions v1 — AI-powered surgical fixes with proof verification loop. --apply and --autopilot require PRO tier ($99/mo).",
331
334
  inputSchema: {
332
335
  type: "object",
333
336
  properties: {
@@ -396,7 +399,7 @@ const TOOLS = USE_CONSOLIDATED_TOOLS ? [
396
399
  // 7. PROVE - One Command Reality Proof (orchestrates ctx → reality → ship → fix)
397
400
  {
398
401
  name: "vibecheck.prove",
399
- description: "🔬 One Command Reality Proof — orchestrates ctx → reality → ship → fix loop until SHIP or stuck",
402
+ description: "🔬 One Command Reality Proof — orchestrates ctx → reality → ship → fix loop until SHIP or stuck (PRO tier)",
400
403
  inputSchema: {
401
404
  type: "object",
402
405
  properties: {
@@ -593,11 +596,11 @@ const TOOLS = USE_CONSOLIDATED_TOOLS ? [
593
596
  },
594
597
  },
595
598
 
596
- // 13. AUTOPILOT PLAN - Generate fix plan (Pro/Compliance)
599
+ // 13. AUTOPILOT PLAN - Generate fix plan (PRO tier)
597
600
  {
598
601
  name: "vibecheck.autopilot_plan",
599
602
  description:
600
- "🤖 Autopilot Plan — scan codebase, group issues into fix packs, estimate risk (Pro/Compliance)",
603
+ "🤖 Autopilot Plan — scan codebase, group issues into fix packs, estimate risk (PRO tier)",
601
604
  inputSchema: {
602
605
  type: "object",
603
606
  properties: {
@@ -620,11 +623,11 @@ const TOOLS = USE_CONSOLIDATED_TOOLS ? [
620
623
  },
621
624
  },
622
625
 
623
- // 14. AUTOPILOT APPLY - Apply fixes (Pro/Compliance)
626
+ // 14. AUTOPILOT APPLY - Apply fixes (PRO tier)
624
627
  {
625
628
  name: "vibecheck.autopilot_apply",
626
629
  description:
627
- "🔧 Autopilot Apply — apply fix packs with verification, re-scan to confirm (Pro/Compliance)",
630
+ "🔧 Autopilot Apply — apply fix packs with verification, re-scan to confirm (PRO tier)",
628
631
  inputSchema: {
629
632
  type: "object",
630
633
  properties: {
@@ -661,7 +664,7 @@ const TOOLS = USE_CONSOLIDATED_TOOLS ? [
661
664
  {
662
665
  name: "vibecheck.badge",
663
666
  description:
664
- "🏅 Ship Badge — generate a badge for README/PR showing scan status",
667
+ "🏅 Ship Badge — generate a badge for README/PR showing scan status (STARTER tier)",
665
668
  inputSchema: {
666
669
  type: "object",
667
670
  properties: {
@@ -1046,8 +1049,27 @@ class VibecheckMCP {
1046
1049
  return { content: [{ type: "text", text }] };
1047
1050
  }
1048
1051
 
1049
- error(text) {
1050
- return { content: [{ type: "text", text: `❌ ${text}` }], isError: true };
1052
+ error(text, options = {}) {
1053
+ const { code, suggestion, nextSteps = [] } = options;
1054
+
1055
+ let errorText = `❌ ${text}`;
1056
+
1057
+ if (code) {
1058
+ errorText += `\n\n**Error Code:** \`${code}\``;
1059
+ }
1060
+
1061
+ if (suggestion) {
1062
+ errorText += `\n\n💡 **Suggestion:** ${suggestion}`;
1063
+ }
1064
+
1065
+ if (nextSteps.length > 0) {
1066
+ errorText += `\n\n**Next Steps:**\n`;
1067
+ nextSteps.forEach((step, i) => {
1068
+ errorText += `${i + 1}. ${step}\n`;
1069
+ });
1070
+ }
1071
+
1072
+ return { content: [{ type: "text", text: errorText }], isError: true };
1051
1073
  }
1052
1074
 
1053
1075
  // Validate project path exists and is accessible
@@ -1055,11 +1077,29 @@ class VibecheckMCP {
1055
1077
  try {
1056
1078
  const stats = require("fs").statSync(projectPath);
1057
1079
  if (!stats.isDirectory()) {
1058
- return { valid: false, error: `Path is not a directory: ${projectPath}` };
1080
+ return {
1081
+ valid: false,
1082
+ error: `Path is not a directory: ${projectPath}`,
1083
+ suggestion: "Provide a directory path, not a file",
1084
+ nextSteps: [
1085
+ "Check the path you provided",
1086
+ "Ensure it points to a project directory",
1087
+ ],
1088
+ };
1059
1089
  }
1060
1090
  return { valid: true };
1061
1091
  } catch (e) {
1062
- return { valid: false, error: `Cannot access path: ${projectPath} (${e.code || e.message})` };
1092
+ return {
1093
+ valid: false,
1094
+ error: `Cannot access path: ${projectPath}`,
1095
+ code: e.code || "PATH_ACCESS_ERROR",
1096
+ suggestion: "Check that the path exists and you have read permissions",
1097
+ nextSteps: [
1098
+ "Verify the path is correct",
1099
+ "Check file permissions",
1100
+ "Ensure the directory exists",
1101
+ ],
1102
+ };
1063
1103
  }
1064
1104
  }
1065
1105
 
@@ -1067,6 +1107,16 @@ class VibecheckMCP {
1067
1107
  // SCAN
1068
1108
  // ============================================================================
1069
1109
  async handleScan(projectPath, args) {
1110
+ // Validate project path first
1111
+ const validation = this.validateProjectPath(projectPath);
1112
+ if (!validation.valid) {
1113
+ return this.error(validation.error, {
1114
+ code: validation.code || "INVALID_PATH",
1115
+ suggestion: validation.suggestion,
1116
+ nextSteps: validation.nextSteps || [],
1117
+ });
1118
+ }
1119
+
1070
1120
  const profile = args?.profile || "quick";
1071
1121
  const format = args?.format || "text";
1072
1122
  const only = args?.only;
@@ -1105,18 +1155,38 @@ class VibecheckMCP {
1105
1155
  }
1106
1156
 
1107
1157
  output += `\n📄 **Report:** .vibecheck/report.html\n`;
1158
+ output += "\n---\n_Context Enhanced by vibecheck AI_\n";
1159
+ return this.success(output);
1108
1160
  } catch (err) {
1109
- output += `\n⚠️ Scan error: ${err.message}\n`;
1161
+ return this.error(`Scan failed: ${err.message}`, {
1162
+ code: "SCAN_ERROR",
1163
+ suggestion: "Check that the project path is valid and contains scanable code",
1164
+ nextSteps: [
1165
+ "Verify the project path is correct",
1166
+ "Ensure you have read permissions",
1167
+ "Check that required dependencies are installed",
1168
+ "Try running: vibecheck scan --help",
1169
+ ],
1170
+ });
1110
1171
  }
1111
-
1112
- output += "\n---\n_Context Enhanced by vibecheck AI_\n";
1113
- return this.success(output);
1114
1172
  }
1115
1173
 
1116
1174
  // ============================================================================
1117
1175
  // GATE
1118
1176
  // ============================================================================
1119
1177
  async handleGate(projectPath, args) {
1178
+ // Check tier access (STARTER tier required)
1179
+ const access = await checkFeatureAccess("gate", args?.apiKey);
1180
+ if (!access.hasAccess) {
1181
+ return {
1182
+ content: [{
1183
+ type: "text",
1184
+ text: `🚫 UPGRADE REQUIRED\n\n${access.reason}\n\nCurrent tier: ${access.tier}\nUpgrade at: ${access.upgradeUrl}`
1185
+ }],
1186
+ isError: true
1187
+ };
1188
+ }
1189
+
1120
1190
  const policy = args?.policy || "strict";
1121
1191
 
1122
1192
  let output = "# 🚦 vibecheck Gate\n\n";
@@ -1148,6 +1218,20 @@ class VibecheckMCP {
1148
1218
  // FIX MISSIONS v1
1149
1219
  // ============================================================================
1150
1220
  async handleFix(projectPath, args) {
1221
+ // Check tier access for --apply and --autopilot (PRO tier required)
1222
+ if (args?.apply || args?.autopilot) {
1223
+ const access = await checkFeatureAccess("fix.apply_patches", args?.apiKey);
1224
+ if (!access.hasAccess) {
1225
+ return {
1226
+ content: [{
1227
+ type: "text",
1228
+ text: `🚫 UPGRADE REQUIRED\n\n${access.reason}\n\nCurrent tier: ${access.tier}\nUpgrade at: ${access.upgradeUrl}\n\nNote: --prompt-only mode is available for FREE tier.`
1229
+ }],
1230
+ isError: true
1231
+ };
1232
+ }
1233
+ }
1234
+
1151
1235
  const mode = args?.autopilot ? "Autopilot" :
1152
1236
  args?.apply ? "Apply" :
1153
1237
  args?.promptOnly ? "Prompt Only" : "Plan";
@@ -1319,6 +1403,18 @@ class VibecheckMCP {
1319
1403
  // PROVE - One Command Reality Proof
1320
1404
  // ============================================================================
1321
1405
  async handleProve(projectPath, args) {
1406
+ // Check tier access (PRO tier required)
1407
+ const access = await checkFeatureAccess("prove", args?.apiKey);
1408
+ if (!access.hasAccess) {
1409
+ return {
1410
+ content: [{
1411
+ type: "text",
1412
+ text: `🚫 UPGRADE REQUIRED\n\n${access.reason}\n\nCurrent tier: ${access.tier}\nUpgrade at: ${access.upgradeUrl}`
1413
+ }],
1414
+ isError: true
1415
+ };
1416
+ }
1417
+
1322
1418
  let output = "# 🔬 vibecheck prove\n\n";
1323
1419
  output += `**URL:** ${args?.url || "(static only)"}\n`;
1324
1420
  output += `**Max Fix Rounds:** ${args?.maxFixRounds || 3}\n\n`;
@@ -1787,9 +1883,21 @@ class VibecheckMCP {
1787
1883
  }
1788
1884
 
1789
1885
  // ============================================================================
1790
- // AUTOPILOT PLAN - Generate fix plan (Pro/Compliance)
1886
+ // AUTOPILOT PLAN - Generate fix plan (PRO tier)
1791
1887
  // ============================================================================
1792
1888
  async handleAutopilotPlan(projectPath, args) {
1889
+ // Check tier access (PRO tier required)
1890
+ const access = await checkFeatureAccess("fix.apply_patches", args?.apiKey);
1891
+ if (!access.hasAccess) {
1892
+ return {
1893
+ content: [{
1894
+ type: "text",
1895
+ text: `🚫 UPGRADE REQUIRED\n\n${access.reason}\n\nCurrent tier: ${access.tier}\nUpgrade at: ${access.upgradeUrl}`
1896
+ }],
1897
+ isError: true
1898
+ };
1899
+ }
1900
+
1793
1901
  let output = "# 🤖 vibecheck Autopilot Plan\n\n";
1794
1902
  output += `**Path:** ${projectPath}\n`;
1795
1903
  output += `**Profile:** ${args?.profile || "ship"}\n\n`;
@@ -1865,9 +1973,21 @@ class VibecheckMCP {
1865
1973
  }
1866
1974
 
1867
1975
  // ============================================================================
1868
- // AUTOPILOT APPLY - Apply fixes (Pro/Compliance)
1976
+ // AUTOPILOT APPLY - Apply fixes (PRO tier)
1869
1977
  // ============================================================================
1870
1978
  async handleAutopilotApply(projectPath, args) {
1979
+ // Check tier access (PRO tier required)
1980
+ const access = await checkFeatureAccess("fix.apply_patches", args?.apiKey);
1981
+ if (!access.hasAccess) {
1982
+ return {
1983
+ content: [{
1984
+ type: "text",
1985
+ text: `🚫 UPGRADE REQUIRED\n\n${access.reason}\n\nCurrent tier: ${access.tier}\nUpgrade at: ${access.upgradeUrl}`
1986
+ }],
1987
+ isError: true
1988
+ };
1989
+ }
1990
+
1871
1991
  let output = "# 🔧 vibecheck Autopilot Apply\n\n";
1872
1992
  output += `**Path:** ${projectPath}\n`;
1873
1993
  output += `**Profile:** ${args?.profile || "ship"}\n`;
@@ -1919,6 +2039,18 @@ class VibecheckMCP {
1919
2039
  // BADGE - Generate ship badge
1920
2040
  // ============================================================================
1921
2041
  async handleBadge(projectPath, args) {
2042
+ // Check tier access (STARTER tier required)
2043
+ const access = await checkFeatureAccess("badge", args?.apiKey);
2044
+ if (!access.hasAccess) {
2045
+ return {
2046
+ content: [{
2047
+ type: "text",
2048
+ text: `🚫 UPGRADE REQUIRED\n\n${access.reason}\n\nCurrent tier: ${access.tier}\nUpgrade at: ${access.upgradeUrl}`
2049
+ }],
2050
+ isError: true
2051
+ };
2052
+ }
2053
+
1922
2054
  const format = args?.format || "svg";
1923
2055
 
1924
2056
  let output = "# 🏅 vibecheck Badge\n\n";
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vibecheck-mcp-server",
3
- "version": "3.0.10",
3
+ "version": "3.1.4",
4
4
  "description": "Professional MCP server for vibecheck - Intelligent development environment vibechecks",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -21,7 +21,7 @@
21
21
  "architecture"
22
22
  ],
23
23
  "dependencies": {
24
- "@modelcontextprotocol/sdk": "^0.5.0"
24
+ "@modelcontextprotocol/sdk": "^1.0.0"
25
25
  },
26
26
  "engines": {
27
27
  "node": ">=18.0.0"
@@ -417,19 +417,19 @@ export async function handlePremiumTool(name, args, logger) {
417
417
 
418
418
  // Map premium tools to required features (all require starter+)
419
419
  const featureMap = {
420
- 'run_ship': 'smells', // ship check requires starter+
421
- 'run_reality': 'smells', // reality mode requires starter+
422
- 'run_mockproof': 'smells', // mockproof requires starter+
423
- 'run_airlock': 'breaking', // supply chain analysis requires pro+
424
- 'get_last_run': 'verify', // basic access
425
- 'open_artifact': 'verify', // basic access
426
- 'rerun_last_check': 'verify', // basic access
427
- 'run_doctor': 'verify', // basic access
428
- 'edit_policies': 'breaking', // policy editing requires pro+
429
- 'explain_finding': 'quality', // explanations require starter+
430
- 'policy_patch': 'smells', // patching requires starter+
431
- 'enter_fix_mode': 'smells', // fix mode requires starter+
432
- 'get_status': 'verify' // status check is free
420
+ 'run_ship': 'ship', // ship check is FREE (static-only)
421
+ 'run_reality': 'reality.preview', // reality preview is FREE, full requires STARTER+
422
+ 'run_mockproof': 'reality.full', // mockproof requires STARTER+
423
+ 'run_airlock': 'prove', // supply chain analysis requires PRO
424
+ 'get_last_run': 'scan', // basic access - FREE
425
+ 'open_artifact': 'scan', // basic access - FREE
426
+ 'rerun_last_check': 'scan', // basic access - FREE
427
+ 'run_doctor': 'doctor', // doctor is FREE
428
+ 'edit_policies': 'gate', // policy editing requires STARTER
429
+ 'explain_finding': 'scan', // explanations are FREE
430
+ 'policy_patch': 'gate', // patching requires STARTER
431
+ 'enter_fix_mode': 'fix.plan_only', // fix mode planning is FREE, apply requires PRO
432
+ 'get_status': 'status' // status check is FREE
433
433
  };
434
434
 
435
435
  const requiredFeature = featureMap[name];