@vibecheckai/cli 3.2.5 → 3.3.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 (197) hide show
  1. package/bin/.generated +25 -25
  2. package/bin/dev/run-v2-torture.js +30 -30
  3. package/bin/registry.js +192 -5
  4. package/bin/runners/lib/__tests__/entitlements-v2.test.js +295 -295
  5. package/bin/runners/lib/agent-firewall/change-packet/builder.js +280 -6
  6. package/bin/runners/lib/agent-firewall/critic/index.js +151 -0
  7. package/bin/runners/lib/agent-firewall/critic/judge.js +432 -0
  8. package/bin/runners/lib/agent-firewall/critic/prompts.js +305 -0
  9. package/bin/runners/lib/agent-firewall/lawbook/distributor.js +465 -0
  10. package/bin/runners/lib/agent-firewall/lawbook/evaluator.js +604 -0
  11. package/bin/runners/lib/agent-firewall/lawbook/index.js +304 -0
  12. package/bin/runners/lib/agent-firewall/lawbook/registry.js +514 -0
  13. package/bin/runners/lib/agent-firewall/lawbook/schema.js +420 -0
  14. package/bin/runners/lib/agent-firewall/logger.js +141 -0
  15. package/bin/runners/lib/agent-firewall/policy/loader.js +312 -4
  16. package/bin/runners/lib/agent-firewall/policy/rules/ghost-env.js +113 -1
  17. package/bin/runners/lib/agent-firewall/policy/rules/ghost-route.js +133 -6
  18. package/bin/runners/lib/agent-firewall/proposal/extractor.js +394 -0
  19. package/bin/runners/lib/agent-firewall/proposal/index.js +212 -0
  20. package/bin/runners/lib/agent-firewall/proposal/schema.js +251 -0
  21. package/bin/runners/lib/agent-firewall/proposal/validator.js +386 -0
  22. package/bin/runners/lib/agent-firewall/reality/index.js +332 -0
  23. package/bin/runners/lib/agent-firewall/reality/state.js +625 -0
  24. package/bin/runners/lib/agent-firewall/reality/watcher.js +322 -0
  25. package/bin/runners/lib/agent-firewall/risk/index.js +173 -0
  26. package/bin/runners/lib/agent-firewall/risk/scorer.js +328 -0
  27. package/bin/runners/lib/agent-firewall/risk/thresholds.js +321 -0
  28. package/bin/runners/lib/agent-firewall/risk/vectors.js +421 -0
  29. package/bin/runners/lib/agent-firewall/simulator/diff-simulator.js +472 -0
  30. package/bin/runners/lib/agent-firewall/simulator/import-resolver.js +346 -0
  31. package/bin/runners/lib/agent-firewall/simulator/index.js +181 -0
  32. package/bin/runners/lib/agent-firewall/simulator/route-validator.js +380 -0
  33. package/bin/runners/lib/agent-firewall/time-machine/incident-correlator.js +661 -0
  34. package/bin/runners/lib/agent-firewall/time-machine/index.js +267 -0
  35. package/bin/runners/lib/agent-firewall/time-machine/replay-engine.js +436 -0
  36. package/bin/runners/lib/agent-firewall/time-machine/state-reconstructor.js +490 -0
  37. package/bin/runners/lib/agent-firewall/time-machine/timeline-builder.js +530 -0
  38. package/bin/runners/lib/analyzers.js +81 -18
  39. package/bin/runners/lib/api-client.js +269 -0
  40. package/bin/runners/lib/auth-truth.js +193 -193
  41. package/bin/runners/lib/authority-badge.js +425 -0
  42. package/bin/runners/lib/backup.js +62 -62
  43. package/bin/runners/lib/billing.js +107 -107
  44. package/bin/runners/lib/claims.js +118 -118
  45. package/bin/runners/lib/cli-output.js +7 -1
  46. package/bin/runners/lib/cli-ui.js +540 -540
  47. package/bin/runners/lib/contracts/auth-contract.js +202 -202
  48. package/bin/runners/lib/contracts/env-contract.js +181 -181
  49. package/bin/runners/lib/contracts/external-contract.js +206 -206
  50. package/bin/runners/lib/contracts/guard.js +168 -168
  51. package/bin/runners/lib/contracts/index.js +89 -89
  52. package/bin/runners/lib/contracts/plan-validator.js +311 -311
  53. package/bin/runners/lib/contracts/route-contract.js +199 -199
  54. package/bin/runners/lib/contracts.js +804 -804
  55. package/bin/runners/lib/detect.js +89 -89
  56. package/bin/runners/lib/doctor/autofix.js +254 -254
  57. package/bin/runners/lib/doctor/index.js +37 -37
  58. package/bin/runners/lib/doctor/modules/dependencies.js +325 -325
  59. package/bin/runners/lib/doctor/modules/index.js +46 -46
  60. package/bin/runners/lib/doctor/modules/network.js +250 -250
  61. package/bin/runners/lib/doctor/modules/project.js +312 -312
  62. package/bin/runners/lib/doctor/modules/runtime.js +224 -224
  63. package/bin/runners/lib/doctor/modules/security.js +348 -348
  64. package/bin/runners/lib/doctor/modules/system.js +213 -213
  65. package/bin/runners/lib/doctor/modules/vibecheck.js +394 -394
  66. package/bin/runners/lib/doctor/reporter.js +262 -262
  67. package/bin/runners/lib/doctor/service.js +262 -262
  68. package/bin/runners/lib/doctor/types.js +113 -113
  69. package/bin/runners/lib/doctor/ui.js +263 -263
  70. package/bin/runners/lib/doctor-v2.js +608 -608
  71. package/bin/runners/lib/drift.js +425 -425
  72. package/bin/runners/lib/enforcement.js +72 -72
  73. package/bin/runners/lib/enterprise-detect.js +603 -603
  74. package/bin/runners/lib/enterprise-init.js +942 -942
  75. package/bin/runners/lib/env-resolver.js +417 -417
  76. package/bin/runners/lib/env-template.js +66 -66
  77. package/bin/runners/lib/env.js +189 -189
  78. package/bin/runners/lib/error-handler.js +16 -9
  79. package/bin/runners/lib/exit-codes.js +275 -0
  80. package/bin/runners/lib/extractors/client-calls.js +990 -990
  81. package/bin/runners/lib/extractors/fastify-route-dump.js +573 -573
  82. package/bin/runners/lib/extractors/fastify-routes.js +426 -426
  83. package/bin/runners/lib/extractors/index.js +363 -363
  84. package/bin/runners/lib/extractors/next-routes.js +524 -524
  85. package/bin/runners/lib/extractors/proof-graph.js +431 -431
  86. package/bin/runners/lib/extractors/route-matcher.js +451 -451
  87. package/bin/runners/lib/extractors/truthpack-v2.js +377 -377
  88. package/bin/runners/lib/extractors/ui-bindings.js +547 -547
  89. package/bin/runners/lib/findings-schema.js +281 -281
  90. package/bin/runners/lib/firewall-prompt.js +50 -50
  91. package/bin/runners/lib/global-flags.js +37 -0
  92. package/bin/runners/lib/graph/graph-builder.js +265 -265
  93. package/bin/runners/lib/graph/html-renderer.js +413 -413
  94. package/bin/runners/lib/graph/index.js +32 -32
  95. package/bin/runners/lib/graph/runtime-collector.js +215 -215
  96. package/bin/runners/lib/graph/static-extractor.js +518 -518
  97. package/bin/runners/lib/help-formatter.js +413 -0
  98. package/bin/runners/lib/html-report.js +650 -650
  99. package/bin/runners/lib/llm.js +75 -75
  100. package/bin/runners/lib/logger.js +38 -0
  101. package/bin/runners/lib/meter.js +61 -61
  102. package/bin/runners/lib/missions/evidence.js +126 -126
  103. package/bin/runners/lib/patch.js +40 -40
  104. package/bin/runners/lib/permissions/auth-model.js +213 -213
  105. package/bin/runners/lib/permissions/idor-prover.js +205 -205
  106. package/bin/runners/lib/permissions/index.js +45 -45
  107. package/bin/runners/lib/permissions/matrix-builder.js +198 -198
  108. package/bin/runners/lib/pkgjson.js +28 -28
  109. package/bin/runners/lib/policy.js +295 -295
  110. package/bin/runners/lib/preflight.js +142 -142
  111. package/bin/runners/lib/reality/correlation-detectors.js +359 -359
  112. package/bin/runners/lib/reality/index.js +318 -318
  113. package/bin/runners/lib/reality/request-hashing.js +416 -416
  114. package/bin/runners/lib/reality/request-mapper.js +453 -453
  115. package/bin/runners/lib/reality/safety-rails.js +463 -463
  116. package/bin/runners/lib/reality/semantic-snapshot.js +408 -408
  117. package/bin/runners/lib/reality/toast-detector.js +393 -393
  118. package/bin/runners/lib/reality-findings.js +84 -84
  119. package/bin/runners/lib/receipts.js +179 -179
  120. package/bin/runners/lib/redact.js +29 -29
  121. package/bin/runners/lib/replay/capsule-manager.js +154 -154
  122. package/bin/runners/lib/replay/index.js +263 -263
  123. package/bin/runners/lib/replay/player.js +348 -348
  124. package/bin/runners/lib/replay/recorder.js +331 -331
  125. package/bin/runners/lib/report.js +135 -135
  126. package/bin/runners/lib/route-detection.js +1140 -1140
  127. package/bin/runners/lib/sandbox/index.js +59 -59
  128. package/bin/runners/lib/sandbox/proof-chain.js +399 -399
  129. package/bin/runners/lib/sandbox/sandbox-runner.js +205 -205
  130. package/bin/runners/lib/sandbox/worktree.js +174 -174
  131. package/bin/runners/lib/schema-validator.js +350 -350
  132. package/bin/runners/lib/schemas/contracts.schema.json +160 -160
  133. package/bin/runners/lib/schemas/finding.schema.json +100 -100
  134. package/bin/runners/lib/schemas/mission-pack.schema.json +206 -206
  135. package/bin/runners/lib/schemas/proof-graph.schema.json +176 -176
  136. package/bin/runners/lib/schemas/reality-report.schema.json +162 -162
  137. package/bin/runners/lib/schemas/share-pack.schema.json +180 -180
  138. package/bin/runners/lib/schemas/ship-report.schema.json +117 -117
  139. package/bin/runners/lib/schemas/truthpack-v2.schema.json +303 -303
  140. package/bin/runners/lib/schemas/validator.js +438 -438
  141. package/bin/runners/lib/score-history.js +282 -282
  142. package/bin/runners/lib/share-pack.js +239 -239
  143. package/bin/runners/lib/snippets.js +67 -67
  144. package/bin/runners/lib/unified-cli-output.js +604 -0
  145. package/bin/runners/lib/upsell.js +658 -510
  146. package/bin/runners/lib/usage.js +153 -153
  147. package/bin/runners/lib/validate-patch.js +156 -156
  148. package/bin/runners/lib/verdict-engine.js +628 -628
  149. package/bin/runners/reality/engine.js +917 -917
  150. package/bin/runners/reality/flows.js +122 -122
  151. package/bin/runners/reality/report.js +378 -378
  152. package/bin/runners/reality/session.js +193 -193
  153. package/bin/runners/runAgent.d.ts +5 -0
  154. package/bin/runners/runApprove.js +1200 -0
  155. package/bin/runners/runAuth.js +324 -95
  156. package/bin/runners/runCheckpoint.js +39 -21
  157. package/bin/runners/runClassify.js +859 -0
  158. package/bin/runners/runContext.js +136 -24
  159. package/bin/runners/runDoctor.js +108 -68
  160. package/bin/runners/runFirewall.d.ts +5 -0
  161. package/bin/runners/runFirewallHook.d.ts +5 -0
  162. package/bin/runners/runFix.js +6 -5
  163. package/bin/runners/runGuard.js +262 -168
  164. package/bin/runners/runInit.js +3 -2
  165. package/bin/runners/runMcp.js +130 -52
  166. package/bin/runners/runPolish.js +43 -20
  167. package/bin/runners/runProve.js +1 -2
  168. package/bin/runners/runReport.js +3 -2
  169. package/bin/runners/runScan.js +145 -44
  170. package/bin/runners/runShip.js +3 -4
  171. package/bin/runners/runTruth.d.ts +5 -0
  172. package/bin/runners/runValidate.js +19 -2
  173. package/bin/runners/runWatch.js +104 -53
  174. package/bin/vibecheck.js +106 -19
  175. package/mcp-server/HARDENING_SUMMARY.md +299 -0
  176. package/mcp-server/agent-firewall-interceptor.js +367 -31
  177. package/mcp-server/authority-tools.js +569 -0
  178. package/mcp-server/conductor/conflict-resolver.js +588 -0
  179. package/mcp-server/conductor/execution-planner.js +544 -0
  180. package/mcp-server/conductor/index.js +377 -0
  181. package/mcp-server/conductor/lock-manager.js +615 -0
  182. package/mcp-server/conductor/request-queue.js +550 -0
  183. package/mcp-server/conductor/session-manager.js +500 -0
  184. package/mcp-server/conductor/tools.js +510 -0
  185. package/mcp-server/index.js +1199 -208
  186. package/mcp-server/lib/api-client.cjs +305 -0
  187. package/mcp-server/lib/logger.cjs +30 -0
  188. package/mcp-server/logger.js +173 -0
  189. package/mcp-server/package.json +2 -2
  190. package/mcp-server/premium-tools.js +2 -2
  191. package/mcp-server/tier-auth.js +351 -136
  192. package/mcp-server/tools/index.js +72 -72
  193. package/mcp-server/truth-firewall-tools.js +145 -15
  194. package/mcp-server/vibecheck-tools.js +2 -2
  195. package/package.json +2 -3
  196. package/mcp-server/index.old.js +0 -4137
  197. package/mcp-server/package-lock.json +0 -165
