@vibecheckai/cli 3.0.10 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/.generated ADDED
@@ -0,0 +1,25 @@
1
+ ================================================================================
2
+ ⚠️ GENERATED CODE - DO NOT EDIT
3
+ ================================================================================
4
+
5
+ This directory contains GENERATED build output.
6
+
7
+ All JavaScript files here are compiled from TypeScript sources.
8
+ Manual edits will be OVERWRITTEN on the next build.
9
+
10
+ SOURCE OF TRUTH:
11
+ packages/cli/src/ TypeScript CLI source
12
+ packages/cli/src/commands/ Command implementations
13
+ packages/cli/src/index.ts Main entry point
14
+
15
+ TO MAKE CHANGES:
16
+ 1. Edit the TypeScript source in packages/cli/src/
17
+ 2. Run: pnpm build
18
+ 3. Changes will be reflected here automatically
19
+
20
+ BUILD COMMAND:
21
+ pnpm --filter @vibecheck/cli build
22
+
23
+ ================================================================================
24
+ Generated: 2026-01-17
25
+ ================================================================================
@@ -0,0 +1,105 @@
1
+ /**
2
+ * Vibecheck CLI Command Registry
3
+ *
4
+ * Single source of truth for all CLI commands.
5
+ * Tiers match entitlements-v2.js EXACTLY.
6
+ *
7
+ * @module bin/registry
8
+ */
9
+
10
+ "use strict";
11
+
12
+ // ═══════════════════════════════════════════════════════════════════════════════
13
+ // COMMAND REGISTRY - Tiers match entitlements-v2.js EXACTLY
14
+ // ═══════════════════════════════════════════════════════════════════════════════
15
+ const COMMANDS = {
16
+ // PROOF LOOP
17
+ scan: { description: "Static analysis - routes, secrets, contracts", tier: "free", category: "proof", aliases: ["s", "check"], runner: () => require("./runners/runScan").runScan },
18
+ ship: { description: "Verdict engine - SHIP / WARN / BLOCK", tier: "free", category: "proof", aliases: ["verdict"], caps: "static-only on FREE", runner: () => require("./runners/runShip").runShip },
19
+ reality: { description: "Runtime proof - Playwright clicks every button", tier: "free", category: "proof", aliases: ["r", "test", "e2e"], caps: "preview mode on FREE (5 pages, no auth)", runner: () => { try { return require("./runners/runReality").runReality; } catch (e) { return async () => { console.error("Reality runner unavailable:", e.message); return 1; }; } } },
20
+ prove: { description: "Full proof loop - ctx → reality → ship → fix", tier: "pro", category: "proof", aliases: ["p", "full", "all"], runner: () => require("./runners/runProve").runProve },
21
+ fix: { description: "AI-powered auto-fix", tier: "free", category: "proof", caps: "--plan-only on FREE/PRO", aliases: ["f", "repair"], runner: () => require("./runners/runFix").runFix },
22
+ report: { description: "Generate HTML/MD/SARIF reports", tier: "free", category: "proof", caps: "HTML/MD only on FREE", aliases: ["html", "artifact"], runner: () => require("./runners/runReport").runReport },
23
+ replay: { description: "Record and replay user sessions", tier: "pro", category: "proof", aliases: ["record", "playback"], runner: () => async (args) => { try { const { runReplay } = require("./runners/runReplay"); return await runReplay(args); } catch (e) { if (args && (args.includes("--help") || args.includes("-h") || args.length === 0)) { console.log("\n vibecheck replay - Record and replay user sessions\n\n Subcommands:\n record <url> Record a user session\n play <capsule> Replay a recorded session\n list List available replay capsules\n show <id> Show details of a replay capsule\n delete <id> Delete a replay capsule\n export <id> Export a replay capsule\n import <file> Import a replay capsule\n\n Note: Requires 'chalk' and 'playwright' dependencies.\n Install: npm install chalk playwright\n"); return 0; } console.error("Replay runner error:", e.message); return 1; } } },
24
+ permissions: { description: "AuthZ matrix & IDOR detection", tier: "complete", category: "proof", aliases: ["authz", "idor"], runner: () => require("./runners/runPermissions").runPermissions },
25
+ graph: { description: "Reality proof graph visualization", tier: "complete", category: "proof", aliases: ["graphviz", "causal"], runner: () => require("./runners/runGraph").runGraph },
26
+
27
+ // SETUP & DX
28
+ install: { description: "Zero-friction onboarding", tier: "free", category: "setup", aliases: ["i", "bootstrap"], runner: () => require("./runners/runInstall").runInstall },
29
+ init: { description: "Project setup wizard", tier: "free", category: "setup", aliases: ["setup", "configure"], runner: () => require("./runners/runInit").runInit },
30
+ doctor: { description: "Environment diagnostics", tier: "free", category: "setup", aliases: ["health", "diag"], runner: () => require("./runners/runDoctor").runDoctor },
31
+ status: { description: "Project health dashboard", tier: "free", category: "setup", aliases: ["st"], runner: () => require("./runners/runStatus").runStatus },
32
+ watch: { description: "Continuous mode - re-runs on changes", tier: "free", category: "setup", aliases: ["w", "dev"], runner: () => require("./runners/runWatch").runWatch },
33
+ launch: { description: "Pre-launch checklist wizard", tier: "starter", category: "setup", aliases: ["checklist", "preflight"], runner: () => require("./runners/runLaunch").runLaunch },
34
+ preflight: { description: "Deployment validation checks", tier: "free", category: "setup", aliases: ["validate", "check"], runner: () => require("./runners/runPreflight").runPreflight },
35
+
36
+ // AI TRUTH
37
+ ctx: { description: "Generate truthpack for AI agents", tier: "free", category: "truth", aliases: ["truthpack", "tp"], subcommands: ["build", "diff", "guard", "sync", "search"], runner: () => require("./runners/runCtx").runCtx },
38
+ guard: { description: "Validate AI claims against truth", tier: "free", category: "truth", aliases: ["validate", "trust"], runner: () => require("./runners/runGuard").runGuard },
39
+ verify: { description: "Verify AI-generated code output", tier: "free", category: "truth", aliases: ["v", "check-output"], runner: () => require("./runners/runVerify").main },
40
+ context: { description: "Generate .cursorrules, .windsurf/rules", tier: "free", category: "truth", aliases: ["rules", "ai-rules"], runner: () => require("./runners/runContext").runContext },
41
+ mdc: { description: "Generate MDC specifications", tier: "free", category: "truth", aliases: [], runner: () => require("./runners/runMdc").runMdc },
42
+
43
+ // CI & COLLABORATION (STARTER+)
44
+ gate: { description: "CI/CD gate - blocks deploys on failures", tier: "starter", category: "ci", aliases: ["ci", "block"], runner: () => require("./runners/runGate").runGate },
45
+ pr: { description: "Generate PR comment with findings", tier: "starter", category: "ci", aliases: ["pull-request"], runner: () => require("./runners/runPR").runPR },
46
+ badge: { description: "Generate ship badge for README", tier: "starter", category: "ci", aliases: ["b"], runner: () => require("./runners/runBadge").runBadge },
47
+
48
+ // AUTOMATION (STARTER+/PRO)
49
+ mcp: { description: "Start MCP server for AI IDEs", tier: "starter", category: "automation", aliases: [], runner: () => require("./runners/runMcp").runMcp },
50
+ share: { description: "Generate share pack for PR/docs", tier: "pro", category: "automation", aliases: [], runner: () => require("./runners/runShare").runShare },
51
+ "ai-test": { description: "AI autonomous test generation", tier: "pro", category: "automation", aliases: ["ai", "agent"], runner: () => require("./runners/runAIAgent").runAIAgent },
52
+
53
+ // ACCOUNT (always free)
54
+ login: { description: "Authenticate with API key", tier: "free", category: "account", aliases: ["auth", "signin"], runner: () => require("./runners/runAuth").runLogin, skipAuth: true },
55
+ logout: { description: "Remove stored credentials", tier: "free", category: "account", aliases: ["signout"], runner: () => require("./runners/runAuth").runLogout, skipAuth: true },
56
+ whoami: { description: "Show current user and plan", tier: "free", category: "account", aliases: ["me", "user"], runner: () => require("./runners/runAuth").runWhoami, skipAuth: true },
57
+
58
+ // EXTRAS
59
+ labs: { description: "Experimental features", tier: "free", category: "extras", aliases: ["experimental", "beta"], runner: () => require("./runners/runLabs").runLabs },
60
+ };
61
+
62
+ // ═══════════════════════════════════════════════════════════════════════════════
63
+ // DERIVED DATA STRUCTURES
64
+ // ═══════════════════════════════════════════════════════════════════════════════
65
+ const ALIAS_MAP = {};
66
+ for (const [cmd, def] of Object.entries(COMMANDS)) {
67
+ for (const alias of def.aliases || []) ALIAS_MAP[alias] = cmd;
68
+ }
69
+
70
+ const ALL_COMMANDS = [...Object.keys(COMMANDS), ...Object.values(COMMANDS).flatMap(c => c.aliases || [])];
71
+
72
+ // ═══════════════════════════════════════════════════════════════════════════════
73
+ // RUNNER LOADER
74
+ // ═══════════════════════════════════════════════════════════════════════════════
75
+ /**
76
+ * Get runner function for a command
77
+ * @param {string} cmd - Command name
78
+ * @param {object} styles - Optional styling object with { red, reset, errorSymbol }
79
+ * @returns {Function|null} Runner function or null if not found
80
+ */
81
+ function getRunner(cmd, styles = {}) {
82
+ const def = COMMANDS[cmd];
83
+ if (!def) return null;
84
+ const red = styles.red || "";
85
+ const reset = styles.reset || "";
86
+ const errorSym = styles.errorSymbol || "✗";
87
+ try {
88
+ return def.runner();
89
+ } catch (e) {
90
+ return async () => {
91
+ console.error(`${red}${errorSym}${reset} Failed to load ${cmd}: ${e.message}`);
92
+ return 1;
93
+ };
94
+ }
95
+ }
96
+
97
+ // ═══════════════════════════════════════════════════════════════════════════════
98
+ // EXPORTS
99
+ // ═══════════════════════════════════════════════════════════════════════════════
100
+ module.exports = {
101
+ COMMANDS,
102
+ ALIAS_MAP,
103
+ ALL_COMMANDS,
104
+ getRunner,
105
+ };
@@ -0,0 +1,368 @@
1
+ /**
2
+ * CLI Output Utilities - World-Class Consistency
3
+ *
4
+ * Provides standardized JSON output, run ID generation,
5
+ * and artifact management for all CLI commands.
6
+ */
7
+
8
+ "use strict";
9
+
10
+ const fs = require("fs");
11
+ const path = require("path");
12
+ const crypto = require("crypto");
13
+
14
+ // ═══════════════════════════════════════════════════════════════════════════════
15
+ // RUN ID GENERATION
16
+ // ═══════════════════════════════════════════════════════════════════════════════
17
+
18
+ /**
19
+ * Generate a unique run ID for tracking
20
+ */
21
+ function generateRunId() {
22
+ return crypto.randomUUID();
23
+ }
24
+
25
+ // ═══════════════════════════════════════════════════════════════════════════════
26
+ // OUTPUT DIRECTORY MANAGEMENT
27
+ // ═══════════════════════════════════════════════════════════════════════════════
28
+
29
+ /**
30
+ * Get run directory paths
31
+ */
32
+ function getRunPaths(runId, projectPath = process.cwd()) {
33
+ const runDir = path.join(projectPath, ".vibecheck", "runs", runId);
34
+ const artifactsDir = path.join(runDir, "artifacts");
35
+
36
+ // Ensure directories exist
37
+ if (!fs.existsSync(runDir)) fs.mkdirSync(runDir, { recursive: true });
38
+ if (!fs.existsSync(artifactsDir)) fs.mkdirSync(artifactsDir, { recursive: true });
39
+
40
+ return {
41
+ runDir,
42
+ artifactsDir,
43
+ manifestPath: path.join(runDir, "manifest.json"),
44
+ commandPath: path.join(runDir, "command.json"),
45
+ };
46
+ }
47
+
48
+ /**
49
+ * Save artifact to run directory
50
+ */
51
+ function saveArtifact(runId, name, data, type = "json") {
52
+ const { artifactsDir } = getRunPaths(runId);
53
+ const artifactPath = path.join(artifactsDir, `${name}.${type}`);
54
+
55
+ if (type === "json") {
56
+ fs.writeFileSync(artifactPath, JSON.stringify(data, null, 2));
57
+ } else {
58
+ fs.writeFileSync(artifactPath, data);
59
+ }
60
+
61
+ return artifactPath;
62
+ }
63
+
64
+ // ═══════════════════════════════════════════════════════════════════════════════
65
+ // JSON OUTPUT SCHEMA
66
+ // ═══════════════════════════════════════════════════════════════════════════════
67
+
68
+ /**
69
+ * Create standardized JSON output
70
+ */
71
+ function createJsonOutput(options) {
72
+ const {
73
+ runId,
74
+ command,
75
+ startTime,
76
+ exitCode,
77
+ verdict,
78
+ result = {},
79
+ tier = "free",
80
+ version = "1.0.0",
81
+ projectPath = process.cwd(),
82
+ artifacts = [],
83
+ } = options;
84
+
85
+ const output = {
86
+ runId,
87
+ command,
88
+ timestamp: new Date().toISOString(),
89
+ duration: startTime ? Date.now() - new Date(startTime).getTime() : null,
90
+ exitCode,
91
+ verdict,
92
+ result,
93
+ meta: {
94
+ tier,
95
+ version,
96
+ path: projectPath,
97
+ },
98
+ artifacts: artifacts.map(art => ({
99
+ type: art.type,
100
+ path: path.relative(projectPath, art.path),
101
+ description: art.description,
102
+ })),
103
+ };
104
+
105
+ return output;
106
+ }
107
+
108
+ /**
109
+ * Write JSON output to file or stdout
110
+ */
111
+ function writeJsonOutput(output, outputPath) {
112
+ const jsonString = JSON.stringify(output, null, 2);
113
+
114
+ if (outputPath) {
115
+ fs.writeFileSync(outputPath, jsonString);
116
+ return outputPath;
117
+ } else {
118
+ console.log(jsonString);
119
+ return null;
120
+ }
121
+ }
122
+
123
+ // ═══════════════════════════════════════════════════════════════════════════════
124
+ // VERDCT MAPPING
125
+ // ═══════════════════════════════════════════════════════════════════════════════
126
+
127
+ /**
128
+ * Map exit codes to verdict strings
129
+ */
130
+ function exitCodeToVerdict(exitCode) {
131
+ switch (exitCode) {
132
+ case 0: return "SHIP";
133
+ case 1: return "WARN";
134
+ case 2: return "BLOCK";
135
+ case 3: return "FEATURE_DENIED";
136
+ case 4: return "INVALID_INPUT";
137
+ case 5: return "INTERNAL_ERROR";
138
+ default: return "UNKNOWN";
139
+ }
140
+ }
141
+
142
+ /**
143
+ * Map verdict to exit code
144
+ */
145
+ function verdictToExitCode(verdict) {
146
+ switch (verdict?.toUpperCase()) {
147
+ case "SHIP": return 0;
148
+ case "WARN": return 1;
149
+ case "BLOCK": return 2;
150
+ case "FEATURE_DENIED": return 3;
151
+ case "INVALID_INPUT": return 4;
152
+ case "INTERNAL_ERROR": return 5;
153
+ default: return 5;
154
+ }
155
+ }
156
+
157
+ // ═══════════════════════════════════════════════════════════════════════════════
158
+ // COMMAND WRAPPER
159
+ // ═══════════════════════════════════════════════════════════════════════════════
160
+
161
+ /**
162
+ * Wrap a command runner with standard output handling
163
+ */
164
+ async function withStandardOutput(commandFn, options = {}) {
165
+ const {
166
+ command,
167
+ startTime = new Date().toISOString(),
168
+ runId = generateRunId(),
169
+ json = false,
170
+ output = null,
171
+ ci = false,
172
+ verbose = false,
173
+ tier = "free",
174
+ version = "1.0.0",
175
+ } = options;
176
+
177
+ // Initialize paths
178
+ const paths = getRunPaths(runId);
179
+ const artifacts = [];
180
+
181
+ try {
182
+ // Run the command
183
+ const result = await commandFn({
184
+ runId,
185
+ paths,
186
+ saveArtifact: (name, data, type) => {
187
+ const artifactPath = saveArtifact(runId, name, data, type);
188
+ artifacts.push({
189
+ type,
190
+ path: artifactPath,
191
+ description: `${name} artifact`,
192
+ });
193
+ return artifactPath;
194
+ },
195
+ });
196
+
197
+ // Extract exit code and verdict
198
+ const exitCode = typeof result === "number" ? result : result.exitCode || 0;
199
+ const verdict = result.verdict || exitCodeToVerdict(exitCode);
200
+
201
+ // Create JSON output if requested
202
+ if (json) {
203
+ const jsonOutput = createJsonOutput({
204
+ runId,
205
+ command,
206
+ startTime,
207
+ exitCode,
208
+ verdict,
209
+ result: typeof result === "object" ? result : {},
210
+ tier,
211
+ version,
212
+ artifacts,
213
+ });
214
+
215
+ writeJsonOutput(jsonOutput, output);
216
+ }
217
+
218
+ // Save command output to run directory
219
+ const commandOutput = {
220
+ exitCode,
221
+ verdict,
222
+ result: typeof result === "object" ? result : {},
223
+ artifacts,
224
+ };
225
+ fs.writeFileSync(paths.commandPath, JSON.stringify(commandOutput, null, 2));
226
+
227
+ // Update stable pointer
228
+ const lastPath = path.join(process.cwd(), ".vibecheck", "last", `${command}.json`);
229
+ if (!fs.existsSync(path.dirname(lastPath))) {
230
+ fs.mkdirSync(path.dirname(lastPath), { recursive: true });
231
+ }
232
+ fs.writeFileSync(lastPath, JSON.stringify(commandOutput, null, 2));
233
+
234
+ return exitCode;
235
+ } catch (error) {
236
+ // Handle errors consistently
237
+ const exitCode = error.exitCode || 5;
238
+ const verdict = "INTERNAL_ERROR";
239
+
240
+ if (json) {
241
+ const jsonOutput = createJsonOutput({
242
+ runId,
243
+ command,
244
+ startTime,
245
+ exitCode,
246
+ verdict,
247
+ result: {
248
+ error: error.message,
249
+ stack: error.stack,
250
+ },
251
+ tier,
252
+ version,
253
+ artifacts,
254
+ });
255
+
256
+ writeJsonOutput(jsonOutput, output);
257
+ }
258
+
259
+ // Save error to run directory
260
+ const commandOutput = {
261
+ exitCode,
262
+ verdict,
263
+ error: error.message,
264
+ artifacts,
265
+ };
266
+ fs.writeFileSync(paths.commandPath, JSON.stringify(commandOutput, null, 2));
267
+
268
+ throw error;
269
+ }
270
+ }
271
+
272
+ // ═══════════════════════════════════════════════════════════════════════════════
273
+ // STANDARD FLAGS PARSER
274
+ // ═══════════════════════════════════════════════════════════════════════════════
275
+
276
+ /**
277
+ * Parse standard CLI flags
278
+ */
279
+ function parseStandardFlags(args) {
280
+ const flags = {
281
+ json: false,
282
+ ci: false,
283
+ path: process.cwd(),
284
+ output: null,
285
+ verbose: false,
286
+ strict: false,
287
+ help: false,
288
+ version: false,
289
+ debug: false,
290
+ quiet: false,
291
+ };
292
+
293
+ const parsed = args.filter(arg => {
294
+ switch (arg) {
295
+ case "--json":
296
+ case "-j":
297
+ flags.json = true;
298
+ return false;
299
+ case "--ci":
300
+ flags.ci = true;
301
+ return false;
302
+ case "--verbose":
303
+ case "-v":
304
+ flags.verbose = true;
305
+ return false;
306
+ case "--strict":
307
+ flags.strict = true;
308
+ return false;
309
+ case "--help":
310
+ case "-h":
311
+ flags.help = true;
312
+ return false;
313
+ case "--version":
314
+ case "-V":
315
+ flags.version = true;
316
+ return false;
317
+ case "--debug":
318
+ case "-d":
319
+ flags.debug = true;
320
+ return false;
321
+ case "--quiet":
322
+ case "-q":
323
+ flags.quiet = true;
324
+ return false;
325
+ default:
326
+ if (arg.startsWith("--path=")) {
327
+ flags.path = arg.split("=")[1];
328
+ return false;
329
+ } else if (arg.startsWith("-p")) {
330
+ const idx = args.indexOf(arg);
331
+ if (idx < args.length - 1) {
332
+ flags.path = args[idx + 1];
333
+ args.splice(idx, 2);
334
+ return false;
335
+ }
336
+ } else if (arg.startsWith("--output=")) {
337
+ flags.output = arg.split("=")[1];
338
+ return false;
339
+ } else if (arg.startsWith("-o")) {
340
+ const idx = args.indexOf(arg);
341
+ if (idx < args.length - 1) {
342
+ flags.output = args[idx + 1];
343
+ args.splice(idx, 2);
344
+ return false;
345
+ }
346
+ }
347
+ return true;
348
+ }
349
+ });
350
+
351
+ return { flags, parsed };
352
+ }
353
+
354
+ // ═══════════════════════════════════════════════════════════════════════════════
355
+ // EXPORTS
356
+ // ═══════════════════════════════════════════════════════════════════════════════
357
+
358
+ module.exports = {
359
+ generateRunId,
360
+ getRunPaths,
361
+ saveArtifact,
362
+ createJsonOutput,
363
+ writeJsonOutput,
364
+ exitCodeToVerdict,
365
+ verdictToExitCode,
366
+ withStandardOutput,
367
+ parseStandardFlags,
368
+ };
@@ -17,9 +17,8 @@
17
17
  *
