@vibecheckai/cli 3.0.9 → 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 +25 -0
- package/bin/registry.js +105 -0
- package/bin/runners/lib/cli-output.js +368 -0
- package/bin/runners/lib/entitlements-v2.js +26 -30
- package/bin/runners/lib/receipts.js +179 -0
- package/bin/runners/lib/report-html.js +378 -1
- package/bin/runners/lib/upsell.js +510 -0
- package/bin/runners/lib/usage.js +153 -0
- package/bin/runners/runBadge.js +850 -116
- package/bin/runners/runCtx.js +602 -119
- package/bin/runners/runDoctor.js +400 -44
- package/bin/runners/runFix.js +557 -85
- package/bin/runners/runGraph.js +245 -74
- package/bin/runners/runInit.js +647 -88
- package/bin/runners/runInstall.js +207 -46
- package/bin/runners/runMcp.js +865 -42
- package/bin/runners/runPR.js +123 -32
- package/bin/runners/runPermissions.js +14 -0
- package/bin/runners/runPreflight.js +553 -0
- package/bin/runners/runProve.js +884 -104
- package/bin/runners/runReality.js +812 -92
- package/bin/runners/runReport.js +68 -2
- package/bin/runners/runShare.js +156 -38
- package/bin/runners/runShip.js +999 -889
- package/bin/runners/runVerify.js +272 -0
- package/bin/runners/runWatch.js +175 -55
- package/bin/vibecheck.js +108 -94
- package/mcp-server/package.json +1 -1
- package/package.json +1 -1
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
|
+
================================================================================
|
package/bin/registry.js
ADDED
|
@@ -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
|
-
* -
|
|
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
|
-
|
|
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: "
|
|
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: "
|
|
65
|
-
"reality.advanced_auth_boundary": { minTier: "
|
|
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: "
|
|
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: "
|
|
79
|
-
"report.compliance_packs": { minTier: "
|
|
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
|
-
//
|
|
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
|
-
"
|
|
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
|
-
//
|
|
102
|
-
"
|
|
103
|
-
"
|
|
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:
|
|
141
|
-
reportFormats: ["html", "md", "sarif", "csv"
|
|
142
|
-
fixApplyPatches:
|
|
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
|
-
|
|
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"
|
|
147
|
+
reportFormats: ["html", "md", "sarif", "csv", "compliance"],
|
|
152
148
|
fixApplyPatches: true,
|
|
153
149
|
scansPerMonth: -1,
|
|
154
150
|
shipChecksPerMonth: -1,
|