@vibecheckai/cli 3.0.3 → 3.0.5

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 (119) hide show
  1. package/bin/cli-hygiene.js +241 -0
  2. package/bin/dev/run-v2-torture.js +30 -0
  3. package/bin/guardrail.js +843 -0
  4. package/bin/runners/cli-utils.js +1070 -0
  5. package/bin/runners/context/ai-task-decomposer.js +337 -0
  6. package/bin/runners/context/analyzer.js +462 -0
  7. package/bin/runners/context/api-contracts.js +427 -0
  8. package/bin/runners/context/context-diff.js +342 -0
  9. package/bin/runners/context/context-pruner.js +291 -0
  10. package/bin/runners/context/dependency-graph.js +414 -0
  11. package/bin/runners/context/generators/claude.js +107 -0
  12. package/bin/runners/context/generators/codex.js +108 -0
  13. package/bin/runners/context/generators/copilot.js +119 -0
  14. package/bin/runners/context/generators/cursor.js +514 -0
  15. package/bin/runners/context/generators/mcp.js +151 -0
  16. package/bin/runners/context/generators/windsurf.js +180 -0
  17. package/bin/runners/context/git-context.js +302 -0
  18. package/bin/runners/context/index.js +1042 -0
  19. package/bin/runners/context/insights.js +173 -0
  20. package/bin/runners/context/mcp-server/generate-rules.js +337 -0
  21. package/bin/runners/context/mcp-server/index.js +1176 -0
  22. package/bin/runners/context/mcp-server/package.json +24 -0
  23. package/bin/runners/context/memory.js +200 -0
  24. package/bin/runners/context/monorepo.js +215 -0
  25. package/bin/runners/context/multi-repo-federation.js +404 -0
  26. package/bin/runners/context/patterns.js +253 -0
  27. package/bin/runners/context/proof-context.js +972 -0
  28. package/bin/runners/context/security-scanner.js +303 -0
  29. package/bin/runners/context/semantic-search.js +350 -0
  30. package/bin/runners/context/shared.js +264 -0
  31. package/bin/runners/context/team-conventions.js +310 -0
  32. package/bin/runners/lib/ai-bridge.js +416 -0
  33. package/bin/runners/lib/analysis-core.js +271 -0
  34. package/bin/runners/lib/analyzers.js +579 -0
  35. package/bin/runners/lib/assets/vibecheck-logo.png +0 -0
  36. package/bin/runners/lib/audit-bridge.js +391 -0
  37. package/bin/runners/lib/auth-truth.js +193 -0
  38. package/bin/runners/lib/auth.js +215 -0
  39. package/bin/runners/lib/backup.js +62 -0
  40. package/bin/runners/lib/billing.js +107 -0
  41. package/bin/runners/lib/claims.js +118 -0
  42. package/bin/runners/lib/cli-ui.js +540 -0
  43. package/bin/runners/lib/compliance-bridge-new.js +0 -0
  44. package/bin/runners/lib/compliance-bridge.js +165 -0
  45. package/bin/runners/lib/contracts/auth-contract.js +202 -0
  46. package/bin/runners/lib/contracts/env-contract.js +181 -0
  47. package/bin/runners/lib/contracts/external-contract.js +206 -0
  48. package/bin/runners/lib/contracts/guard.js +168 -0
  49. package/bin/runners/lib/contracts/index.js +89 -0
  50. package/bin/runners/lib/contracts/plan-validator.js +311 -0
  51. package/bin/runners/lib/contracts/route-contract.js +199 -0
  52. package/bin/runners/lib/contracts.js +804 -0
  53. package/bin/runners/lib/detect.js +89 -0
  54. package/bin/runners/lib/detectors-v2.js +703 -0
  55. package/bin/runners/lib/doctor/autofix.js +254 -0
  56. package/bin/runners/lib/doctor/index.js +37 -0
  57. package/bin/runners/lib/doctor/modules/dependencies.js +325 -0
  58. package/bin/runners/lib/doctor/modules/index.js +46 -0
  59. package/bin/runners/lib/doctor/modules/network.js +250 -0
  60. package/bin/runners/lib/doctor/modules/project.js +312 -0
  61. package/bin/runners/lib/doctor/modules/runtime.js +224 -0
  62. package/bin/runners/lib/doctor/modules/security.js +348 -0
  63. package/bin/runners/lib/doctor/modules/system.js +213 -0
  64. package/bin/runners/lib/doctor/modules/vibecheck.js +394 -0
  65. package/bin/runners/lib/doctor/reporter.js +262 -0
  66. package/bin/runners/lib/doctor/service.js +262 -0
  67. package/bin/runners/lib/doctor/types.js +113 -0
  68. package/bin/runners/lib/doctor/ui.js +263 -0
  69. package/bin/runners/lib/doctor-enhanced.js +233 -0
  70. package/bin/runners/lib/doctor-v2.js +608 -0
  71. package/bin/runners/lib/drift.js +425 -0
  72. package/bin/runners/lib/enforcement.js +72 -0
  73. package/bin/runners/lib/entitlements.js +8 -3
  74. package/bin/runners/lib/env-resolver.js +417 -0
  75. package/bin/runners/lib/extractors/client-calls.js +990 -0
  76. package/bin/runners/lib/extractors/fastify-route-dump.js +573 -0
  77. package/bin/runners/lib/extractors/fastify-routes.js +426 -0
  78. package/bin/runners/lib/extractors/index.js +363 -0
  79. package/bin/runners/lib/extractors/next-routes.js +524 -0
  80. package/bin/runners/lib/extractors/proof-graph.js +431 -0
  81. package/bin/runners/lib/extractors/route-matcher.js +451 -0
  82. package/bin/runners/lib/extractors/truthpack-v2.js +377 -0
  83. package/bin/runners/lib/extractors/ui-bindings.js +547 -0
  84. package/bin/runners/lib/findings-schema.js +281 -0
  85. package/bin/runners/lib/html-report.js +650 -0
  86. package/bin/runners/lib/missions/templates.js +45 -0
  87. package/bin/runners/lib/policy.js +295 -0
  88. package/bin/runners/lib/reality/correlation-detectors.js +359 -0
  89. package/bin/runners/lib/reality/index.js +318 -0
  90. package/bin/runners/lib/reality/request-hashing.js +416 -0
  91. package/bin/runners/lib/reality/request-mapper.js +453 -0
  92. package/bin/runners/lib/reality/safety-rails.js +463 -0
  93. package/bin/runners/lib/reality/semantic-snapshot.js +408 -0
  94. package/bin/runners/lib/reality/toast-detector.js +393 -0
  95. package/bin/runners/lib/route-truth.js +10 -10
  96. package/bin/runners/lib/schema-validator.js +350 -0
  97. package/bin/runners/lib/schemas/contracts.schema.json +160 -0
  98. package/bin/runners/lib/schemas/finding.schema.json +100 -0
  99. package/bin/runners/lib/schemas/mission-pack.schema.json +206 -0
  100. package/bin/runners/lib/schemas/proof-graph.schema.json +176 -0
  101. package/bin/runners/lib/schemas/reality-report.schema.json +162 -0
  102. package/bin/runners/lib/schemas/share-pack.schema.json +180 -0
  103. package/bin/runners/lib/schemas/ship-report.schema.json +117 -0
  104. package/bin/runners/lib/schemas/truthpack-v2.schema.json +303 -0
  105. package/bin/runners/lib/schemas/validator.js +438 -0
  106. package/bin/runners/lib/verdict-engine.js +628 -0
  107. package/bin/runners/runAIAgent.js +228 -1
  108. package/bin/runners/runBadge.js +181 -1
  109. package/bin/runners/runCtxDiff.js +301 -0
  110. package/bin/runners/runInitGha.js +78 -15
  111. package/bin/runners/runLaunch.js +180 -1
  112. package/bin/runners/runProve.js +23 -0
  113. package/bin/runners/runReplay.js +114 -84
  114. package/bin/runners/runScan.js +111 -32
  115. package/bin/runners/runShip.js +23 -2
  116. package/bin/runners/runTruthpack.js +9 -7
  117. package/bin/runners/runValidate.js +161 -1
  118. package/bin/vibecheck.js +6 -1
  119. package/package.json +1 -1