18
18
  * Tiers:
19
19
  * - FREE ($0): Basic scanning and validation
20
- * - STARTER ($29/repo/mo): CI gates, launch, PR, badge, MCP
21
20
  * - PRO ($99/repo/mo): Full fix, prove, ai-test, share, advanced reality
22
- * - ENTERPRISE: Custom (placeholder only)
21
+ * - COMPLETE ($199/repo/mo): Everything including permissions, graph, advanced compliance
23
22
  */
24
23
 
25
24
  "use strict";
@@ -44,7 +43,7 @@ const TIERS = {
44
43
  free: { name: "FREE", price: 0, order: 0 },
45
44
  starter: { name: "STARTER", price: 29, order: 1 },
46
45
  pro: { name: "PRO", price: 99, order: 2 },
47
- enterprise: { name: "ENTERPRISE", price: 499, order: 3 },
46
+ complete: { name: "COMPLETE", price: 199, order: 3 },
48
47
  };
49
48
 
50
49
  // ═══════════════════════════════════════════════════════════════════════════════
@@ -56,13 +55,13 @@ const ENTITLEMENTS = {
56
55
  "scan": { minTier: "free" },
57
56
  "ship": { minTier: "free", caps: { free: "static-only" } },
58
57
  "ship.static": { minTier: "free" },
59
- "ship.full": { minTier: "starter" },
58
+ "ship.full": { minTier: "pro" },
60
59
 
61
60
  // Reality testing
62
61
  "reality": { minTier: "free", downgrade: "reality.preview" },
63
62
  "reality.preview": { minTier: "free", caps: { free: { maxPages: 5, maxClicks: 20, noAuthBoundary: true } } },
64
- "reality.full": { minTier: "starter" },
65
- "reality.advanced_auth_boundary": { minTier: "pro" },
63
+ "reality.full": { minTier: "pro" },
64
+ "reality.advanced_auth_boundary": { minTier: "complete" },
66
65
 
67
66
  // Prove command
68
67
  "prove": { minTier: "pro" },
@@ -70,13 +69,13 @@ const ENTITLEMENTS = {
70
69
  // Fix command
71
70
  "fix": { minTier: "free", downgrade: "fix.plan_only" },
72
71
  "fix.plan_only": { minTier: "free" },
73
- "fix.apply_patches": { minTier: "pro" },
72
+ "fix.apply_patches": { minTier: "complete" },
74
73
 
75
74
  // Report formats
76
75
  "report": { minTier: "free", downgrade: "report.html_md" },
77
76
  "report.html_md": { minTier: "free" },
78
- "report.sarif_csv": { minTier: "starter" },
79
- "report.compliance_packs": { minTier: "pro" },
77
+ "report.sarif_csv": { minTier: "pro" },
78
+ "report.compliance_packs": { minTier: "complete" },
80
79
 
81
80
  // Setup & DX
82
81
  "install": { minTier: "free" },
@@ -91,16 +90,22 @@ const ENTITLEMENTS = {
91
90
  "context": { minTier: "free" },
92
91
  "mdc": { minTier: "free" },
93
92
 
94
- // CI & Collaboration (STARTER+)
93
+ // PRO only
94
+ "replay": { minTier: "pro" },
95
+ "share": { minTier: "pro" },
96
+ "ai-test": { minTier: "pro" },
97
+
98
+ // STARTER and above
95
99
  "gate": { minTier: "starter" },
96
- "launch": { minTier: "starter" },
97
100
  "pr": { minTier: "starter" },
98
101
  "badge": { minTier: "starter" },
99
- "mcp": { minTier: "starter" },
102
+ "launch": { minTier: "starter" },
103
+ "mcp": { minTier: "starter", downgrade: "mcp.help_only" },
104
+ "mcp.help_only": { minTier: "free", caps: { free: "help and print-config only" } },
100
105
 
101
- // PRO only
102
- "share": { minTier: "pro" },
103
- "ai-test": { minTier: "pro" },
106
+ // COMPLETE only
107
+ "permissions": { minTier: "complete" },
108
+ "graph": { minTier: "complete" },
104
109
 
105
110
  // Account (always free)
106
111
  "login": { minTier: "free" },
@@ -124,31 +129,22 @@ const LIMITS = {
124
129
  scansPerMonth: 50,
125
130
  shipChecksPerMonth: 20,
126
131
  },
127
- starter: {
128
- realityMaxPages: 50,
129
- realityMaxClicks: 500,
130
- realityAuthBoundary: true,
131
- reportFormats: ["html", "md", "sarif", "csv"],
132
- fixApplyPatches: false,
133
- scansPerMonth: -1, // unlimited
134
- shipChecksPerMonth: -1,
135
- },
136
132
  pro: {
137
133
  realityMaxPages: -1, // unlimited
138
134
  realityMaxClicks: -1,
139
135
  realityAuthBoundary: true,
140
- realityAdvancedAuth: true,
141
- reportFormats: ["html", "md", "sarif", "csv", "compliance"],
142
- fixApplyPatches: true,
143
- scansPerMonth: -1,
136
+ realityAdvancedAuth: false,
137
+ reportFormats: ["html", "md", "sarif", "csv"],
138
+ fixApplyPatches: false,
139
+ scansPerMonth: -1, // unlimited
144
140
  shipChecksPerMonth: -1,
145
141
  },
146
- enterprise: {
142
+ complete: {
147
143
  realityMaxPages: -1,
148
144
  realityMaxClicks: -1,
149
145
  realityAuthBoundary: true,
150
146
  realityAdvancedAuth: true,
151
- reportFormats: ["html", "md", "sarif", "csv", "compliance", "custom"],
147
+ reportFormats: ["html", "md", "sarif", "csv", "compliance"],
152
148
  fixApplyPatches: true,
153
149
  scansPerMonth: -1,
154
150
  shipChecksPerMonth: -1,