@@ -1,3 +1,11 @@
1
+ /**
2
+ * vibecheck auth commands - Login, Logout, Whoami
3
+ *
4
+ * ═══════════════════════════════════════════════════════════════════════════════
5
+ * World-Class Authentication Experience
6
+ * ═══════════════════════════════════════════════════════════════════════════════
7
+ */
8
+
1
9
  const readline = require("readline");
2
10
  const {
3
11
  saveApiKey,
@@ -5,17 +13,22 @@ const {
5
13
  getApiKey,
6
14
  getEntitlements,
7
15
  } = require("./lib/auth");
8
-
9
- const c = {
10
- reset: "\x1b[0m",
11
- bold: "\x1b[1m",
12
- dim: "\x1b[2m",
13
- red: "\x1b[31m",
14
- green: "\x1b[32m",
15
- yellow: "\x1b[33m",
16
- blue: "\x1b[34m",
17
- cyan: "\x1b[36m",
18
- };
16
+ const { EXIT } = require("./lib/exit-codes");
17
+ const { parseGlobalFlags, shouldSuppressOutput, isJsonMode } = require("./lib/global-flags");
18
+ const {
19
+ ansi,
20
+ sym,
21
+ renderMinimalHeader,
22
+ renderSectionHeader,
23
+ renderSuccess,
24
+ renderError,
25
+ renderWarning,
26
+ renderKeyValue,
27
+ renderFooter,
28
+ Spinner,
29
+ getTierBadge,
30
+ TIER,
31
+ } = require("./lib/unified-cli-output");
19
32
 
20
33
  async function prompt(question) {
21
34
  const rl = readline.createInterface({
@@ -31,127 +44,343 @@ async function prompt(question) {
31
44
  });
32
45
  }
33
46
 
47
+ function isValidKeyFormat(key) {
48
+ if (!key || typeof key !== "string") return false;
49
+ return key.length >= 20 && /^[a-zA-Z0-9_-]+$/.test(key);
50
+ }
51
+
34
52
  async function runLogin(args) {
35
- if (args.includes("--help") || args.includes("-h")) {
53
+ const { flags } = parseGlobalFlags(args);
54
+ const quiet = shouldSuppressOutput(flags);
55
+ const json = isJsonMode(flags);
56
+
57
+ if (flags.help) {
36
58
  console.log(`
37
- ${c.bold}vibecheck login${c.reset} - Authenticate with API key
59
+ ${ansi.bold}USAGE${ansi.reset}
60
+ ${ansi.cyan}vibecheck login${ansi.reset} [options]
38
61
 
39
- ${c.bold}Usage:${c.reset}
40
- vibecheck login
62
+ ${ansi.dim}Aliases: auth, signin${ansi.reset}
41
63
 
42
- ${c.bold}Description:${c.reset}
43
- Authenticate with your Vibecheck API key.
44
- Get your API key from https://vibecheckai.dev/settings/keys
64
+ Authenticate with your Vibecheck API key to unlock paid features
65
+ and sync with the dashboard.
45
66
 
46
- ${c.bold}Options:${c.reset}
47
- ${c.cyan}--help, -h${c.reset} Show this help
67
+ ${ansi.bold}OPTIONS${ansi.reset}
68
+ ${ansi.cyan}--key <key>${ansi.reset} Provide API key directly (non-interactive)
69
+ ${ansi.cyan}--json${ansi.reset} Output result as JSON
70
+ ${ansi.cyan}--quiet, -q${ansi.reset} Suppress non-essential output
71
+ ${ansi.cyan}--help, -h${ansi.reset} Show this help
48
72
 
49
- ${c.bold}Examples:${c.reset}
73
+ ${ansi.bold}EXAMPLES${ansi.reset}
74
+ ${ansi.dim}# Interactive login${ansi.reset}
50
75
  vibecheck login
76
+
77
+ ${ansi.dim}# Non-interactive (CI/scripts)${ansi.reset}
78
+ vibecheck login --key YOUR_API_KEY
79
+
80
+ ${ansi.dim}# Using environment variable${ansi.reset}
81
+ VIBECHECK_API_KEY=xxx vibecheck whoami
82
+
83
+ ${ansi.bold}GET YOUR API KEY${ansi.reset}
84
+ https://vibecheckai.dev/settings/keys
85
+
86
+ ${ansi.dim}────────────────────────────────────────────────────────────────────${ansi.reset}
87
+ ${ansi.dim}Documentation: https://docs.vibecheckai.dev/authentication${ansi.reset}
51
88
  `);
52
- return 0;
89
+ return EXIT.SUCCESS;
53
90
  }
54
-
55
- console.log("\n 🔐 vibecheck LOGIN\n");
56
91
 
57
- const existing = getApiKey();
58
- if (existing.key) {
59
- console.log(` Already logged in (source: ${existing.source}).`);
60
- const answer = await prompt(" Do you want to overwrite? (y/N) ");
61
- if (answer.toLowerCase() !== "y") {
62
- console.log(" Cancelled.");
63
- return 0;
92
+ try {
93
+ let key = null;
94
+ const keyIndex = args.indexOf("--key");
95
+ if (keyIndex !== -1 && args[keyIndex + 1]) {
96
+ key = args[keyIndex + 1];
97
+ }
98
+
99
+ if (!quiet && !json) {
100
+ renderMinimalHeader("login", "free");
64
101
  }
65
- }
66
102
 
67
- console.log(
68
- " Paste your API key from https://vibecheckai.dev/settings/keys",
69
- );
70
- const key = await prompt(" API Key: ");
103
+ const existing = getApiKey();
104
+ if (existing.key && !key) {
105
+ if (!quiet && !json) {
106
+ renderWarning(`Already logged in (source: ${existing.source})`);
107
+ }
108
+
109
+ if (process.env.CI || !process.stdin.isTTY) {
110
+ if (json) {
111
+ console.log(JSON.stringify({ success: true, message: "Already logged in", source: existing.source }));
112
+ }
113
+ return EXIT.SUCCESS;
114
+ }
115
+
116
+ const answer = await prompt(` Overwrite existing credentials? (y/N) `);
117
+ if (answer.toLowerCase() !== "y") {
118
+ if (!quiet && !json) console.log(` ${ansi.dim}Cancelled.${ansi.reset}\n`);
119
+ if (json) console.log(JSON.stringify({ success: false, message: "Cancelled by user" }));
120
+ return EXIT.SUCCESS;
121
+ }
122
+ }
71
123
 
72
- if (!key) {
73
- console.error(" ❌ No key provided.");
74
- return 1;
75
- }
124
+ if (!key) {
125
+ if (process.env.CI || !process.stdin.isTTY) {
126
+ const errorMsg = "No API key provided. Use --key flag or set VIBECHECK_API_KEY environment variable.";
127
+ if (json) {
128
+ console.log(JSON.stringify({ success: false, error: errorMsg }));
129
+ } else {
130
+ renderError(errorMsg);
131
+ }
132
+ return EXIT.USER_ERROR;
133
+ }
134
+
135
+ if (!quiet && !json) {
136
+ console.log(` ${ansi.dim}Get your API key at: https://vibecheckai.dev/settings/keys${ansi.reset}\n`);
137
+ }
138
+ key = await prompt(` ${sym.key} API Key: `);
139
+ }
76
140
 
77
- // Validate key by fetching entitlements
78
- console.log(" Verifying...");
79
- const entitlements = await getEntitlements(key);
141
+ if (!key) {
142
+ if (json) {
143
+ console.log(JSON.stringify({ success: false, error: "No API key provided" }));
144
+ } else {
145
+ renderError("No API key provided");
146
+ }
147
+ return EXIT.USER_ERROR;
148
+ }
80
149
 
81
- if (
82
- !entitlements ||
83
- (entitlements.plan === "free" && !key.startsWith("gr_"))
84
- ) {
85
- // If mocking, we might accept anything, but let's pretend valid keys start with gr_
86
- // For now, since it's a mock, we just check if we got entitlements back.
87
- }
150
+ if (!isValidKeyFormat(key)) {
151
+ if (json) {
152
+ console.log(JSON.stringify({ success: false, error: "Invalid API key format" }));
153
+ } else {
154
+ renderError("Invalid API key format");
155
+ console.log(` ${ansi.dim}API keys should be at least 20 characters.${ansi.reset}`);
156
+ }
157
+ return EXIT.USER_ERROR;
158
+ }
159
+
160
+ const spinner = !quiet && !json ? new Spinner("Verifying API key").start() : null;
161
+
162
+ let entitlements;
163
+ try {
164
+ entitlements = await getEntitlements(key);
165
+ } catch (apiError) {
166
+ spinner?.fail(`Failed to verify: ${apiError.message}`);
167
+ if (json) {
168
+ console.log(JSON.stringify({ success: false, error: apiError.message }));
169
+ }
170
+ return EXIT.NETWORK_ERROR;
171
+ }
172
+
173
+ if (!entitlements) {
174
+ spinner?.fail("Invalid API key or server unreachable");
175
+ if (json) {
176
+ console.log(JSON.stringify({ success: false, error: "Invalid API key" }));
177
+ }
178
+ return EXIT.AUTH_FAILED;
179
+ }
88
180
 
89
- saveApiKey(key);
90
- console.log(
91
- ` ✅ Successfully logged in as ${entitlements?.user?.name || "User"}`,
92
- );
93
- console.log(` Plan: ${entitlements?.plan || "Free"}`);
181
+ saveApiKey(key);
182
+
183
+ const result = {
184
+ success: true,
185
+ user: entitlements?.user?.name || "User",
186
+ plan: entitlements?.plan || "free",
187
+ };
188
+
189
+ if (json) {
190
+ console.log(JSON.stringify(result));
191
+ } else if (!quiet) {
192
+ spinner?.succeed(`Logged in as ${ansi.bold}${result.user}${ansi.reset}`);
193
+ console.log(` ${ansi.dim}Plan:${ansi.reset} ${getTierBadge(result.plan)}\n`);
194
+ }
94
195
 
95
- return 0;
196
+ return EXIT.SUCCESS;
197
+ } catch (error) {
198
+ if (json) {
199
+ console.log(JSON.stringify({ success: false, error: error.message }));
200
+ } else {
201
+ renderError(`Login failed: ${error.message}`);
202
+ }
203
+ return EXIT.INTERNAL_ERROR;
204
+ }
96
205
  }
97
206
 
98
207
  async function runLogout(args) {
99
- console.log("\n 🔓 vibecheck LOGOUT\n");
100
- deleteApiKey();
101
- console.log(" ✅ API key removed from local config.");
102
- return 0;
208
+ const { flags } = parseGlobalFlags(args);
209
+ const quiet = shouldSuppressOutput(flags);
210
+ const json = isJsonMode(flags);
211
+
212
+ if (flags.help) {
213
+ console.log(`
214
+ ${ansi.bold}USAGE${ansi.reset}
215
+ ${ansi.cyan}vibecheck logout${ansi.reset}
216
+
217
+ ${ansi.dim}Aliases: signout${ansi.reset}
218
+
219
+ Remove stored API credentials from local config.
220
+
221
+ ${ansi.bold}OPTIONS${ansi.reset}
222
+ ${ansi.cyan}--json${ansi.reset} Output result as JSON
223
+ ${ansi.cyan}--quiet, -q${ansi.reset} Suppress non-essential output
224
+ ${ansi.cyan}--help, -h${ansi.reset} Show this help
225
+ `);
226
+ return EXIT.SUCCESS;
227
+ }
228
+
229
+ try {
230
+ if (!quiet && !json) {
231
+ renderMinimalHeader("logout", "free");
232
+ }
233
+
234
+ deleteApiKey();
235
+
236
+ if (json) {
237
+ console.log(JSON.stringify({ success: true, message: "Logged out" }));
238
+ } else if (!quiet) {
239
+ renderSuccess("API key removed from local config");
240
+ console.log();
241
+ }
242
+
243
+ return EXIT.SUCCESS;
244
+ } catch (error) {
245
+ if (json) {
246
+ console.log(JSON.stringify({ success: false, error: error.message }));
247
+ } else {
248
+ renderError(`Logout failed: ${error.message}`);
249
+ }
250
+ return EXIT.INTERNAL_ERROR;
251
+ }
103
252
  }
104
253
 
105
254
  async function runWhoami(args) {
106
- if (args.includes("--help") || args.includes("-h")) {
255
+ const { flags } = parseGlobalFlags(args);
256
+ const quiet = shouldSuppressOutput(flags);
257
+ const json = isJsonMode(flags);
258
+
259
+ if (flags.help) {
107
260
  console.log(`
108
- ${c.bold}vibecheck whoami${c.reset} - Show current user and plan
261
+ ${ansi.bold}USAGE${ansi.reset}
262
+ ${ansi.cyan}vibecheck whoami${ansi.reset}
109
263
 
110
- ${c.bold}Usage:${c.reset}
111
- vibecheck whoami
264
+ ${ansi.dim}Aliases: me, user${ansi.reset}
112
265
 
113
- ${c.bold}Description:${c.reset}
114
- Display information about the currently authenticated user,
115
- including plan, limits, and scopes.
266
+ Display information about the currently authenticated user,
267
+ including plan, limits, and available scopes.
116
268
 
117
- ${c.bold}Options:${c.reset}
118
- ${c.cyan}--help, -h${c.reset} Show this help
269
+ ${ansi.bold}OPTIONS${ansi.reset}
270
+ ${ansi.cyan}--json${ansi.reset} Output result as JSON
271
+ ${ansi.cyan}--quiet, -q${ansi.reset} Suppress non-essential output
272
+ ${ansi.cyan}--help, -h${ansi.reset} Show this help
119
273
 
120
- ${c.bold}Examples:${c.reset}
274
+ ${ansi.bold}EXAMPLES${ansi.reset}
275
+ ${ansi.dim}# Check current user${ansi.reset}
121
276
  vibecheck whoami
277
+
278
+ ${ansi.dim}# Get JSON for scripts${ansi.reset}
279
+ vibecheck whoami --json
122
280
  `);
123
- return 0;
281
+ return EXIT.SUCCESS;
124
282
  }
125
-
126
- console.log("\n 👤 vibecheck WHOAMI\n");
127
283
 
128
- const { key, source } = getApiKey();
284
+ try {
285
+ if (!quiet && !json) {
286
+ renderMinimalHeader("whoami", "free");
287
+ }
129
288
 
130
- if (!key) {
131
- console.log(" Not logged in.");
132
- console.log(' Run "vibecheck login" or set VIBECHECK_API_KEY.');
133
- return 1;
134
- }
289
+ const { key, source } = getApiKey();
135
290
 
136
- console.log(
137
- ` Source: ${source === "env" ? "Environment Variable" : "Local Config"}`,
138
- );
291
+ if (!key) {
292
+ const result = { authenticated: false, message: "Not logged in" };
293
+
294
+ if (json) {
295
+ console.log(JSON.stringify(result));
296
+ } else {
297
+ renderWarning("Not logged in");
298
+ console.log(` ${ansi.dim}Run "vibecheck login" or set VIBECHECK_API_KEY.${ansi.reset}\n`);
299
+ }
300
+ return EXIT.AUTH_REQUIRED;
301
+ }
139
302
 
140
- const entitlements = await getEntitlements(key);
141
- if (!entitlements) {
142
- console.log(" ⚠️ Invalid API Key or server unreachable.");
143
- return 1;
144
- }
303
+ const spinner = !quiet && !json ? new Spinner("Fetching user info").start() : null;
145
304
 
146
- console.log(` User: ${entitlements.user.name} (${entitlements.user.id})`);
147
- console.log(` Plan: ${entitlements.plan.toUpperCase()}`);
148
- console.log(` Limits: ${entitlements.limits.runsPerMonth} runs/month`);
149
- console.log("");
150
- console.log(" Scopes:");
151
- entitlements.scopes.forEach((s) => console.log(` - ${s}`));
152
- console.log("");
305
+ let entitlements;
306
+ try {
307
+ entitlements = await getEntitlements(key);
308
+ } catch (apiError) {
309
+ spinner?.fail(`Failed to fetch: ${apiError.message}`);
310
+ if (json) {
311
+ console.log(JSON.stringify({ authenticated: true, error: apiError.message }));
312
+ }
313
+ return EXIT.NETWORK_ERROR;
314
+ }
315
+
316
+ if (!entitlements) {
317
+ spinner?.fail("Invalid API key or server unreachable");
318
+ if (json) {
319
+ console.log(JSON.stringify({ authenticated: false, error: "Invalid API key" }));
320
+ } else {
321
+ console.log(` ${ansi.dim}Run "vibecheck login" to re-authenticate.${ansi.reset}\n`);
322
+ }
323
+ return EXIT.AUTH_FAILED;
324
+ }
153
325
 
154
- return 0;
326
+ spinner?.stop(null, null, null); // Clear spinner without message
327
+
328
+ const result = {
329
+ authenticated: true,
330
+ source: source === "env" ? "environment" : "config",
331
+ user: {
332
+ name: entitlements.user?.name,
333
+ id: entitlements.user?.id,
334
+ },
335
+ plan: entitlements.plan,
336
+ limits: entitlements.limits,
337
+ scopes: entitlements.scopes,
338
+ };
339
+
340
+ if (json) {
341
+ console.log(JSON.stringify(result, null, 2));
342
+ } else if (!quiet) {
343
+ renderSectionHeader("Account", sym.key);
344
+
345
+ renderKeyValue([
346
+ { label: "User", value: `${ansi.bold}${result.user.name}${ansi.reset} ${ansi.dim}(${result.user.id})${ansi.reset}` },
347
+ { label: "Plan", value: getTierBadge(result.plan) },
348
+ { label: "Source", value: source === "env" ? "Environment Variable" : "Local Config" },
349
+ { label: "Limits", value: `${result.limits?.runsPerMonth || "unlimited"} runs/month` },
350
+ ]);
351
+
352
+ if (result.scopes && result.scopes.length > 0) {
353
+ console.log();
354
+ console.log(` ${ansi.dim}Scopes:${ansi.reset}`);
355
+ result.scopes.forEach(s => console.log(` ${ansi.gray}${sym.bullet}${ansi.reset} ${s}`));
356
+ }
357
+
358
+ // Tier-specific upsell
359
+ if (result.plan === "free") {
360
+ renderFooter({
361
+ nextSteps: [
362
+ { cmd: "vibecheck scan", desc: "analyze your codebase" },
363
+ ],
364
+ showUpsell: true,
365
+ });
366
+ } else if (result.plan === "starter") {
367
+ console.log();
368
+ console.log(` ${ansi.dim}${sym.star}${ansi.reset} ${ansi.magenta}PRO${ansi.reset}${ansi.dim}: proof loops + badge generation + priority support${ansi.reset}`);
369
+ console.log(` ${ansi.dim} Upgrade ${sym.arrow} https://vibecheckai.dev${ansi.reset}\n`);
370
+ } else {
371
+ console.log();
372
+ }
373
+ }
374
+
375
+ return EXIT.SUCCESS;
376
+ } catch (error) {
377
+ if (json) {
378
+ console.log(JSON.stringify({ authenticated: false, error: error.message }));
379
+ } else {
380
+ renderError(`Whoami failed: ${error.message}`);
381
+ }
382
+ return EXIT.INTERNAL_ERROR;
383
+ }
155
384
  }
156
385
 
157
386
  module.exports = { runLogin, runLogout, runWhoami };
@@ -13,7 +13,8 @@
13
13
  const fs = require("fs");
14
14
  const path = require("path");
15
15
  const entitlements = require("./lib/entitlements-v2");
16
- const { parseGlobalFlags, shouldShowBanner } = require("./lib/global-flags");
16
+ const { parseGlobalFlags, shouldShowBanner, shouldSuppressOutput, isJsonMode } = require("./lib/global-flags");
17
+ const { EXIT } = require("./lib/exit-codes");
17
18
 
18
19
  // ═══════════════════════════════════════════════════════════════════════════════
19
20
  // TERMINAL STYLING
@@ -284,13 +285,25 @@ ${c.bold}TIER${c.reset}
284
285
 
285
286
  async function runCheckpoint(args) {
286
287
  const opts = parseArgs(args);
288
+ const quiet = shouldSuppressOutput(opts);
289
+ const json = isJsonMode(opts) || opts.json;
287
290
 
288
291
  if (opts.help) {
289
292
  printHelp(shouldShowBanner(opts));
290
- return 0;
293
+ return EXIT.SUCCESS;
291
294
  }
292
295
 
293
296
  const projectPath = path.resolve(opts.path);
297
+
298
+ // Validate project path exists
299
+ if (!fs.existsSync(projectPath)) {
300
+ if (json) {
301
+ console.log(JSON.stringify({ success: false, error: `Project path does not exist: ${projectPath}` }));
302
+ } else {
303
+ console.error(`${c.red}${icons.cross}${c.reset} Project path does not exist: ${projectPath}`);
304
+ }
305
+ return EXIT.NOT_FOUND;
306
+ }
294
307
  const vibecheckDir = path.join(projectPath, '.vibecheck');
295
308
  const resultsDir = path.join(vibecheckDir, 'results');
296
309
  const checkpointsDir = path.join(vibecheckDir, 'checkpoints');
@@ -316,17 +329,17 @@ async function runCheckpoint(args) {
316
329
  }
317
330
 
318
331
  if (!baseline) {
319
- console.log(`\n${c.yellow}${icons.warning}${c.reset} No baseline found.`);
320
- console.log(`${c.dim}Run 'vibecheck scan' first to create a baseline.${c.reset}`);
321
- console.log(`${c.dim}Or specify --baseline <file> to use a specific file.${c.reset}\n`);
322
-
323
- if (!opts.json) {
332
+ if (json) {
333
+ console.log(JSON.stringify({ success: false, error: "No baseline found" }));
334
+ } else if (!quiet) {
335
+ console.log(`\n${c.yellow}${icons.warning}${c.reset} No baseline found.`);
336
+ console.log(`${c.dim}Run 'vibecheck scan' first to create a baseline.${c.reset}`);
337
+ console.log(`${c.dim}Or specify --baseline <file> to use a specific file.${c.reset}\n`);
324
338
  console.log(`${c.bold}TIP:${c.reset} Create a baseline with:`);
325
339
  console.log(` ${c.cyan}vibecheck scan${c.reset}`);
326
340
  console.log(` ${c.cyan}cp .vibecheck/results/latest.json .vibecheck/results/baseline.json${c.reset}\n`);
327
341
  }
328
-
329
- return 1;
342
+ return EXIT.NOT_FOUND;
330
343
  }
331
344
 
332
345
  // Load current
@@ -340,9 +353,13 @@ async function runCheckpoint(args) {
340
353
  }
341
354
 
342
355
  if (!current) {
343
- console.log(`\n${c.yellow}${icons.warning}${c.reset} No current results found.`);
344
- console.log(`${c.dim}Run 'vibecheck scan' to generate current results.${c.reset}\n`);
345
- return 1;
356
+ if (json) {
357
+ console.log(JSON.stringify({ success: false, error: "No current results found" }));
358
+ } else if (!quiet) {
359
+ console.log(`\n${c.yellow}${icons.warning}${c.reset} No current results found.`);
360
+ console.log(`${c.dim}Run 'vibecheck scan' to generate current results.${c.reset}\n`);
361
+ }
362
+ return EXIT.NOT_FOUND;
346
363
  }
347
364
 
348
365
  // Extract findings arrays
@@ -408,18 +425,17 @@ async function runCheckpoint(args) {
408
425
  if (comparison.regressions.length > 3) {
409
426
  console.log(` ${c.dim}... and ${comparison.regressions.length - 3} more${c.reset}`);
410
427
  }
411
- return 1;
428
+ return EXIT.BLOCKING;
412
429
  } else {
413
430
  console.log(`${c.green}${icons.check}${c.reset} Checkpoint passed (${checkpoint.summary.fixed} fixed, ${checkpoint.summary.remaining} remaining)`);
414
- return 0;
431
+ return EXIT.SUCCESS;
415
432
  }
416
433
  }
417
434
 
418
435
  // JSON output
419
436
  if (opts.json) {
420
437
  console.log(JSON.stringify(checkpoint, null, 2));
421
- const exitCode = opts.gate && checkpoint.summary.regressions > 0 ? 1 : 0;
422
- return exitCode;
438
+ return opts.gate && checkpoint.summary.regressions > 0 ? EXIT.BLOCKING : EXIT.SUCCESS;
423
439
  }
424
440
 
425
441
  // Human-readable output
@@ -531,14 +547,16 @@ ${c.bold}╔══════════════════════
531
547
  console.log();
532
548
 
533
549
  // Return code:
534
- // - With --gate: 1 if regressions, 0 otherwise
535
- // - Without --gate: always 0 (informational only)
550
+ // - With --gate: BLOCKING (2) if regressions, SUCCESS otherwise
551
+ // - Without --gate: always SUCCESS (informational only)
536
552
  if (opts.gate && comparison.regressions.length > 0) {
537
- console.log(`${c.yellow}${icons.warning} Gate mode: Exiting with code 1 due to regressions${c.reset}\n`);
538
- return 1;
553
+ if (!quiet) {
554
+ console.log(`${c.yellow}${icons.warning} Gate mode: Exiting with code 2 due to regressions${c.reset}\n`);
555
+ }
556
+ return EXIT.BLOCKING;
539
557
  }
540
558
 
541
- return 0;
559
+ return EXIT.SUCCESS;
542
560
  }
543
561
 
544
562
  module.exports = { runCheckpoint };