@@ -0,0 +1,843 @@
1
+ #!/usr/bin/env node
2
+ // bin/vibecheck.js
3
+ const readline = require("readline");
4
+ const path = require("path");
5
+ const fs = require("fs");
6
+ const { routeArgv } = require("./_router");
7
+ const { warnDeprecationOnce } = require("./_deprecations");
8
+ const {
9
+ getApiKey,
10
+ checkEntitlement,
11
+ getEntitlements,
12
+ } = require("./runners/lib/auth");
13
+
14
+ // Read version from package.json
15
+ function getVersion() {
16
+ try {
17
+ const pkgPath = path.join(__dirname, "..", "package.json");
18
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
19
+ return pkg.version || "0.0.0";
20
+ } catch {
21
+ return "0.0.0";
22
+ }
23
+ }
24
+
25
+ // Runners
26
+ const { runScan } = require("./runners/runScan");
27
+ const { runGate } = require("./runners/runGate");
28
+ const { runContext } = require("./runners/runContext");
29
+ const { runDashboard, runDemo } = require("./runners/runDashboard");
30
+ const { runFix } = require("./runners/runFix");
31
+ const { runShip } = require("./runners/runShip");
32
+ const { runLaunch } = require("./runners/runLaunch");
33
+ const { runAutopilot } = require("./runners/runAutopilot");
34
+ const { runProof } = require("./runners/runProof");
35
+
36
+ // Graceful loading for modules that may have syntax issues
37
+ let runReality, runRealitySniff;
38
+ try {
39
+ runReality = require("./runners/runReality").runReality;
40
+ } catch (e) {
41
+ runReality = async () => { console.error("Reality runner unavailable:", e.message); return 1; };
42
+ }
43
+ try {
44
+ runRealitySniff = require("./runners/runRealitySniff").runRealitySniff;
45
+ } catch (e) {
46
+ runRealitySniff = async () => { console.error("RealitySniff runner unavailable:", e.message); return 1; };
47
+ }
48
+ const { runValidate } = require("./runners/runValidate");
49
+ const { runDoctor } = require("./runners/runDoctor");
50
+ const { runInit } = require("./runners/runInit");
51
+ const { runMcp } = require("./runners/runMcp");
52
+ const { runLogin, runLogout, runWhoami } = require("./runners/runAuth");
53
+ const {
54
+ runNaturalLanguage,
55
+ isNaturalLanguageCommand,
56
+ } = require("./runners/runNaturalLanguage");
57
+ const { runAIAgent } = require("./runners/runAIAgent");
58
+ const { runBadge } = require("./runners/runBadge");
59
+ const { runUpgrade } = require("./runners/runUpgrade");
60
+ const { runCertify } = require("./runners/runCertify");
61
+ const { runVerifyAgentOutput } = require("./runners/runVerifyAgentOutput");
62
+ const { runFixPacks } = require("./runners/runFixPacks");
63
+ const { runAudit } = require("./runners/runAudit");
64
+ const { runMdc } = require("./runners/runMdc");
65
+ const { runEnhancedShip } = require("./runners/runEnhancedShip");
66
+ const { runPromptFirewall } = require("./runners/runPromptFirewall");
67
+
68
+ // Route Truth v1 - ctx command
69
+ const { runCtx } = require("./runners/runCtx");
70
+ // Share command
71
+ const { runShare } = require("./runners/runShare");
72
+ // PR command
73
+ const { runPR } = require("./runners/runPR");
74
+ // Init GHA command
75
+ const { runInitGha } = require("./runners/runInitGha");
76
+ // Install command
77
+ const { runInstall } = require("./runners/runInstall");
78
+ // Prove command
79
+ const { runProve } = require("./runners/runProve");
80
+ // Watch command
81
+ const { runWatch } = require("./runners/runWatch");
82
+ // Status command
83
+ const { runStatus } = require("./runners/runStatus");
84
+ // Graph command - Reality Proof Graph
85
+ const { runGraph } = require("./runners/runGraph");
86
+ // Permissions command - AuthZ Matrix & IDOR
87
+ const { runPermissions } = require("./runners/runPermissions");
88
+ // Replay command - Record and replay user sessions
89
+ const { runReplay } = require("./runners/runReplay");
90
+ // Context Contracts commands
91
+ const { runCtxSync } = require("./runners/runCtxSync");
92
+ const { runCtxGuard } = require("./runners/runCtxGuard");
93
+ const { runCtxDiff } = require("./runners/runCtxDiff");
94
+
95
+ // Truth Pack v1 - Core truth system
96
+ let runTruthpack;
97
+ try {
98
+ runTruthpack = require("./runners/runTruthpack").runTruthpack;
99
+ } catch (e) {
100
+ runTruthpack = async () => { console.error("Truthpack runner unavailable:", e.message); return 1; };
101
+ }
102
+
103
+ const VERSION = getVersion();
104
+
105
+ // ANSI colors
106
+ const c = {
107
+ reset: "\x1b[0m",
108
+ dim: "\x1b[2m",
109
+ cyan: "\x1b[36m",
110
+ green: "\x1b[32m",
111
+ yellow: "\x1b[33m",
112
+ red: "\x1b[31m",
113
+ blue: "\x1b[34m",
114
+ magenta: "\x1b[35m",
115
+ };
116
+
117
+ // Detect CI/CD environment (non-interactive)
118
+ function isCI() {
119
+ return !!(
120
+ process.env.CI ||
121
+ process.env.CONTINUOUS_INTEGRATION ||
122
+ process.env.RAILWAY_ENVIRONMENT ||
123
+ process.env.VERCEL ||
124
+ process.env.NETLIFY ||
125
+ process.env.GITHUB_ACTIONS ||
126
+ process.env.GITLAB_CI ||
127
+ process.env.CIRCLECI ||
128
+ process.env.TRAVIS ||
129
+ process.env.BUILDKITE ||
130
+ process.env.RENDER ||
131
+ process.env.HEROKU ||
132
+ !process.stdin.isTTY
133
+ );
134
+ }
135
+
136
+ // ============================================================================
137
+ // TIER-BASED COMMAND ACCESS
138
+ // ============================================================================
139
+ // FREE ($0) - No API key needed
140
+ const FREE_COMMANDS = [
141
+ "help",
142
+ "version",
143
+ "doctor",
144
+ "init",
145
+ "login",
146
+ "logout",
147
+ "whoami",
148
+ "scan", // Route integrity + security analysis
149
+ "validate", // AI code validation
150
+ "badge", // Generate badges
151
+ "certify", // Certification badges (SEO fuel)
152
+ "context", // AI rules generator
153
+ "dashboard", // Real-time monitoring
154
+ "demo", // Interactive demo
155
+ "upgrade", // Subscription management
156
+ "verify-agent-output", // Verify AI agent output
157
+ "mdc", // MDC documentation generator
158
+ "prompt-firewall", // Prompt firewall (free tier with limited features)
159
+ "firewall", // Alias for prompt-firewall
160
+ "ctx", // Truth Pack generator
161
+ "truthpack", // Alias for ctx
162
+ "replay", // Record and replay user sessions
163
+ "graph", // Reality Proof Graph
164
+ "status", // Quick project status
165
+ "watch", // Continuous dev mode
166
+ "prove", // One command reality proof
167
+ "install", // Zero-friction onboarding
168
+ "pr", // PR comment generator
169
+ "share", // Share bundle generator
170
+ "reality", // Runtime UI verification
171
+ ];
172
+
173
+ // STARTER ($19/mo) - Requires API key with starter+ plan
174
+ const STARTER_COMMANDS = {
175
+ ship: "ship:audit", // Plain English audit
176
+ "enhanced-ship": "enhanced-ship:full", // Enhanced ship decision with all features
177
+ gate: "gate:ci", // CI/CD gate
178
+ reality: "reality:basic", // Browser testing
179
+ launch: "launch:checklist", // Pre-launch wizard
180
+ };
181
+
182
+ // PRO ($49/mo) - Requires API key with pro+ plan
183
+ const PRO_COMMANDS = {
184
+ "ai-test": "ai:agent", // AI Agent testing
185
+ ai: "ai:agent",
186
+ agent: "ai:agent",
187
+ // fix: removed - handled specially to allow --plan-only on FREE tier
188
+ autopilot: "autopilot:enable", // Continuous protection
189
+ };
190
+
191
+ // Commands with FREE tier read-only modes
192
+ const TIERED_COMMANDS = {
193
+ fix: {
194
+ freeArgs: ["--plan-only", "--help", "-h"], // Allow these args on FREE
195
+ requiredScope: "fix:apply",
196
+ tier: "pro",
197
+ },
198
+ };
199
+
200
+ // Special: proof command has sub-modes with different tiers
201
+ const PROOF_COMMANDS = {
202
+ mocks: "proof:mocks", // Starter+
203
+ reality: "proof:reality", // Pro+
204
+ };
205
+
206
+ // Commands that always work (utilities)
207
+ const UTILITY_COMMANDS = ["mcp", "rules", "api", "deps", "sbom", "fixpacks"];
208
+
209
+ // Compliance tier commands
210
+ const COMPLIANCE_COMMANDS = {
211
+ audit: "audit:full", // Full audit trail
212
+ };
213
+
214
+ async function prompt(question) {
215
+ const rl = readline.createInterface({
216
+ input: process.stdin,
217
+ output: process.stdout,
218
+ });
219
+ return new Promise((resolve) => {
220
+ rl.question(question, (answer) => {
221
+ rl.close();
222
+ resolve(answer.trim());
223
+ });
224
+ });
225
+ }
226
+
227
+ async function showWelcomeAndPromptLogin() {
228
+ console.log(`
229
+ ${c.cyan}╔════════════════════════════════════════════════════════════╗
230
+ ║ ${c.reset}🛡️ VIBECHECK${c.cyan} ║
231
+ ╚════════════════════════════════════════════════════════════╝${c.reset}
232
+
233
+ ${c.dim}Ship with confidence. Catch fake features before your users do.${c.reset}
234
+
235
+ `);
236
+
237
+ const { key, source } = getApiKey();
238
+
239
+ if (!key) {
240
+ // In CI/CD environments, skip interactive prompts
241
+ if (isCI()) {
242
+ console.log(`${c.yellow}⚠ No API key found${c.reset}`);
243
+ console.log(
244
+ `${c.dim}Running in CI mode with FREE tier features.${c.reset}`,
245
+ );
246
+ console.log(
247
+ `${c.dim}Set VIBECHECK_API_KEY env var to unlock more features.${c.reset}\n`,
248
+ );
249
+ return { key: null, entitlements: null };
250
+ }
251
+
252
+ console.log(`${c.yellow}⚠ No API key found${c.reset}`);
253
+ console.log(`
254
+ ${c.dim}To unlock all features, you need a vibecheck API key.${c.reset}
255
+
256
+ ${c.green}FREE${c.reset} scan, validate, badge, doctor, init
257
+ ${c.cyan}STARTER${c.reset} ship, gate, reality, launch, proof mocks ${c.dim}($29/mo)${c.reset}
258
+ ${c.magenta}PRO${c.reset} ai-test, fix, autopilot, proof reality ${c.dim}($99/mo)${c.reset}
259
+
260
+ ${c.dim}Get your API key at: ${c.cyan}https://vibecheckai.dev/settings/keys${c.reset}
261
+ `);
262
+
263
+ const answer = await prompt(
264
+ `${c.cyan}?${c.reset} Do you have an API key? (y/N) `,
265
+ );
266
+
267
+ if (answer.toLowerCase() === "y") {
268
+ const apiKey = await prompt(`${c.cyan}?${c.reset} Paste your API key: `);
269
+ if (apiKey) {
270
+ const { saveApiKey } = require("./runners/lib/auth");
271
+ console.log(`\n${c.dim}Verifying...${c.reset}`);
272
+
273
+ const entitlements = await getEntitlements(apiKey);
274
+ if (entitlements) {
275
+ saveApiKey(apiKey);
276
+ console.log(
277
+ `\n${c.green}✓${c.reset} Logged in as ${entitlements.user?.name || "User"}`,
278
+ );
279
+ console.log(
280
+ `${c.dim}Plan: ${entitlements.plan?.toUpperCase() || "FREE"}${c.reset}\n`,
281
+ );
282
+ return { key: apiKey, entitlements };
283
+ } else {
284
+ console.log(`\n${c.red}✗${c.reset} Invalid API key\n`);
285
+ }
286
+ }
287
+ }
288
+
289
+ console.log(`
290
+ ${c.dim}Continuing in FREE mode. Some features will be limited.${c.reset}
291
+ ${c.dim}Run ${c.cyan}vibecheck login${c.dim} anytime to upgrade.${c.reset}
292
+ `);
293
+ return { key: null, entitlements: null };
294
+ }
295
+
296
+ // User has API key, get entitlements
297
+ const entitlements = await getEntitlements(key);
298
+ return { key, entitlements };
299
+ }
300
+
301
+ async function checkCommandAccess(cmd, entitlements, args = []) {
302
+ // Free commands always work (no API key needed)
303
+ if (FREE_COMMANDS.includes(cmd)) {
304
+ return { allowed: true, tier: "free" };
305
+ }
306
+
307
+ // Utility commands always work
308
+ if (UTILITY_COMMANDS.includes(cmd)) {
309
+ return { allowed: true, tier: "utility" };
310
+ }
311
+
312
+ // Tiered commands with FREE read-only modes
313
+ if (TIERED_COMMANDS[cmd]) {
314
+ const config = TIERED_COMMANDS[cmd];
315
+ // Check if using a FREE tier argument
316
+ const hasFreeArg = args.some(arg => config.freeArgs.includes(arg));
317
+ // Also allow if no fix pack specified (shows help)
318
+ const hasNoFixPack = cmd === "fix" && !args.some(arg => !arg.startsWith("-"));
319
+
320
+ if (hasFreeArg || hasNoFixPack) {
321
+ return { allowed: true, tier: "free" };
322
+ }
323
+
324
+ // Requires paid tier
325
+ if (!entitlements) {
326
+ return {
327
+ allowed: false,
328
+ reason: `${c.yellow}${cmd}${c.reset} requires a ${c.magenta}PRO${c.reset} plan.\n\n Run ${c.cyan}vibecheck login${c.reset} to authenticate.\n Get your API key at: ${c.cyan}https://vibecheckai.dev/settings/keys${c.reset}`,
329
+ };
330
+ }
331
+
332
+ if (
333
+ entitlements.scopes?.includes(config.requiredScope) ||
334
+ entitlements.scopes?.includes("*")
335
+ ) {
336
+ return { allowed: true, tier: config.tier };
337
+ }
338
+
339
+ return {
340
+ allowed: false,
341
+ reason: `Your ${c.yellow}${entitlements.plan?.toUpperCase()}${c.reset} plan doesn't include this feature.\n\n Required: ${config.requiredScope}\n Upgrade to ${c.magenta}PRO${c.reset} at: ${c.cyan}https://vibecheckai.dev/pricing${c.reset}`,
342
+ };
343
+ }
344
+
345
+ // Special handling for proof command (has sub-modes with different tiers)
346
+ if (cmd === "proof") {
347
+ const subMode = args[0]; // mocks or reality
348
+ if (subMode === "mocks") {
349
+ // Starter+ required
350
+ if (!entitlements) {
351
+ return {
352
+ allowed: false,
353
+ reason: `${c.yellow}proof mocks${c.reset} requires a ${c.cyan}STARTER${c.reset} plan or higher.\n\n Run ${c.cyan}vibecheck login${c.reset} to authenticate.\n Get your API key at: ${c.cyan}https://vibecheckai.dev/settings/keys${c.reset}`,
354
+ };
355
+ }
356
+ if (
357
+ entitlements.scopes?.includes("proof:mocks") ||
358
+ entitlements.scopes?.includes("*")
359
+ ) {
360
+ return { allowed: true, tier: "starter" };
361
+ }
362
+ return {
363
+ allowed: false,
364
+ reason: `Your ${c.yellow}${entitlements.plan?.toUpperCase()}${c.reset} plan doesn't include mock detection.\n\n Upgrade to ${c.cyan}STARTER${c.reset} at: ${c.cyan}https://vibecheckai.dev/pricing${c.reset}`,
365
+ };
366
+ } else if (subMode === "reality") {
367
+ // Pro+ required
368
+ if (!entitlements) {
369
+ return {
370
+ allowed: false,
371
+ reason: `${c.yellow}proof reality${c.reset} requires a ${c.magenta}PRO${c.reset} plan.\n\n Run ${c.cyan}vibecheck login${c.reset} to authenticate.\n Get your API key at: ${c.cyan}https://vibecheckai.dev/settings/keys${c.reset}`,
372
+ };
373
+ }
374
+ if (
375
+ entitlements.scopes?.includes("proof:reality") ||
376
+ entitlements.scopes?.includes("*")
377
+ ) {
378
+ return { allowed: true, tier: "pro" };
379
+ }
380
+ return {
381
+ allowed: false,
382
+ reason: `Your ${c.yellow}${entitlements.plan?.toUpperCase()}${c.reset} plan doesn't include runtime verification.\n\n Upgrade to ${c.magenta}PRO${c.reset} at: ${c.cyan}https://vibecheckai.dev/pricing${c.reset}`,
383
+ };
384
+ }
385
+ // No submode - show help
386
+ return { allowed: true };
387
+ }
388
+
389
+ // STARTER tier commands
390
+ if (STARTER_COMMANDS[cmd]) {
391
+ const requiredScope = STARTER_COMMANDS[cmd];
392
+
393
+ if (!entitlements) {
394
+ return {
395
+ allowed: false,
396
+ reason: `${c.yellow}${cmd}${c.reset} requires a ${c.cyan}STARTER${c.reset} plan or higher.\n\n Run ${c.cyan}vibecheck login${c.reset} to authenticate.\n Get your API key at: ${c.cyan}https://vibecheckai.dev/settings/keys${c.reset}`,
397
+ };
398
+ }
399
+
400
+ if (
401
+ entitlements.scopes?.includes(requiredScope) ||
402
+ entitlements.scopes?.includes("*")
403
+ ) {
404
+ return { allowed: true, tier: "starter" };
405
+ }
406
+
407
+ return {
408
+ allowed: false,
409
+ reason: `Your ${c.yellow}${entitlements.plan?.toUpperCase()}${c.reset} plan doesn't include this feature.\n\n Required: ${requiredScope}\n Upgrade to ${c.cyan}STARTER${c.reset} at: ${c.cyan}https://vibecheckai.dev/pricing${c.reset}`,
410
+ };
411
+ }
412
+
413
+ // PRO tier commands
414
+ if (PRO_COMMANDS[cmd]) {
415
+ const requiredScope = PRO_COMMANDS[cmd];
416
+
417
+ if (!entitlements) {
418
+ return {
419
+ allowed: false,
420
+ reason: `${c.yellow}${cmd}${c.reset} requires a ${c.magenta}PRO${c.reset} plan.\n\n Run ${c.cyan}vibecheck login${c.reset} to authenticate.\n Get your API key at: ${c.cyan}https://vibecheckai.dev/settings/keys${c.reset}`,
421
+ };
422
+ }
423
+
424
+ if (
425
+ entitlements.scopes?.includes(requiredScope) ||
426
+ entitlements.scopes?.includes("*")
427
+ ) {
428
+ return { allowed: true, tier: "pro" };
429
+ }
430
+
431
+ return {
432
+ allowed: false,
433
+ reason: `Your ${c.yellow}${entitlements.plan?.toUpperCase()}${c.reset} plan doesn't include this feature.\n\n Required: ${requiredScope}\n Upgrade to ${c.magenta}PRO${c.reset} at: ${c.cyan}https://vibecheckai.dev/pricing${c.reset}`,
434
+ };
435
+ }
436
+
437
+ return { allowed: true };
438
+ }
439
+
440
+ function printHelp() {
441
+ console.log(
442
+ `
443
+ ${c.cyan}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${c.reset}
444
+ ${c.cyan} VIBECHECK${c.reset} - Ship with confidence
445
+ ${c.cyan}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${c.reset}
446
+
447
+ ${c.green}🚀 QUICK START${c.reset}
448
+
449
+ ${c.cyan}ship${c.reset} "Is my app ready?" - Plain English, traffic light score
450
+ ${c.cyan}ship --fix${c.reset} Same + auto-fix problems
451
+
452
+ ${c.yellow}🧪 TESTING${c.reset} (each does something different!)
453
+
454
+ ${c.cyan}scan${c.reset} ${c.dim}Route Integrity${c.reset} - dead links, orphans, coverage, security 🗺️
455
+ ${c.cyan}scan --truth${c.reset} ${c.dim}+ Build manifest${c.reset} verification (CI/ship ready)
456
+ ${c.cyan}scan --reality${c.reset} ${c.dim}+ Playwright${c.reset} runtime proof (best-in-class)
457
+ ${c.cyan}reality${c.reset} ${c.dim}Browser testing${c.reset} - clicks buttons, fills forms, finds broken UI
458
+ ${c.cyan}ai-test${c.reset} ${c.dim}AI Agent${c.reset} - autonomous testing + generates fix prompts 🤖
459
+
460
+ ${c.magenta}🚦 CI/CD & GATES${c.reset}
461
+
462
+ ${c.cyan}gate${c.reset} Block bad deploys - pass/fail for CI pipelines
463
+ ${c.cyan}proof mocks${c.reset} Block mock/demo code from reaching production
464
+ ${c.cyan}proof reality${c.reset} Runtime GO/NO-GO verification with Playwright
465
+
466
+ ${c.blue}🔧 FIX & AUTOMATE${c.reset}
467
+
468
+ ${c.cyan}fix${c.reset} Auto-fix detected issues (--plan first, then --apply)
469
+ ${c.cyan}autopilot${c.reset} Continuous protection - weekly reports, auto-PRs
470
+ ${c.cyan}badge${c.reset} Generate Ship Badge for your README/PR
471
+ ${c.cyan}certify${c.reset} Generate vibecheck Certified badge with verification link
472
+
473
+ ${c.dim}📦 TRUTH SYSTEM${c.reset}
474
+
475
+ ${c.cyan}ctx${c.reset} Generate Truth Pack - ground truth for AI agents
476
+ ${c.cyan}ctx sync${c.reset} Generate contracts from truthpack (routes/env/auth/external)
477
+ ${c.cyan}ctx guard${c.reset} CI gate - fail on contract drift (BLOCK on route/env/auth drift)
478
+ ${c.cyan}ctx diff${c.reset} Preview contract changes before syncing
479
+ ${c.cyan}ctx --snapshot${c.reset} Save snapshot to .vibecheck/truth/snapshots/
480
+ ${c.cyan}graph${c.reset} Build Reality Proof Graph - end-to-end causal chains
481
+
482
+ ${c.dim}📦 EXTRAS${c.reset}
483
+
484
+ ${c.cyan}audit${c.reset} View/export audit trail (Compliance+ tier)
485
+ ${c.cyan}context${c.reset} Generate AI rules files (.cursorrules, .windsurf/rules, etc.)
486
+ ${c.cyan}dashboard${c.reset} Real-time monitoring dashboard with live metrics
487
+ ${c.cyan}demo${c.reset} Interactive terminal features showcase
488
+ ${c.cyan}launch${c.reset} Pre-launch checklist wizard
489
+ ${c.cyan}validate${c.reset} Check AI-generated code for hallucinations
490
+ ${c.cyan}init${c.reset} Set up vibecheck in your project
491
+ ${c.cyan}doctor${c.reset} Debug environment issues
492
+ ${c.cyan}mcp${c.reset} Start MCP server for AI editors
493
+
494
+ ${c.dim}🔑 ACCOUNT${c.reset}
495
+
496
+ ${c.cyan}login${c.reset} Sign in with API key
497
+ ${c.cyan}logout${c.reset} Sign out
498
+ ${c.cyan}whoami${c.reset} Show current user & plan
499
+ ${c.cyan}upgrade${c.reset} Manage subscription & view usage
500
+
501
+ ${c.cyan}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${c.reset}
502
+
503
+ ${c.green}Examples:${c.reset}
504
+
505
+ vibecheck ship ${c.dim}# Quick health check${c.reset}
506
+ vibecheck scan ${c.dim}# Route integrity + security analysis${c.reset}
507
+ vibecheck scan --truth ${c.dim}# + Build manifest verification${c.reset}
508
+ vibecheck scan --reality --url http://localhost:3000 ${c.dim}# Full proof${c.reset}
509
+ vibecheck reality --url https://... ${c.dim}# Test live app${c.reset}
510
+ vibecheck ai-test --url https://... ${c.dim}# AI explores your app${c.reset}
511
+ vibecheck gate ${c.dim}# Block bad deploy in CI${c.reset}
512
+ vibecheck badge ${c.dim}# Generate ship badge${c.reset}
513
+
514
+ ${c.dim}Run 'vibecheck <command> --help' for details.${c.reset}
515
+ `.trim(),
516
+ );
517
+ }
518
+
519
+ (async function main() {
520
+ const rawArgs = process.argv.slice(2);
521
+
522
+ // Check if the first argument looks like a natural language command
523
+ // Natural language commands are typically quoted strings or multi-word phrases
524
+ const firstArg = rawArgs[0];
525
+ if (firstArg && isNaturalLanguageCommand(firstArg)) {
526
+ // Join all args as the natural language input
527
+ const nlInput = rawArgs.join(" ");
528
+ const exitCode = await runNaturalLanguage(nlInput);
529
+ process.exit(exitCode);
530
+ }
531
+
532
+ const { legacyFrom, routed } = routeArgv(process.argv);
533
+
534
+ const cmd = routed[0];
535
+ const args = routed.slice(1);
536
+
537
+ // Commands that skip auth check entirely
538
+ const skipAuthCommands = [
539
+ "help",
540
+ "-h",
541
+ "--help",
542
+ "version",
543
+ "login",
544
+ "logout",
545
+ "whoami",
546
+ "doctor",
547
+ ];
548
+
549
+ if (!cmd || cmd === "-h" || cmd === "--help" || cmd === "help") {
550
+ printHelp();
551
+ process.exit(0);
552
+ }
553
+
554
+ // Check for first run (no API key) - only for non-skip commands
555
+ let authInfo = { key: null, entitlements: null };
556
+
557
+ if (!skipAuthCommands.includes(cmd)) {
558
+ // Check if this is a free command or utility - if so, skip entitlement checks
559
+ const isFreeCommand = FREE_COMMANDS.includes(cmd) || UTILITY_COMMANDS.includes(cmd);
560
+ const isKnownCommand = isFreeCommand ||
561
+ STARTER_COMMANDS[cmd] ||
562
+ PRO_COMMANDS[cmd] ||
563
+ TIERED_COMMANDS[cmd] ||
564
+ PROOF_COMMANDS[cmd] ||
565
+ COMPLIANCE_COMMANDS[cmd];
566
+
567
+ // For unknown commands, skip auth and let them fail with "Unknown command" message
568
+ if (!isKnownCommand) {
569
+ // Skip authentication for unknown commands - they'll show help/error message
570
+ authInfo = { key: null, entitlements: null };
571
+ } else {
572
+ const { key } = getApiKey();
573
+
574
+ // First run without API key - show welcome and prompt
575
+ if (!key && !process.env.VIBECHECK_SKIP_AUTH) {
576
+ authInfo = await showWelcomeAndPromptLogin();
577
+ } else if (key && !isFreeCommand) {
578
+ // Has API key and command requires auth - get entitlements silently
579
+ // For free commands, we don't need to check entitlements
580
+ try {
581
+ const entitlements = await getEntitlements(key);
582
+ authInfo = { key, entitlements };
583
+ } catch (error) {
584
+ // If API is unavailable, allow free commands to proceed
585
+ // Paid commands will be blocked in checkCommandAccess
586
+ if (isFreeCommand) {
587
+ authInfo = { key, entitlements: null };
588
+ } else {
589
+ throw error;
590
+ }
591
+ }
592
+ } else if (key && isFreeCommand) {
593
+ // Free command with key - no need to fetch entitlements
594
+ authInfo = { key, entitlements: null };
595
+ }
596
+ }
597
+
598
+ // Check if user has access to this command (only for known commands)
599
+ if (isKnownCommand) {
600
+ const access = await checkCommandAccess(cmd, authInfo.entitlements, args);
601
+
602
+ if (!access.allowed) {
603
+ console.log(`\n${c.red}✗ Access Denied${c.reset}\n`);
604
+ console.log(access.reason);
605
+ console.log("");
606
+ process.exit(1);
607
+ }
608
+
609
+ // Show tier info for paid features
610
+ if (access.tier === "starter") {
611
+ console.log(`${c.cyan}▸ STARTER${c.reset} ${c.dim}feature${c.reset}\n`);
612
+ } else if (access.tier === "pro") {
613
+ console.log(`${c.magenta}▸ PRO${c.reset} ${c.dim}feature${c.reset}\n`);
614
+ }
615
+ }
616
+ }
617
+
618
+ // Deprecation suggestions
619
+ if (legacyFrom) {
620
+ const suggestion = routed.slice(0, 2).join(" ");
621
+ warnDeprecationOnce(legacyFrom, suggestion, VERSION);
622
+ }
623
+
624
+ try {
625
+ let exitCode = 0;
626
+ switch (cmd) {
627
+ case "scan":
628
+ exitCode = await runScan(args);
629
+ break;
630
+ case "gate":
631
+ exitCode = await runGate(args);
632
+ break;
633
+ case "ship":
634
+ exitCode = await runShip(args);
635
+ break;
636
+ case "enhanced-ship":
637
+ exitCode = await runEnhancedShip(args);
638
+ break;
639
+ case "prompt-firewall":
640
+ case "firewall":
641
+ exitCode = await runPromptFirewall(args);
642
+ break;
643
+ case "launch":
644
+ exitCode = await runLaunch(args);
645
+ break;
646
+ case "autopilot":
647
+ exitCode = await runAutopilot(args);
648
+ break;
649
+ case "fix":
650
+ exitCode = await runFix(args);
651
+ break;
652
+ case "share":
653
+ exitCode = await runShare({
654
+ repoRoot: process.cwd(),
655
+ missionDir: args.find((a, i) => args[i-1] === '--mission-dir'),
656
+ outputDir: args.find((a, i) => args[i-1] === '--output-dir'),
657
+ prComment: args.includes('--pr-comment')
658
+ });
659
+ break;
660
+ case "pr":
661
+ exitCode = await runPR({
662
+ repoRoot: process.cwd(),
663
+ fastifyEntry: args.find((a, i) => args[i-1] === '--fastify-entry'),
664
+ out: args.find((a, i) => args[i-1] === '--out'),
665
+ failOnWarn: args.includes('--fail-on-warn'),
666
+ maxFindings: Number(args.find((a, i) => args[i-1] === '--max-findings')) || 12
667
+ });
668
+ break;
669
+ case "install":
670
+ exitCode = await runInstall({ repoRoot: process.cwd() });
671
+ break;
672
+ case "prove":
673
+ exitCode = await runProve({
674
+ repoRoot: process.cwd(),
675
+ url: args.find((a, i) => args[i-1] === '--url' || args[i-1] === '-u'),
676
+ auth: args.find((a, i) => args[i-1] === '--auth'),
677
+ storageState: args.find((a, i) => args[i-1] === '--storage-state'),
678
+ fastifyEntry: args.find((a, i) => args[i-1] === '--fastify-entry'),
679
+ maxFixRounds: Number(args.find((a, i) => args[i-1] === '--max-fix-rounds')) || 3,
680
+ maxMissions: Number(args.find((a, i) => args[i-1] === '--max-missions')) || 8,
681
+ maxSteps: Number(args.find((a, i) => args[i-1] === '--max-steps')) || 10,
682
+ skipReality: args.includes('--skip-reality'),
683
+ skipFix: args.includes('--skip-fix'),
684
+ headed: args.includes('--headed'),
685
+ danger: args.includes('--danger'),
686
+ maxPages: Number(args.find((a, i) => args[i-1] === '--max-pages')) || 18,
687
+ maxDepth: Number(args.find((a, i) => args[i-1] === '--max-depth')) || 2,
688
+ timeoutMs: Number(args.find((a, i) => args[i-1] === '--timeout-ms')) || 15000
689
+ });
690
+ break;
691
+ case "watch":
692
+ exitCode = await runWatch({
693
+ repoRoot: process.cwd(),
694
+ fastifyEntry: args.find((a, i) => args[i-1] === '--fastify-entry'),
695
+ debounceMs: Number(args.find((a, i) => args[i-1] === '--debounce')) || 500,
696
+ clearScreen: !args.includes('--no-clear')
697
+ });
698
+ break;
699
+ case "status":
700
+ exitCode = await runStatus({
701
+ repoRoot: process.cwd(),
702
+ json: args.includes('--json')
703
+ });
704
+ break;
705
+ case "proof":
706
+ exitCode = await runProof(args);
707
+ break;
708
+ case "reality":
709
+ exitCode = await runReality({
710
+ repoRoot: process.cwd(),
711
+ url: args.find((a, i) => args[i-1] === '--url' || args[i-1] === '-u'),
712
+ auth: args.find((a, i) => args[i-1] === '--auth'),
713
+ storageState: args.find((a, i) => args[i-1] === '--storage-state'),
714
+ saveStorageState: args.find((a, i) => args[i-1] === '--save-storage-state'),
715
+ truthpack: args.find((a, i) => args[i-1] === '--truthpack'),
716
+ verifyAuth: args.includes('--verify-auth'),
717
+ headed: args.includes('--headed'),
718
+ maxPages: Number(args.find((a, i) => args[i-1] === '--max-pages')) || 18,
719
+ maxDepth: Number(args.find((a, i) => args[i-1] === '--max-depth')) || 2,
720
+ danger: args.includes('--danger'),
721
+ timeoutMs: Number(args.find((a, i) => args[i-1] === '--timeout-ms')) || 15000
722
+ });
723
+ break;
724
+ case "reality-sniff":
725
+ case "sniff":
726
+ exitCode = await runRealitySniff(args);
727
+ break;
728
+ case "ai-test":
729
+ case "ai":
730
+ case "agent":
731
+ exitCode = await runAIAgent(args);
732
+ break;
733
+ case "validate":
734
+ exitCode = await runValidate(args);
735
+ break;
736
+ case "doctor":
737
+ exitCode = runDoctor(args);
738
+ break;
739
+ case "init":
740
+ // Enterprise init handles all flags including --gha, --gitlab, --compliance, etc.
741
+ exitCode = await runInit(args);
742
+ break;
743
+ case "mcp":
744
+ exitCode = runMcp(args);
745
+ break;
746
+ case "login":
747
+ exitCode = await runLogin(args);
748
+ break;
749
+ case "logout":
750
+ exitCode = await runLogout(args);
751
+ break;
752
+ case "whoami":
753
+ exitCode = await runWhoami(args);
754
+ break;
755
+ case "badge":
756
+ exitCode = await runBadge(args);
757
+ break;
758
+ case "context":
759
+ case "rules":
760
+ exitCode = await runContext(args);
761
+ break;
762
+ case "dashboard":
763
+ exitCode = await runDashboard(args);
764
+ break;
765
+ case "demo":
766
+ exitCode = await runDemo(args);
767
+ break;
768
+ case "upgrade":
769
+ exitCode = await runUpgrade(args);
770
+ break;
771
+ case "certify":
772
+ exitCode = await runCertify(args, process.cwd());
773
+ break;
774
+ case "verify-agent-output":
775
+ exitCode = await runVerifyAgentOutput(args);
776
+ break;
777
+ case "fixpacks":
778
+ exitCode = await runFixPacks(args);
779
+ break;
780
+ case "audit":
781
+ exitCode = await runAudit(args);
782
+ break;
783
+ case "mdc":
784
+ exitCode = await runMdc(args);
785
+ break;
786
+ case "graph":
787
+ exitCode = await runGraph(args);
788
+ break;
789
+ case "permissions":
790
+ case "authz":
791
+ exitCode = await runPermissions(args);
792
+ break;
793
+ case "ctx":
794
+ case "truthpack":
795
+ // Check for subcommands
796
+ if (args[0] === "sync") {
797
+ exitCode = await runCtxSync({
798
+ repoRoot: process.cwd(),
799
+ fastifyEntry: args.find((a, i) => args[i-1] === '--fastify-entry')
800
+ });
801
+ break;
802
+ }
803
+ if (args[0] === "guard") {
804
+ exitCode = await runCtxGuard.main(args.slice(1));
805
+ break;
806
+ }
807
+ if (args[0] === "diff") {
808
+ const { main: ctxDiffMain } = require("./runners/runCtxDiff");
809
+ exitCode = await ctxDiffMain(args.slice(1));
810
+ break;
811
+ }
812
+ // Parse args for ctx command - use Route Truth v1
813
+ const fastifyEntryIdx = args.indexOf('--fastify-entry');
814
+ const fastifyEntry = fastifyEntryIdx !== -1 ? args[fastifyEntryIdx + 1] : undefined;
815
+ await runCtx({
816
+ repoRoot: process.cwd(),
817
+ fastifyEntry,
818
+ print: args.includes('--print'),
819
+ });
820
+ exitCode = 0;
821
+ break;
822
+ case "version":
823
+ console.log(`vibecheck v${VERSION}`);
824
+ break;
825
+ default:
826
+ // Try natural language parsing as fallback for unknown commands
827
+ const nlInput = [cmd, ...args].join(" ");
828
+ if (isNaturalLanguageCommand(nlInput)) {
829
+ exitCode = await runNaturalLanguage(nlInput);
830
+ } else {
831
+ process.stderr.write(`Unknown command: ${cmd}\n\n`);
832
+ printHelp();
833
+ exitCode = 1;
834
+ }
835
+ }
836
+ process.exit(exitCode);
837
+ } catch (err) {
838
+ process.stderr.write(
839
+ err && err.stack ? err.stack + "\n" : String(err) + "\n",
840
+ );
841
+ process.exit(1);
842
+ }
843
+ })();