@vibecheckai/cli 3.0.4 → 3.0.7
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/dev/run-v2-torture.js +30 -0
- package/bin/runners/context/index.js +1 -1
- package/bin/runners/lib/analyzers.js +38 -0
- package/bin/runners/lib/assets/vibecheck-logo.png +0 -0
- package/bin/runners/lib/contracts/auth-contract.js +8 -0
- package/bin/runners/lib/contracts/env-contract.js +3 -0
- package/bin/runners/lib/contracts/external-contract.js +10 -2
- package/bin/runners/lib/contracts/route-contract.js +7 -0
- package/bin/runners/lib/contracts.js +804 -0
- package/bin/runners/lib/detectors-v2.js +703 -0
- package/bin/runners/lib/drift.js +425 -0
- package/bin/runners/lib/entitlements-v2.js +3 -1
- package/bin/runners/lib/entitlements.js +11 -3
- package/bin/runners/lib/env-resolver.js +417 -0
- package/bin/runners/lib/extractors/client-calls.js +990 -0
- package/bin/runners/lib/extractors/fastify-route-dump.js +573 -0
- package/bin/runners/lib/extractors/fastify-routes.js +426 -0
- package/bin/runners/lib/extractors/index.js +363 -0
- package/bin/runners/lib/extractors/next-routes.js +524 -0
- package/bin/runners/lib/extractors/proof-graph.js +431 -0
- package/bin/runners/lib/extractors/route-matcher.js +451 -0
- package/bin/runners/lib/extractors/truthpack-v2.js +377 -0
- package/bin/runners/lib/extractors/ui-bindings.js +547 -0
- package/bin/runners/lib/findings-schema.js +281 -0
- package/bin/runners/lib/html-report.js +650 -0
- package/bin/runners/lib/missions/templates.js +45 -0
- package/bin/runners/lib/policy.js +295 -0
- package/bin/runners/lib/reality/correlation-detectors.js +359 -0
- package/bin/runners/lib/reality/index.js +318 -0
- package/bin/runners/lib/reality/request-hashing.js +416 -0
- package/bin/runners/lib/reality/request-mapper.js +453 -0
- package/bin/runners/lib/reality/safety-rails.js +463 -0
- package/bin/runners/lib/reality/semantic-snapshot.js +408 -0
- package/bin/runners/lib/reality/toast-detector.js +393 -0
- package/bin/runners/lib/report-html.js +5 -0
- package/bin/runners/lib/report-templates.js +5 -0
- package/bin/runners/lib/report.js +135 -0
- package/bin/runners/lib/route-truth.js +10 -10
- package/bin/runners/lib/schema-validator.js +350 -0
- package/bin/runners/lib/schemas/contracts.schema.json +160 -0
- package/bin/runners/lib/schemas/finding.schema.json +100 -0
- package/bin/runners/lib/schemas/mission-pack.schema.json +206 -0
- package/bin/runners/lib/schemas/proof-graph.schema.json +176 -0
- package/bin/runners/lib/schemas/reality-report.schema.json +162 -0
- package/bin/runners/lib/schemas/share-pack.schema.json +180 -0
- package/bin/runners/lib/schemas/ship-report.schema.json +117 -0
- package/bin/runners/lib/schemas/truthpack-v2.schema.json +303 -0
- package/bin/runners/lib/schemas/validator.js +438 -0
- package/bin/runners/lib/ui.js +562 -0
- package/bin/runners/lib/verdict-engine.js +628 -0
- package/bin/runners/runAIAgent.js +228 -1
- package/bin/runners/runBadge.js +181 -1
- package/bin/runners/runCtx.js +7 -2
- package/bin/runners/runCtxDiff.js +301 -0
- package/bin/runners/runGuard.js +168 -0
- package/bin/runners/runInitGha.js +78 -15
- package/bin/runners/runLabs.js +341 -0
- package/bin/runners/runLaunch.js +180 -1
- package/bin/runners/runMdc.js +203 -1
- package/bin/runners/runProof.zip +0 -0
- package/bin/runners/runProve.js +23 -0
- package/bin/runners/runReplay.js +114 -84
- package/bin/runners/runScan.js +111 -32
- package/bin/runners/runShip.js +23 -2
- package/bin/runners/runTruthpack.js +9 -7
- package/bin/runners/runValidate.js +161 -1
- package/bin/vibecheck.js +416 -770
- package/mcp-server/.guardrail/audit/audit.log.jsonl +2 -0
- package/mcp-server/.specs/architecture.mdc +90 -0
- package/mcp-server/.specs/security.mdc +30 -0
- package/mcp-server/README.md +252 -0
- package/mcp-server/agent-checkpoint.js +364 -0
- package/mcp-server/architect-tools.js +707 -0
- package/mcp-server/audit-mcp.js +206 -0
- package/mcp-server/codebase-architect-tools.js +838 -0
- package/mcp-server/consolidated-tools.js +804 -0
- package/mcp-server/hygiene-tools.js +428 -0
- package/mcp-server/index-v1.js +698 -0
- package/mcp-server/index.js +2092 -0
- package/mcp-server/index.old.js +4137 -0
- package/mcp-server/intelligence-tools.js +664 -0
- package/mcp-server/intent-drift-tools.js +873 -0
- package/mcp-server/mdc-generator.js +298 -0
- package/mcp-server/package-lock.json +165 -0
- package/mcp-server/package.json +47 -0
- package/mcp-server/premium-tools.js +1275 -0
- package/mcp-server/test-mcp.js +108 -0
- package/mcp-server/test-tools.js +36 -0
- package/mcp-server/tier-auth.js +147 -0
- package/mcp-server/tools/index.js +72 -0
- package/mcp-server/tools-reorganized.ts +244 -0
- package/mcp-server/truth-context.js +581 -0
- package/mcp-server/truth-firewall-tools.js +1500 -0
- package/mcp-server/vibecheck-2.0-tools.js +748 -0
- package/mcp-server/vibecheck-tools.js +1075 -0
- package/package.json +10 -8
- package/bin/guardrail.js +0 -834
- package/bin/runners/runAudit.js +0 -2
- package/bin/runners/runAutopilot.js +0 -2
- package/bin/runners/runCertify.js +0 -2
- package/bin/runners/runDashboard.js +0 -10
- package/bin/runners/runEnhancedShip.js +0 -2
- package/bin/runners/runFixPacks.js +0 -2
- package/bin/runners/runNaturalLanguage.js +0 -3
- package/bin/runners/runProof.js +0 -2
- package/bin/runners/runRealitySniff.js +0 -2
- package/bin/runners/runUpgrade.js +0 -2
- package/bin/runners/runVerifyAgentOutput.js +0 -2
package/bin/vibecheck.js
CHANGED
|
@@ -1,17 +1,26 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// bin/vibecheck.js
|
|
2
|
+
// bin/vibecheck.js - World-Class CLI
|
|
3
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
4
|
+
// VibeCheck - Proves your app is real
|
|
5
|
+
// Ship with confidence. Catch fake features before your users do.
|
|
6
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
7
|
+
|
|
8
|
+
"use strict";
|
|
9
|
+
|
|
3
10
|
const readline = require("readline");
|
|
4
11
|
const path = require("path");
|
|
5
12
|
const fs = require("fs");
|
|
6
|
-
const
|
|
7
|
-
const {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
//
|
|
13
|
+
const os = require("os");
|
|
14
|
+
const { performance } = require("perf_hooks");
|
|
15
|
+
|
|
16
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
17
|
+
// PERFORMANCE: Track startup time
|
|
18
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
19
|
+
const STARTUP_TIME = performance.now();
|
|
20
|
+
|
|
21
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
22
|
+
// VERSION & METADATA
|
|
23
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
15
24
|
function getVersion() {
|
|
16
25
|
try {
|
|
17
26
|
const pkgPath = path.join(__dirname, "..", "package.json");
|
|
@@ -22,813 +31,450 @@ function getVersion() {
|
|
|
22
31
|
}
|
|
23
32
|
}
|
|
24
33
|
|
|
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
|
-
|
|
94
|
-
// Truth Pack v1 - Core truth system
|
|
95
|
-
let runTruthpack;
|
|
96
|
-
try {
|
|
97
|
-
runTruthpack = require("./runners/runTruthpack").runTruthpack;
|
|
98
|
-
} catch (e) {
|
|
99
|
-
runTruthpack = async () => { console.error("Truthpack runner unavailable:", e.message); return 1; };
|
|
100
|
-
}
|
|
101
|
-
|
|
102
34
|
const VERSION = getVersion();
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
const
|
|
35
|
+
const CLI_NAME = "vibecheck";
|
|
36
|
+
const CONFIG_FILE = ".vibecheckrc";
|
|
37
|
+
const CACHE_DIR = path.join(os.homedir(), ".vibecheck");
|
|
38
|
+
const STATE_FILE = path.join(CACHE_DIR, "state.json");
|
|
39
|
+
const UPDATE_CHECK_INTERVAL = 24 * 60 * 60 * 1000; // 24 hours
|
|
40
|
+
|
|
41
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
42
|
+
// ANSI STYLES - Premium terminal styling with gradient support
|
|
43
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
44
|
+
const SUPPORTS_COLOR = process.stdout.isTTY && !process.env.NO_COLOR;
|
|
45
|
+
const SUPPORTS_TRUECOLOR = SUPPORTS_COLOR && (
|
|
46
|
+
process.env.COLORTERM === "truecolor" ||
|
|
47
|
+
process.env.TERM_PROGRAM === "iTerm.app" ||
|
|
48
|
+
process.env.TERM_PROGRAM === "Apple_Terminal" ||
|
|
49
|
+
process.env.WT_SESSION // Windows Terminal
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
const c = SUPPORTS_COLOR ? {
|
|
106
53
|
reset: "\x1b[0m",
|
|
54
|
+
bold: "\x1b[1m",
|
|
107
55
|
dim: "\x1b[2m",
|
|
108
|
-
|
|
56
|
+
italic: "\x1b[3m",
|
|
57
|
+
underline: "\x1b[4m",
|
|
58
|
+
blink: "\x1b[5m",
|
|
59
|
+
inverse: "\x1b[7m",
|
|
60
|
+
hidden: "\x1b[8m",
|
|
61
|
+
strikethrough: "\x1b[9m",
|
|
62
|
+
black: "\x1b[30m",
|
|
63
|
+
red: "\x1b[31m",
|
|
109
64
|
green: "\x1b[32m",
|
|
110
65
|
yellow: "\x1b[33m",
|
|
111
|
-
red: "\x1b[31m",
|
|
112
66
|
blue: "\x1b[34m",
|
|
113
67
|
magenta: "\x1b[35m",
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
);
|
|
68
|
+
cyan: "\x1b[36m",
|
|
69
|
+
white: "\x1b[37m",
|
|
70
|
+
gray: "\x1b[90m",
|
|
71
|
+
brightRed: "\x1b[91m",
|
|
72
|
+
brightGreen: "\x1b[92m",
|
|
73
|
+
brightYellow: "\x1b[93m",
|
|
74
|
+
brightBlue: "\x1b[94m",
|
|
75
|
+
brightMagenta: "\x1b[95m",
|
|
76
|
+
brightCyan: "\x1b[96m",
|
|
77
|
+
brightWhite: "\x1b[97m",
|
|
78
|
+
bgRed: "\x1b[41m",
|
|
79
|
+
bgGreen: "\x1b[42m",
|
|
80
|
+
bgYellow: "\x1b[43m",
|
|
81
|
+
bgBlue: "\x1b[44m",
|
|
82
|
+
bgMagenta: "\x1b[45m",
|
|
83
|
+
bgCyan: "\x1b[46m",
|
|
84
|
+
bgWhite: "\x1b[47m",
|
|
85
|
+
bgGray: "\x1b[100m",
|
|
86
|
+
rgb: (r, g, b) => SUPPORTS_TRUECOLOR ? `\x1b[38;2;${r};${g};${b}m` : "",
|
|
87
|
+
bgRgb: (r, g, b) => SUPPORTS_TRUECOLOR ? `\x1b[48;2;${r};${g};${b}m` : "",
|
|
88
|
+
} : Object.fromEntries([
|
|
89
|
+
"reset", "bold", "dim", "italic", "underline", "blink", "inverse", "hidden", "strikethrough",
|
|
90
|
+
"black", "red", "green", "yellow", "blue", "magenta", "cyan", "white", "gray",
|
|
91
|
+
"brightRed", "brightGreen", "brightYellow", "brightBlue", "brightMagenta", "brightCyan", "brightWhite",
|
|
92
|
+
"bgRed", "bgGreen", "bgYellow", "bgBlue", "bgMagenta", "bgCyan", "bgWhite", "bgGray"
|
|
93
|
+
].map(k => [k, ""]));
|
|
94
|
+
|
|
95
|
+
// Gradient text generator (cyan → magenta → yellow)
|
|
96
|
+
function gradient(text, colors = [[0, 255, 255], [255, 0, 255], [255, 255, 0]]) {
|
|
97
|
+
if (!SUPPORTS_TRUECOLOR) return `${c.cyan}${text}${c.reset}`;
|
|
98
|
+
const chars = [...text];
|
|
99
|
+
const len = chars.length;
|
|
100
|
+
if (len === 0) return text;
|
|
101
|
+
return chars.map((char, i) => {
|
|
102
|
+
const t = i / Math.max(len - 1, 1);
|
|
103
|
+
const segmentLen = colors.length - 1;
|
|
104
|
+
const segment = Math.min(Math.floor(t * segmentLen), segmentLen - 1);
|
|
105
|
+
const localT = (t * segmentLen) - segment;
|
|
106
|
+
const c1 = colors[segment];
|
|
107
|
+
const c2 = colors[segment + 1] || c1;
|
|
108
|
+
const r = Math.round(c1[0] + (c2[0] - c1[0]) * localT);
|
|
109
|
+
const g = Math.round(c1[1] + (c2[1] - c1[1]) * localT);
|
|
110
|
+
const b = Math.round(c1[2] + (c2[2] - c1[2]) * localT);
|
|
111
|
+
return `${c.rgb(r, g, b)}${char}`;
|
|
112
|
+
}).join("") + c.reset;
|
|
133
113
|
}
|
|
134
114
|
|
|
135
|
-
//
|
|
136
|
-
//
|
|
137
|
-
//
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
"
|
|
142
|
-
"
|
|
143
|
-
"
|
|
144
|
-
"
|
|
145
|
-
"
|
|
146
|
-
"
|
|
147
|
-
"
|
|
148
|
-
"
|
|
149
|
-
"
|
|
150
|
-
"
|
|
151
|
-
"
|
|
152
|
-
"
|
|
153
|
-
"
|
|
154
|
-
"
|
|
155
|
-
|
|
156
|
-
"
|
|
157
|
-
"
|
|
158
|
-
"
|
|
159
|
-
"
|
|
160
|
-
"
|
|
161
|
-
"
|
|
162
|
-
"
|
|
163
|
-
"
|
|
164
|
-
"
|
|
165
|
-
"
|
|
166
|
-
"
|
|
167
|
-
"
|
|
168
|
-
"
|
|
169
|
-
"reality", // Runtime UI verification
|
|
170
|
-
];
|
|
171
|
-
|
|
172
|
-
// STARTER ($19/mo) - Requires API key with starter+ plan
|
|
173
|
-
const STARTER_COMMANDS = {
|
|
174
|
-
ship: "ship:audit", // Plain English audit
|
|
175
|
-
"enhanced-ship": "enhanced-ship:full", // Enhanced ship decision with all features
|
|
176
|
-
gate: "gate:ci", // CI/CD gate
|
|
177
|
-
reality: "reality:basic", // Browser testing
|
|
178
|
-
launch: "launch:checklist", // Pre-launch wizard
|
|
179
|
-
};
|
|
180
|
-
|
|
181
|
-
// PRO ($49/mo) - Requires API key with pro+ plan
|
|
182
|
-
const PRO_COMMANDS = {
|
|
183
|
-
"ai-test": "ai:agent", // AI Agent testing
|
|
184
|
-
ai: "ai:agent",
|
|
185
|
-
agent: "ai:agent",
|
|
186
|
-
// fix: removed - handled specially to allow --plan-only on FREE tier
|
|
187
|
-
autopilot: "autopilot:enable", // Continuous protection
|
|
188
|
-
};
|
|
189
|
-
|
|
190
|
-
// Commands with FREE tier read-only modes
|
|
191
|
-
const TIERED_COMMANDS = {
|
|
192
|
-
fix: {
|
|
193
|
-
freeArgs: ["--plan-only", "--help", "-h"], // Allow these args on FREE
|
|
194
|
-
requiredScope: "fix:apply",
|
|
195
|
-
tier: "pro",
|
|
196
|
-
},
|
|
197
|
-
};
|
|
198
|
-
|
|
199
|
-
// Special: proof command has sub-modes with different tiers
|
|
200
|
-
const PROOF_COMMANDS = {
|
|
201
|
-
mocks: "proof:mocks", // Starter+
|
|
202
|
-
reality: "proof:reality", // Pro+
|
|
203
|
-
};
|
|
204
|
-
|
|
205
|
-
// Commands that always work (utilities)
|
|
206
|
-
const UTILITY_COMMANDS = ["mcp", "rules", "api", "deps", "sbom", "fixpacks"];
|
|
207
|
-
|
|
208
|
-
// Compliance tier commands
|
|
209
|
-
const COMPLIANCE_COMMANDS = {
|
|
210
|
-
audit: "audit:full", // Full audit trail
|
|
115
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
116
|
+
// UNICODE SYMBOLS - Rich visual indicators
|
|
117
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
118
|
+
const SUPPORTS_UNICODE = process.platform !== "win32" || process.env.WT_SESSION || process.env.TERM_PROGRAM;
|
|
119
|
+
|
|
120
|
+
const sym = SUPPORTS_UNICODE ? {
|
|
121
|
+
success: "✓", error: "✗", warning: "⚠", info: "ℹ", pending: "○", active: "●",
|
|
122
|
+
spinner: ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"],
|
|
123
|
+
progress: ["░", "▒", "▓", "█"],
|
|
124
|
+
arrow: "→", arrowRight: "▸", arrowDown: "▾", bullet: "•",
|
|
125
|
+
star: "★", starEmpty: "☆", heart: "♥", shield: "🛡️", rocket: "🚀",
|
|
126
|
+
fire: "🔥", sparkles: "✨", check: "☑", box: "☐", lock: "🔒",
|
|
127
|
+
key: "🔑", lightning: "⚡", clock: "⏱", folder: "📁", file: "📄",
|
|
128
|
+
gear: "⚙", chart: "📊",
|
|
129
|
+
boxTopLeft: "╭", boxTopRight: "╮", boxBottomLeft: "╰", boxBottomRight: "╯",
|
|
130
|
+
boxHorizontal: "─", boxVertical: "│", boxCross: "┼",
|
|
131
|
+
boxHorizontalDown: "┬", boxHorizontalUp: "┴",
|
|
132
|
+
boxVerticalRight: "├", boxVerticalLeft: "┤",
|
|
133
|
+
dblBoxTopLeft: "╔", dblBoxTopRight: "╗", dblBoxBottomLeft: "╚", dblBoxBottomRight: "╝",
|
|
134
|
+
dblBoxHorizontal: "═", dblBoxVertical: "║",
|
|
135
|
+
} : {
|
|
136
|
+
success: "√", error: "×", warning: "!", info: "i", pending: "o", active: "*",
|
|
137
|
+
spinner: ["-", "\\", "|", "/"], progress: [".", ":", "#", "#"],
|
|
138
|
+
arrow: "->", arrowRight: ">", arrowDown: "v", bullet: "*",
|
|
139
|
+
star: "*", starEmpty: "o", heart: "<3", shield: "[#]", rocket: ">>",
|
|
140
|
+
fire: "(!)", sparkles: "*", check: "[x]", box: "[ ]", lock: "[L]",
|
|
141
|
+
key: "[K]", lightning: "!", clock: "[T]", folder: "[D]", file: "[F]",
|
|
142
|
+
gear: "[G]", chart: "[C]",
|
|
143
|
+
boxTopLeft: "+", boxTopRight: "+", boxBottomLeft: "+", boxBottomRight: "+",
|
|
144
|
+
boxHorizontal: "-", boxVertical: "|", boxCross: "+",
|
|
145
|
+
boxHorizontalDown: "+", boxHorizontalUp: "+",
|
|
146
|
+
boxVerticalRight: "+", boxVerticalLeft: "+",
|
|
147
|
+
dblBoxTopLeft: "+", dblBoxTopRight: "+", dblBoxBottomLeft: "+", dblBoxBottomRight: "+",
|
|
148
|
+
dblBoxHorizontal: "=", dblBoxVertical: "||",
|
|
211
149
|
};
|
|
212
150
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
});
|
|
223
|
-
});
|
|
151
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
152
|
+
// SPINNER & PROGRESS
|
|
153
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
154
|
+
function isCI() {
|
|
155
|
+
return !!(process.env.CI || process.env.CONTINUOUS_INTEGRATION || process.env.RAILWAY_ENVIRONMENT ||
|
|
156
|
+
process.env.VERCEL || process.env.NETLIFY || process.env.GITHUB_ACTIONS || process.env.GITLAB_CI ||
|
|
157
|
+
process.env.CIRCLECI || process.env.TRAVIS || process.env.BUILDKITE || process.env.RENDER ||
|
|
158
|
+
process.env.HEROKU || process.env.CODEBUILD_BUILD_ID || process.env.JENKINS_URL ||
|
|
159
|
+
process.env.TEAMCITY_VERSION || process.env.TF_BUILD || !process.stdin.isTTY);
|
|
224
160
|
}
|
|
225
161
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
162
|
+
class Spinner {
|
|
163
|
+
constructor(text = "") {
|
|
164
|
+
this.text = text; this.frame = 0; this.interval = null;
|
|
165
|
+
this.stream = process.stderr; this.isCI = isCI();
|
|
166
|
+
}
|
|
167
|
+
start(text) {
|
|
168
|
+
if (text) this.text = text;
|
|
169
|
+
if (this.isCI) { this.stream.write(`${c.dim}${sym.pending}${c.reset} ${this.text}\n`); return this; }
|
|
170
|
+
this.interval = setInterval(() => {
|
|
171
|
+
const spinner = sym.spinner[this.frame % sym.spinner.length];
|
|
172
|
+
this.stream.write(`\r${c.cyan}${spinner}${c.reset} ${this.text}`);
|
|
173
|
+
this.frame++;
|
|
174
|
+
}, 80);
|
|
175
|
+
return this;
|
|
176
|
+
}
|
|
177
|
+
update(text) { this.text = text; if (this.isCI) this.stream.write(` ${c.dim}${sym.arrowRight}${c.reset} ${text}\n`); return this; }
|
|
178
|
+
succeed(text) { this.stop(); this.stream.write(`\r${c.green}${sym.success}${c.reset} ${text || this.text}\n`); return this; }
|
|
179
|
+
fail(text) { this.stop(); this.stream.write(`\r${c.red}${sym.error}${c.reset} ${text || this.text}\n`); return this; }
|
|
180
|
+
warn(text) { this.stop(); this.stream.write(`\r${c.yellow}${sym.warning}${c.reset} ${text || this.text}\n`); return this; }
|
|
181
|
+
info(text) { this.stop(); this.stream.write(`\r${c.blue}${sym.info}${c.reset} ${text || this.text}\n`); return this; }
|
|
182
|
+
stop() { if (this.interval) { clearInterval(this.interval); this.interval = null; this.stream.write("\r\x1b[K"); } return this; }
|
|
183
|
+
}
|
|
233
184
|
|
|
234
|
-
|
|
185
|
+
function stripAnsi(str) { return str.replace(/\x1B\[[0-9;]*[a-zA-Z]/g, ""); }
|
|
235
186
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
if (!key) {
|
|
239
|
-
// In CI/CD environments, skip interactive prompts
|
|
240
|
-
if (isCI()) {
|
|
241
|
-
console.log(`${c.yellow}⚠ No API key found${c.reset}`);
|
|
242
|
-
console.log(
|
|
243
|
-
`${c.dim}Running in CI mode with FREE tier features.${c.reset}`,
|
|
244
|
-
);
|
|
245
|
-
console.log(
|
|
246
|
-
`${c.dim}Set VIBECHECK_API_KEY env var to unlock more features.${c.reset}\n`,
|
|
247
|
-
);
|
|
248
|
-
return { key: null, entitlements: null };
|
|
249
|
-
}
|
|
187
|
+
function ensureCacheDir() { if (!fs.existsSync(CACHE_DIR)) fs.mkdirSync(CACHE_DIR, { recursive: true }); }
|
|
250
188
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
189
|
+
function loadState() {
|
|
190
|
+
try { if (fs.existsSync(STATE_FILE)) return JSON.parse(fs.readFileSync(STATE_FILE, "utf-8")); } catch {}
|
|
191
|
+
return { firstRun: Date.now(), lastRun: null, runCount: 0, lastUpdateCheck: null, latestVersion: null, commandHistory: [], favorites: [] };
|
|
192
|
+
}
|
|
254
193
|
|
|
255
|
-
|
|
256
|
-
${c.cyan}STARTER${c.reset} ship, gate, reality, launch, proof mocks ${c.dim}($29/mo)${c.reset}
|
|
257
|
-
${c.magenta}PRO${c.reset} ai-test, fix, autopilot, proof reality ${c.dim}($99/mo)${c.reset}
|
|
194
|
+
function saveState(state) { try { ensureCacheDir(); fs.writeFileSync(STATE_FILE, JSON.stringify(state, null, 2)); } catch {} }
|
|
258
195
|
|
|
259
|
-
|
|
260
|
-
|
|
196
|
+
function loadConfig() {
|
|
197
|
+
const config = { debug: false, verbose: false, quiet: false, color: true, analytics: true, updateCheck: true, timeout: 30000, maxRetries: 3 };
|
|
198
|
+
const projectConfigPath = path.join(process.cwd(), CONFIG_FILE);
|
|
199
|
+
if (fs.existsSync(projectConfigPath)) { try { Object.assign(config, JSON.parse(fs.readFileSync(projectConfigPath, "utf-8"))); } catch {} }
|
|
200
|
+
if (process.env.VIBECHECK_DEBUG === "true") config.debug = true;
|
|
201
|
+
if (process.env.VIBECHECK_VERBOSE === "true") config.verbose = true;
|
|
202
|
+
if (process.env.NO_COLOR || process.env.VIBECHECK_NO_COLOR) config.color = false;
|
|
203
|
+
return config;
|
|
204
|
+
}
|
|
261
205
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
if (entitlements) {
|
|
274
|
-
saveApiKey(apiKey);
|
|
275
|
-
console.log(
|
|
276
|
-
`\n${c.green}✓${c.reset} Logged in as ${entitlements.user?.name || "User"}`,
|
|
277
|
-
);
|
|
278
|
-
console.log(
|
|
279
|
-
`${c.dim}Plan: ${entitlements.plan?.toUpperCase() || "FREE"}${c.reset}\n`,
|
|
280
|
-
);
|
|
281
|
-
return { key: apiKey, entitlements };
|
|
282
|
-
} else {
|
|
283
|
-
console.log(`\n${c.red}✗${c.reset} Invalid API key\n`);
|
|
284
|
-
}
|
|
285
|
-
}
|
|
206
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
207
|
+
// FUZZY MATCHING
|
|
208
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
209
|
+
function levenshtein(a, b) {
|
|
210
|
+
const matrix = [];
|
|
211
|
+
for (let i = 0; i <= b.length; i++) matrix[i] = [i];
|
|
212
|
+
for (let j = 0; j <= a.length; j++) matrix[0][j] = j;
|
|
213
|
+
for (let i = 1; i <= b.length; i++) {
|
|
214
|
+
for (let j = 1; j <= a.length; j++) {
|
|
215
|
+
if (b.charAt(i - 1) === a.charAt(j - 1)) matrix[i][j] = matrix[i - 1][j - 1];
|
|
216
|
+
else matrix[i][j] = Math.min(matrix[i - 1][j - 1] + 1, matrix[i][j - 1] + 1, matrix[i - 1][j] + 1);
|
|
286
217
|
}
|
|
287
|
-
|
|
288
|
-
console.log(`
|
|
289
|
-
${c.dim}Continuing in FREE mode. Some features will be limited.${c.reset}
|
|
290
|
-
${c.dim}Run ${c.cyan}vibecheck login${c.dim} anytime to upgrade.${c.reset}
|
|
291
|
-
`);
|
|
292
|
-
return { key: null, entitlements: null };
|
|
293
218
|
}
|
|
294
|
-
|
|
295
|
-
// User has API key, get entitlements
|
|
296
|
-
const entitlements = await getEntitlements(key);
|
|
297
|
-
return { key, entitlements };
|
|
219
|
+
return matrix[b.length][a.length];
|
|
298
220
|
}
|
|
299
221
|
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
222
|
+
function findSimilarCommands(input, commands, maxDistance = 3) {
|
|
223
|
+
const matches = [];
|
|
224
|
+
for (const cmd of commands) {
|
|
225
|
+
const distance = levenshtein(input.toLowerCase(), cmd.toLowerCase());
|
|
226
|
+
if (distance <= maxDistance) matches.push({ cmd, distance });
|
|
227
|
+
if (cmd.toLowerCase().startsWith(input.toLowerCase()) && input.length >= 2) matches.push({ cmd, distance: 0.5 });
|
|
304
228
|
}
|
|
229
|
+
return matches.sort((a, b) => a.distance - b.distance).map(m => m.cmd).slice(0, 3);
|
|
230
|
+
}
|
|
305
231
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
232
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
233
|
+
// COMMAND REGISTRY
|
|
234
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
235
|
+
const COMMANDS = {
|
|
236
|
+
scan: { description: "Static truth - routes, contracts, secrets, coverage", tier: "free", category: "proof", aliases: ["s", "check"], runner: () => require("./runners/runScan").runScan },
|
|
237
|
+
ship: { description: "Verdict engine - SHIP / WARN / BLOCK decision", tier: "free", category: "proof", aliases: ["verdict"], runner: () => require("./runners/runShip").runShip },
|
|
238
|
+
reality: { description: "Runtime proof - Playwright clicks every button", tier: "free", category: "proof", aliases: ["r", "test", "e2e"], runner: () => { try { return require("./runners/runReality").runReality; } catch (e) { return async () => { console.error("Reality runner unavailable:", e.message); return 1; }; } } },
|
|
239
|
+
fix: { description: "Mission-based repair - targeted fixes with proof", tier: "pro", category: "proof", freeArgs: ["--plan-only", "--help", "-h"], aliases: ["f", "repair"], runner: () => require("./runners/runFix").runFix },
|
|
240
|
+
prove: { description: "One command - runs the full loop, fixes issues, proves SHIP", tier: "free", category: "proof", aliases: ["p", "full", "all"], runner: () => require("./runners/runProve").runProve },
|
|
241
|
+
report: { description: "HTML artifact - shareable proof of what shipped", tier: "free", category: "proof", aliases: ["html", "artifact"], runner: () => require("./runners/runReport").runReport },
|
|
242
|
+
init: { description: "Set up vibecheck (--gha for GitHub Actions)", tier: "free", category: "setup", aliases: ["setup", "configure"], runner: () => require("./runners/runInit").runInit },
|
|
243
|
+
install: { description: "Zero-friction onboarding - auto-detects everything", tier: "free", category: "setup", aliases: ["i", "bootstrap"], runner: () => require("./runners/runInstall").runInstall },
|
|
244
|
+
doctor: { description: "Environment + project diagnostics", tier: "free", category: "setup", aliases: ["health", "diag"], runner: () => require("./runners/runDoctor").runDoctor },
|
|
245
|
+
watch: { description: "Continuous mode - re-runs on file changes", tier: "free", category: "setup", aliases: ["w", "dev"], runner: () => require("./runners/runWatch").runWatch },
|
|
246
|
+
ctx: { description: "Generate truthpack - ground truth for AI agents", tier: "free", category: "truth", aliases: ["truthpack", "tp"], subcommands: ["build", "diff", "guard", "sync", "search"], runner: () => require("./runners/runCtx").runCtx },
|
|
247
|
+
guard: { description: "Trust boundaries - validates AI claims + prompt injection", tier: "free", category: "truth", aliases: ["validate", "trust"], runner: () => require("./runners/runGuard").runGuard },
|
|
248
|
+
context: { description: "Generate AI rules (.cursorrules, .windsurf/rules, etc.)", tier: "free", category: "truth", aliases: ["rules", "ai-rules"], runner: () => require("./runners/runContext").runContext },
|
|
249
|
+
mcp: { description: "Start MCP server for AI coding agents", tier: "free", category: "extras", aliases: [], runner: () => require("./runners/runMcp").runMcp },
|
|
250
|
+
badge: { description: "Generate ship badge for README/PR", tier: "free", category: "extras", aliases: ["b"], runner: () => require("./runners/runBadge").runBadge },
|
|
251
|
+
pr: { description: "Generate PR comment with findings", tier: "free", category: "extras", aliases: ["pull-request"], runner: () => require("./runners/runPR").runPR },
|
|
252
|
+
labs: { description: "Experimental features", tier: "free", category: "extras", aliases: ["experimental", "beta"], runner: () => require("./runners/runLabs").runLabs },
|
|
253
|
+
gate: { description: "CI/CD gate - blocks deploys on failures", tier: "starter", category: "ci", aliases: ["ci", "block"], scope: "gate:ci", runner: () => require("./runners/runGate").runGate },
|
|
254
|
+
"ai-test": { description: "AI Agent testing - autonomous test generation", tier: "pro", category: "automation", aliases: ["ai", "agent"], scope: "ai:agent", runner: () => require("./runners/runAIAgent").runAIAgent },
|
|
255
|
+
login: { description: "Authenticate with API key", tier: "free", category: "account", aliases: ["auth", "signin"], runner: () => require("./runners/runAuth").runLogin, skipAuth: true },
|
|
256
|
+
logout: { description: "Remove stored credentials", tier: "free", category: "account", aliases: ["signout"], runner: () => require("./runners/runAuth").runLogout, skipAuth: true },
|
|
257
|
+
whoami: { description: "Show current user and plan", tier: "free", category: "account", aliases: ["me", "user"], runner: () => require("./runners/runAuth").runWhoami, skipAuth: true },
|
|
258
|
+
mdc: { description: "Generate MDC specifications", tier: "free", category: "truth", aliases: [], runner: () => require("./runners/runMdc").runMdc },
|
|
259
|
+
status: { description: "Project status dashboard", tier: "free", category: "setup", aliases: ["st"], runner: () => require("./runners/runStatus").runStatus },
|
|
260
|
+
share: { description: "Generate share pack for PR/docs", tier: "free", category: "extras", aliases: [], runner: () => require("./runners/runShare").runShare },
|
|
261
|
+
};
|
|
310
262
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
// Check if using a FREE tier argument
|
|
315
|
-
const hasFreeArg = args.some(arg => config.freeArgs.includes(arg));
|
|
316
|
-
// Also allow if no fix pack specified (shows help)
|
|
317
|
-
const hasNoFixPack = cmd === "fix" && !args.some(arg => !arg.startsWith("-"));
|
|
318
|
-
|
|
319
|
-
if (hasFreeArg || hasNoFixPack) {
|
|
320
|
-
return { allowed: true, tier: "free" };
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
// Requires paid tier
|
|
324
|
-
if (!entitlements) {
|
|
325
|
-
return {
|
|
326
|
-
allowed: false,
|
|
327
|
-
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}`,
|
|
328
|
-
};
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
if (
|
|
332
|
-
entitlements.scopes?.includes(config.requiredScope) ||
|
|
333
|
-
entitlements.scopes?.includes("*")
|
|
334
|
-
) {
|
|
335
|
-
return { allowed: true, tier: config.tier };
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
return {
|
|
339
|
-
allowed: false,
|
|
340
|
-
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}`,
|
|
341
|
-
};
|
|
342
|
-
}
|
|
263
|
+
const ALIAS_MAP = {};
|
|
264
|
+
for (const [cmd, def] of Object.entries(COMMANDS)) { for (const alias of def.aliases || []) ALIAS_MAP[alias] = cmd; }
|
|
265
|
+
const ALL_COMMANDS = [...Object.keys(COMMANDS), ...Object.values(COMMANDS).flatMap(c => c.aliases || [])];
|
|
343
266
|
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
if (!entitlements) {
|
|
350
|
-
return {
|
|
351
|
-
allowed: false,
|
|
352
|
-
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}`,
|
|
353
|
-
};
|
|
354
|
-
}
|
|
355
|
-
if (
|
|
356
|
-
entitlements.scopes?.includes("proof:mocks") ||
|
|
357
|
-
entitlements.scopes?.includes("*")
|
|
358
|
-
) {
|
|
359
|
-
return { allowed: true, tier: "starter" };
|
|
360
|
-
}
|
|
361
|
-
return {
|
|
362
|
-
allowed: false,
|
|
363
|
-
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}`,
|
|
364
|
-
};
|
|
365
|
-
} else if (subMode === "reality") {
|
|
366
|
-
// Pro+ required
|
|
367
|
-
if (!entitlements) {
|
|
368
|
-
return {
|
|
369
|
-
allowed: false,
|
|
370
|
-
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}`,
|
|
371
|
-
};
|
|
372
|
-
}
|
|
373
|
-
if (
|
|
374
|
-
entitlements.scopes?.includes("proof:reality") ||
|
|
375
|
-
entitlements.scopes?.includes("*")
|
|
376
|
-
) {
|
|
377
|
-
return { allowed: true, tier: "pro" };
|
|
378
|
-
}
|
|
379
|
-
return {
|
|
380
|
-
allowed: false,
|
|
381
|
-
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}`,
|
|
382
|
-
};
|
|
383
|
-
}
|
|
384
|
-
// No submode - show help
|
|
385
|
-
return { allowed: true };
|
|
386
|
-
}
|
|
267
|
+
function getRunner(cmd) {
|
|
268
|
+
const def = COMMANDS[cmd];
|
|
269
|
+
if (!def) return null;
|
|
270
|
+
try { return def.runner(); } catch (e) { return async () => { console.error(`${c.red}${sym.error}${c.reset} Failed to load ${cmd}: ${e.message}`); return 1; }; }
|
|
271
|
+
}
|
|
387
272
|
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
273
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
274
|
+
// AUTH & ACCESS CONTROL
|
|
275
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
276
|
+
let authModule = null;
|
|
277
|
+
function getAuthModule() { if (!authModule) authModule = require("./runners/lib/auth"); return authModule; }
|
|
278
|
+
|
|
279
|
+
async function checkCommandAccess(cmd, args, entitlements) {
|
|
280
|
+
const def = COMMANDS[cmd];
|
|
281
|
+
if (!def) return { allowed: true };
|
|
282
|
+
if (def.tier === "free") return { allowed: true, tier: "free" };
|
|
283
|
+
if (!entitlements) return { allowed: false, tier: def.tier, reason: formatAccessDenied(cmd, def.tier, null) };
|
|
284
|
+
const hasAccess = entitlements.scopes?.includes(def.scope) || entitlements.scopes?.includes("*");
|
|
285
|
+
if (!hasAccess) return { allowed: false, tier: def.tier, reason: formatAccessDenied(cmd, def.tier, entitlements.plan) };
|
|
286
|
+
return { allowed: true, tier: def.tier };
|
|
287
|
+
}
|
|
391
288
|
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
289
|
+
function formatAccessDenied(cmd, requiredTier, currentPlan) {
|
|
290
|
+
const tierColors = { starter: c.cyan, pro: c.magenta, enterprise: c.yellow };
|
|
291
|
+
const tierColor = tierColors[requiredTier] || c.white;
|
|
292
|
+
let msg = `${c.yellow}${cmd}${c.reset} requires a ${tierColor}${requiredTier.toUpperCase()}${c.reset} plan.\n\n`;
|
|
293
|
+
if (!currentPlan) { msg += ` Run ${c.cyan}vibecheck login${c.reset} to authenticate.\n Get your API key at: ${c.cyan}https://vibecheckai.dev/settings/keys${c.reset}`; }
|
|
294
|
+
else { msg += ` Your current plan: ${c.yellow}${currentPlan.toUpperCase()}${c.reset}\n Upgrade at: ${c.cyan}https://vibecheckai.dev/pricing${c.reset}`; }
|
|
295
|
+
return msg;
|
|
296
|
+
}
|
|
398
297
|
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
298
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
299
|
+
// HELP SYSTEM
|
|
300
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
301
|
+
function printBanner() {
|
|
302
|
+
console.log(`
|
|
303
|
+
${c.dim}${sym.boxTopLeft}${sym.boxHorizontal.repeat(60)}${sym.boxTopRight}${c.reset}
|
|
304
|
+
${c.dim}${sym.boxVertical}${c.reset} ${gradient("VIBECHECK", [[0, 255, 255], [138, 43, 226], [255, 20, 147]])} ${c.dim}v${VERSION}${c.reset}${" ".repeat(60 - 13 - VERSION.length - 4)}${c.dim}${sym.boxVertical}${c.reset}
|
|
305
|
+
${c.dim}${sym.boxVertical}${c.reset} ${c.dim}Ship with confidence. Catch fake features before users do.${c.reset} ${c.dim}${sym.boxVertical}${c.reset}
|
|
306
|
+
${c.dim}${sym.boxBottomLeft}${sym.boxHorizontal.repeat(60)}${sym.boxBottomRight}${c.reset}
|
|
307
|
+
`);
|
|
308
|
+
}
|
|
405
309
|
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
}
|
|
310
|
+
function printHelp() {
|
|
311
|
+
printBanner();
|
|
312
|
+
const categories = {
|
|
313
|
+
proof: { name: "THE PROOF LOOP", color: c.green, icon: sym.shield },
|
|
314
|
+
setup: { name: "SETUP & DIAGNOSTICS", color: c.yellow, icon: sym.gear },
|
|
315
|
+
truth: { name: "TRUTH SYSTEM", color: c.magenta, icon: sym.lightning },
|
|
316
|
+
extras: { name: "EXTRAS", color: c.dim, icon: sym.star },
|
|
317
|
+
ci: { name: "CI/CD", color: c.cyan, icon: sym.rocket },
|
|
318
|
+
automation: { name: "AUTOMATION", color: c.blue, icon: sym.fire },
|
|
319
|
+
account: { name: "ACCOUNT", color: c.dim, icon: sym.key },
|
|
320
|
+
};
|
|
321
|
+
const grouped = {};
|
|
322
|
+
for (const [cmd, def] of Object.entries(COMMANDS)) {
|
|
323
|
+
const cat = def.category || "extras";
|
|
324
|
+
if (!grouped[cat]) grouped[cat] = [];
|
|
325
|
+
grouped[cat].push({ cmd, ...def });
|
|
410
326
|
}
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
const
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
allowed: false,
|
|
419
|
-
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}`,
|
|
420
|
-
};
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
if (
|
|
424
|
-
entitlements.scopes?.includes(requiredScope) ||
|
|
425
|
-
entitlements.scopes?.includes("*")
|
|
426
|
-
) {
|
|
427
|
-
return { allowed: true, tier: "pro" };
|
|
327
|
+
for (const [catKey, commands] of Object.entries(grouped)) {
|
|
328
|
+
const cat = categories[catKey] || { name: catKey.toUpperCase(), color: c.white, icon: sym.bullet };
|
|
329
|
+
console.log(`\n${cat.color}${cat.icon} ${cat.name}${c.reset}\n`);
|
|
330
|
+
for (const { cmd, description, tier, aliases } of commands) {
|
|
331
|
+
const tierBadge = tier === "starter" ? `${c.cyan}[STARTER]${c.reset} ` : tier === "pro" ? `${c.magenta}[PRO]${c.reset} ` : "";
|
|
332
|
+
const aliasStr = aliases?.length ? `${c.dim}(${aliases.join(", ")})${c.reset}` : "";
|
|
333
|
+
console.log(` ${c.cyan}${cmd.padEnd(12)}${c.reset} ${tierBadge}${description} ${aliasStr}`);
|
|
428
334
|
}
|
|
429
|
-
|
|
430
|
-
return {
|
|
431
|
-
allowed: false,
|
|
432
|
-
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}`,
|
|
433
|
-
};
|
|
434
335
|
}
|
|
336
|
+
console.log(`
|
|
337
|
+
${c.dim}${sym.boxHorizontal.repeat(64)}${c.reset}
|
|
435
338
|
|
|
436
|
-
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
function printHelp() {
|
|
440
|
-
console.log(
|
|
441
|
-
`
|
|
442
|
-
${c.cyan}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${c.reset}
|
|
443
|
-
${c.cyan} VIBECHECK${c.reset} - Ship with confidence
|
|
444
|
-
${c.cyan}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${c.reset}
|
|
445
|
-
|
|
446
|
-
${c.green}🚀 QUICK START${c.reset}
|
|
447
|
-
|
|
448
|
-
${c.cyan}ship${c.reset} "Is my app ready?" - Plain English, traffic light score
|
|
449
|
-
${c.cyan}ship --fix${c.reset} Same + auto-fix problems
|
|
450
|
-
|
|
451
|
-
${c.yellow}🧪 TESTING${c.reset} (each does something different!)
|
|
452
|
-
|
|
453
|
-
${c.cyan}scan${c.reset} ${c.dim}Route Integrity${c.reset} - dead links, orphans, coverage, security 🗺️
|
|
454
|
-
${c.cyan}scan --truth${c.reset} ${c.dim}+ Build manifest${c.reset} verification (CI/ship ready)
|
|
455
|
-
${c.cyan}scan --reality${c.reset} ${c.dim}+ Playwright${c.reset} runtime proof (best-in-class)
|
|
456
|
-
${c.cyan}reality${c.reset} ${c.dim}Browser testing${c.reset} - clicks buttons, fills forms, finds broken UI
|
|
457
|
-
${c.cyan}ai-test${c.reset} ${c.dim}AI Agent${c.reset} - autonomous testing + generates fix prompts 🤖
|
|
458
|
-
|
|
459
|
-
${c.magenta}🚦 CI/CD & GATES${c.reset}
|
|
460
|
-
|
|
461
|
-
${c.cyan}gate${c.reset} Block bad deploys - pass/fail for CI pipelines
|
|
462
|
-
${c.cyan}proof mocks${c.reset} Block mock/demo code from reaching production
|
|
463
|
-
${c.cyan}proof reality${c.reset} Runtime GO/NO-GO verification with Playwright
|
|
464
|
-
|
|
465
|
-
${c.blue}🔧 FIX & AUTOMATE${c.reset}
|
|
466
|
-
|
|
467
|
-
${c.cyan}fix${c.reset} Auto-fix detected issues (--plan first, then --apply)
|
|
468
|
-
${c.cyan}autopilot${c.reset} Continuous protection - weekly reports, auto-PRs
|
|
469
|
-
${c.cyan}badge${c.reset} Generate Ship Badge for your README/PR
|
|
470
|
-
${c.cyan}certify${c.reset} Generate vibecheck Certified badge with verification link
|
|
471
|
-
|
|
472
|
-
${c.dim}📦 TRUTH SYSTEM${c.reset}
|
|
473
|
-
|
|
474
|
-
${c.cyan}ctx${c.reset} Generate Truth Pack - ground truth for AI agents
|
|
475
|
-
${c.cyan}ctx --snapshot${c.reset} Save snapshot to .vibecheck/truth/snapshots/
|
|
476
|
-
${c.cyan}graph${c.reset} Build Reality Proof Graph - end-to-end causal chains
|
|
477
|
-
|
|
478
|
-
${c.dim}📦 EXTRAS${c.reset}
|
|
479
|
-
|
|
480
|
-
${c.cyan}audit${c.reset} View/export audit trail (Compliance+ tier)
|
|
481
|
-
${c.cyan}context${c.reset} Generate AI rules files (.cursorrules, .windsurf/rules, etc.)
|
|
482
|
-
${c.cyan}dashboard${c.reset} Real-time monitoring dashboard with live metrics
|
|
483
|
-
${c.cyan}demo${c.reset} Interactive terminal features showcase
|
|
484
|
-
${c.cyan}launch${c.reset} Pre-launch checklist wizard
|
|
485
|
-
${c.cyan}validate${c.reset} Check AI-generated code for hallucinations
|
|
486
|
-
${c.cyan}init${c.reset} Set up vibecheck in your project
|
|
487
|
-
${c.cyan}doctor${c.reset} Debug environment issues
|
|
488
|
-
${c.cyan}mcp${c.reset} Start MCP server for AI editors
|
|
489
|
-
|
|
490
|
-
${c.dim}🔑 ACCOUNT${c.reset}
|
|
491
|
-
|
|
492
|
-
${c.cyan}login${c.reset} Sign in with API key
|
|
493
|
-
${c.cyan}logout${c.reset} Sign out
|
|
494
|
-
${c.cyan}whoami${c.reset} Show current user & plan
|
|
495
|
-
${c.cyan}upgrade${c.reset} Manage subscription & view usage
|
|
339
|
+
${c.green}QUICK START${c.reset}
|
|
496
340
|
|
|
497
|
-
${c.cyan}
|
|
341
|
+
${c.bold}"Check my repo"${c.reset} ${c.cyan}vibecheck scan${c.reset}
|
|
342
|
+
${c.bold}"Can I ship?"${c.reset} ${c.cyan}vibecheck prove --url http://localhost:3000${c.reset}
|
|
343
|
+
${c.bold}"Why did it fail?"${c.reset} ${c.cyan}vibecheck report${c.reset} ${c.dim}(then: vibecheck fix)${c.reset}
|
|
498
344
|
|
|
499
|
-
${c.
|
|
345
|
+
${c.dim}Run 'vibecheck <command> --help' for command-specific help.${c.reset}
|
|
346
|
+
`);
|
|
347
|
+
}
|
|
500
348
|
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
349
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
350
|
+
// HELPER FUNCTIONS
|
|
351
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
352
|
+
function getArgValue(args, flags) {
|
|
353
|
+
for (const flag of flags) {
|
|
354
|
+
const idx = args.indexOf(flag);
|
|
355
|
+
if (idx !== -1 && idx < args.length - 1) return args[idx + 1];
|
|
356
|
+
}
|
|
357
|
+
return undefined;
|
|
358
|
+
}
|
|
509
359
|
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
);
|
|
360
|
+
function formatError(error, config) {
|
|
361
|
+
const lines = [`${c.red}${sym.error} Error:${c.reset} ${error.message}`];
|
|
362
|
+
if (config.debug && error.stack) { lines.push("", `${c.dim}Stack trace:${c.reset}`, c.dim + error.stack.split("\n").slice(1).join("\n") + c.reset); }
|
|
363
|
+
return lines.join("\n");
|
|
513
364
|
}
|
|
514
365
|
|
|
515
|
-
|
|
366
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
367
|
+
// MAIN ENTRY POINT
|
|
368
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
369
|
+
async function main() {
|
|
370
|
+
const startTime = performance.now();
|
|
516
371
|
const rawArgs = process.argv.slice(2);
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
const
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
}
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
"-h",
|
|
537
|
-
"--help",
|
|
538
|
-
"version",
|
|
539
|
-
"login",
|
|
540
|
-
"logout",
|
|
541
|
-
"whoami",
|
|
542
|
-
"doctor",
|
|
543
|
-
];
|
|
544
|
-
|
|
545
|
-
if (!cmd || cmd === "-h" || cmd === "--help" || cmd === "help") {
|
|
546
|
-
printHelp();
|
|
372
|
+
const config = loadConfig();
|
|
373
|
+
const state = loadState();
|
|
374
|
+
|
|
375
|
+
const globalFlags = {
|
|
376
|
+
debug: rawArgs.includes("--debug") || rawArgs.includes("-d"),
|
|
377
|
+
verbose: rawArgs.includes("--verbose") || rawArgs.includes("-v"),
|
|
378
|
+
quiet: rawArgs.includes("--quiet") || rawArgs.includes("-q"),
|
|
379
|
+
help: rawArgs.includes("--help") || rawArgs.includes("-h"),
|
|
380
|
+
version: rawArgs.includes("--version") || rawArgs.includes("-V"),
|
|
381
|
+
};
|
|
382
|
+
|
|
383
|
+
if (globalFlags.debug) config.debug = true;
|
|
384
|
+
if (globalFlags.verbose) config.verbose = true;
|
|
385
|
+
if (globalFlags.quiet) config.quiet = true;
|
|
386
|
+
|
|
387
|
+
const args = rawArgs.filter(a => !["--debug", "-d", "--verbose", "-v", "--quiet", "-q", "--help", "-h", "--version", "-V"].includes(a));
|
|
388
|
+
|
|
389
|
+
if (globalFlags.version || args[0] === "version") {
|
|
390
|
+
console.log(`vibecheck v${VERSION}`);
|
|
547
391
|
process.exit(0);
|
|
548
392
|
}
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
if (
|
|
554
|
-
//
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
authInfo = await showWelcomeAndPromptLogin();
|
|
573
|
-
} else if (key && !isFreeCommand) {
|
|
574
|
-
// Has API key and command requires auth - get entitlements silently
|
|
575
|
-
// For free commands, we don't need to check entitlements
|
|
576
|
-
try {
|
|
577
|
-
const entitlements = await getEntitlements(key);
|
|
578
|
-
authInfo = { key, entitlements };
|
|
579
|
-
} catch (error) {
|
|
580
|
-
// If API is unavailable, allow free commands to proceed
|
|
581
|
-
// Paid commands will be blocked in checkCommandAccess
|
|
582
|
-
if (isFreeCommand) {
|
|
583
|
-
authInfo = { key, entitlements: null };
|
|
584
|
-
} else {
|
|
585
|
-
throw error;
|
|
586
|
-
}
|
|
587
|
-
}
|
|
588
|
-
} else if (key && isFreeCommand) {
|
|
589
|
-
// Free command with key - no need to fetch entitlements
|
|
590
|
-
authInfo = { key, entitlements: null };
|
|
591
|
-
}
|
|
592
|
-
}
|
|
593
|
-
|
|
594
|
-
// Check if user has access to this command (only for known commands)
|
|
595
|
-
if (isKnownCommand) {
|
|
596
|
-
const access = await checkCommandAccess(cmd, authInfo.entitlements, args);
|
|
597
|
-
|
|
598
|
-
if (!access.allowed) {
|
|
599
|
-
console.log(`\n${c.red}✗ Access Denied${c.reset}\n`);
|
|
600
|
-
console.log(access.reason);
|
|
601
|
-
console.log("");
|
|
602
|
-
process.exit(1);
|
|
603
|
-
}
|
|
604
|
-
|
|
605
|
-
// Show tier info for paid features
|
|
606
|
-
if (access.tier === "starter") {
|
|
607
|
-
console.log(`${c.cyan}▸ STARTER${c.reset} ${c.dim}feature${c.reset}\n`);
|
|
608
|
-
} else if (access.tier === "pro") {
|
|
609
|
-
console.log(`${c.magenta}▸ PRO${c.reset} ${c.dim}feature${c.reset}\n`);
|
|
610
|
-
}
|
|
393
|
+
|
|
394
|
+
if (!args[0]) { printHelp(); process.exit(0); }
|
|
395
|
+
|
|
396
|
+
// Handle command-specific help (vibecheck <cmd> --help)
|
|
397
|
+
if (globalFlags.help && args[0] && COMMANDS[args[0]]) {
|
|
398
|
+
// Pass --help to the command runner
|
|
399
|
+
} else if (globalFlags.help && !args[0]) {
|
|
400
|
+
printHelp(); process.exit(0);
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
let cmd = args[0];
|
|
404
|
+
if (ALIAS_MAP[cmd]) { cmd = ALIAS_MAP[cmd]; }
|
|
405
|
+
let cmdArgs = args.slice(1);
|
|
406
|
+
|
|
407
|
+
// Add --help back to cmdArgs if it was passed with a command
|
|
408
|
+
if (globalFlags.help) cmdArgs = ["--help", ...cmdArgs];
|
|
409
|
+
|
|
410
|
+
if (!COMMANDS[cmd]) {
|
|
411
|
+
const suggestions = findSimilarCommands(cmd, ALL_COMMANDS);
|
|
412
|
+
console.log(`\n${c.red}${sym.error}${c.reset} Unknown command: ${c.yellow}${cmd}${c.reset}`);
|
|
413
|
+
if (suggestions.length > 0) {
|
|
414
|
+
console.log(`\n${c.dim}Did you mean:${c.reset}`);
|
|
415
|
+
suggestions.forEach(s => { const actual = ALIAS_MAP[s] || s; const def = COMMANDS[actual]; console.log(` ${c.cyan}vibecheck ${s}${c.reset} ${c.dim}${def?.description || ""}${c.reset}`); });
|
|
611
416
|
}
|
|
417
|
+
console.log(`\n${c.dim}Run 'vibecheck --help' for available commands.${c.reset}\n`);
|
|
418
|
+
process.exit(1);
|
|
612
419
|
}
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
420
|
+
|
|
421
|
+
const cmdDef = COMMANDS[cmd];
|
|
422
|
+
let authInfo = { key: null, entitlements: null };
|
|
423
|
+
|
|
424
|
+
if (!cmdDef.skipAuth) {
|
|
425
|
+
const auth = getAuthModule();
|
|
426
|
+
const { key } = auth.getApiKey();
|
|
427
|
+
|
|
428
|
+
if (key && cmdDef.tier !== "free") {
|
|
429
|
+
try { authInfo.key = key; authInfo.entitlements = await auth.getEntitlements(key); } catch (e) { if (config.verbose) console.log(`${c.yellow}${sym.warning}${c.reset} ${c.dim}Could not verify credentials${c.reset}`); }
|
|
430
|
+
} else { authInfo.key = key; }
|
|
431
|
+
|
|
432
|
+
const access = await checkCommandAccess(cmd, cmdArgs, authInfo.entitlements);
|
|
433
|
+
if (!access.allowed) { console.log(`\n${c.red}${sym.error} Access Denied${c.reset}\n`); console.log(access.reason); console.log(""); process.exit(1); }
|
|
434
|
+
if (access.tier === "starter" && !config.quiet) console.log(`${c.cyan}${sym.arrowRight} STARTER${c.reset} ${c.dim}feature${c.reset}`);
|
|
435
|
+
else if (access.tier === "pro" && !config.quiet) console.log(`${c.magenta}${sym.arrowRight} PRO${c.reset} ${c.dim}feature${c.reset}`);
|
|
618
436
|
}
|
|
619
|
-
|
|
437
|
+
|
|
438
|
+
state.runCount++; state.lastRun = Date.now();
|
|
439
|
+
state.commandHistory = [...(state.commandHistory || []).slice(-99), { cmd, timestamp: Date.now() }];
|
|
440
|
+
saveState(state);
|
|
441
|
+
|
|
442
|
+
let exitCode = 0;
|
|
620
443
|
try {
|
|
621
|
-
|
|
444
|
+
const runner = getRunner(cmd);
|
|
445
|
+
if (!runner) { console.error(`${c.red}${sym.error}${c.reset} Failed to load runner for: ${cmd}`); process.exit(1); }
|
|
446
|
+
|
|
447
|
+
const context = { repoRoot: process.cwd(), config, state, authInfo, version: VERSION, isCI: isCI() };
|
|
448
|
+
|
|
622
449
|
switch (cmd) {
|
|
623
|
-
case "
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
case "
|
|
627
|
-
exitCode = await
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
exitCode = await
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
case "
|
|
636
|
-
case "
|
|
637
|
-
|
|
638
|
-
break;
|
|
639
|
-
case "launch":
|
|
640
|
-
exitCode = await runLaunch(args);
|
|
641
|
-
break;
|
|
642
|
-
case "autopilot":
|
|
643
|
-
exitCode = await runAutopilot(args);
|
|
644
|
-
break;
|
|
645
|
-
case "fix":
|
|
646
|
-
exitCode = await runFix(args);
|
|
647
|
-
break;
|
|
648
|
-
case "share":
|
|
649
|
-
exitCode = await runShare({
|
|
650
|
-
repoRoot: process.cwd(),
|
|
651
|
-
missionDir: args.find((a, i) => args[i-1] === '--mission-dir'),
|
|
652
|
-
outputDir: args.find((a, i) => args[i-1] === '--output-dir'),
|
|
653
|
-
prComment: args.includes('--pr-comment')
|
|
654
|
-
});
|
|
655
|
-
break;
|
|
656
|
-
case "pr":
|
|
657
|
-
exitCode = await runPR({
|
|
658
|
-
repoRoot: process.cwd(),
|
|
659
|
-
fastifyEntry: args.find((a, i) => args[i-1] === '--fastify-entry'),
|
|
660
|
-
out: args.find((a, i) => args[i-1] === '--out'),
|
|
661
|
-
failOnWarn: args.includes('--fail-on-warn'),
|
|
662
|
-
maxFindings: Number(args.find((a, i) => args[i-1] === '--max-findings')) || 12
|
|
663
|
-
});
|
|
664
|
-
break;
|
|
665
|
-
case "install":
|
|
666
|
-
exitCode = await runInstall({ repoRoot: process.cwd() });
|
|
667
|
-
break;
|
|
668
|
-
case "prove":
|
|
669
|
-
exitCode = await runProve({
|
|
670
|
-
repoRoot: process.cwd(),
|
|
671
|
-
url: args.find((a, i) => args[i-1] === '--url' || args[i-1] === '-u'),
|
|
672
|
-
auth: args.find((a, i) => args[i-1] === '--auth'),
|
|
673
|
-
storageState: args.find((a, i) => args[i-1] === '--storage-state'),
|
|
674
|
-
fastifyEntry: args.find((a, i) => args[i-1] === '--fastify-entry'),
|
|
675
|
-
maxFixRounds: Number(args.find((a, i) => args[i-1] === '--max-fix-rounds')) || 3,
|
|
676
|
-
maxMissions: Number(args.find((a, i) => args[i-1] === '--max-missions')) || 8,
|
|
677
|
-
maxSteps: Number(args.find((a, i) => args[i-1] === '--max-steps')) || 10,
|
|
678
|
-
skipReality: args.includes('--skip-reality'),
|
|
679
|
-
skipFix: args.includes('--skip-fix'),
|
|
680
|
-
headed: args.includes('--headed'),
|
|
681
|
-
danger: args.includes('--danger'),
|
|
682
|
-
maxPages: Number(args.find((a, i) => args[i-1] === '--max-pages')) || 18,
|
|
683
|
-
maxDepth: Number(args.find((a, i) => args[i-1] === '--max-depth')) || 2,
|
|
684
|
-
timeoutMs: Number(args.find((a, i) => args[i-1] === '--timeout-ms')) || 15000
|
|
685
|
-
});
|
|
686
|
-
break;
|
|
687
|
-
case "watch":
|
|
688
|
-
exitCode = await runWatch({
|
|
689
|
-
repoRoot: process.cwd(),
|
|
690
|
-
fastifyEntry: args.find((a, i) => args[i-1] === '--fastify-entry'),
|
|
691
|
-
debounceMs: Number(args.find((a, i) => args[i-1] === '--debounce')) || 500,
|
|
692
|
-
clearScreen: !args.includes('--no-clear')
|
|
693
|
-
});
|
|
694
|
-
break;
|
|
695
|
-
case "status":
|
|
696
|
-
exitCode = await runStatus({
|
|
697
|
-
repoRoot: process.cwd(),
|
|
698
|
-
json: args.includes('--json')
|
|
699
|
-
});
|
|
700
|
-
break;
|
|
701
|
-
case "proof":
|
|
702
|
-
exitCode = await runProof(args);
|
|
703
|
-
break;
|
|
704
|
-
case "reality":
|
|
705
|
-
exitCode = await runReality({
|
|
706
|
-
repoRoot: process.cwd(),
|
|
707
|
-
url: args.find((a, i) => args[i-1] === '--url' || args[i-1] === '-u'),
|
|
708
|
-
auth: args.find((a, i) => args[i-1] === '--auth'),
|
|
709
|
-
storageState: args.find((a, i) => args[i-1] === '--storage-state'),
|
|
710
|
-
saveStorageState: args.find((a, i) => args[i-1] === '--save-storage-state'),
|
|
711
|
-
truthpack: args.find((a, i) => args[i-1] === '--truthpack'),
|
|
712
|
-
verifyAuth: args.includes('--verify-auth'),
|
|
713
|
-
headed: args.includes('--headed'),
|
|
714
|
-
maxPages: Number(args.find((a, i) => args[i-1] === '--max-pages')) || 18,
|
|
715
|
-
maxDepth: Number(args.find((a, i) => args[i-1] === '--max-depth')) || 2,
|
|
716
|
-
danger: args.includes('--danger'),
|
|
717
|
-
timeoutMs: Number(args.find((a, i) => args[i-1] === '--timeout-ms')) || 15000
|
|
718
|
-
});
|
|
719
|
-
break;
|
|
720
|
-
case "reality-sniff":
|
|
721
|
-
case "sniff":
|
|
722
|
-
exitCode = await runRealitySniff(args);
|
|
723
|
-
break;
|
|
724
|
-
case "ai-test":
|
|
725
|
-
case "ai":
|
|
726
|
-
case "agent":
|
|
727
|
-
exitCode = await runAIAgent(args);
|
|
728
|
-
break;
|
|
729
|
-
case "validate":
|
|
730
|
-
exitCode = await runValidate(args);
|
|
731
|
-
break;
|
|
732
|
-
case "doctor":
|
|
733
|
-
exitCode = runDoctor(args);
|
|
734
|
-
break;
|
|
735
|
-
case "init":
|
|
736
|
-
// Enterprise init handles all flags including --gha, --gitlab, --compliance, etc.
|
|
737
|
-
exitCode = await runInit(args);
|
|
738
|
-
break;
|
|
739
|
-
case "mcp":
|
|
740
|
-
exitCode = runMcp(args);
|
|
741
|
-
break;
|
|
742
|
-
case "login":
|
|
743
|
-
exitCode = await runLogin(args);
|
|
744
|
-
break;
|
|
745
|
-
case "logout":
|
|
746
|
-
exitCode = await runLogout(args);
|
|
747
|
-
break;
|
|
748
|
-
case "whoami":
|
|
749
|
-
exitCode = await runWhoami(args);
|
|
750
|
-
break;
|
|
751
|
-
case "badge":
|
|
752
|
-
exitCode = await runBadge(args);
|
|
753
|
-
break;
|
|
754
|
-
case "context":
|
|
755
|
-
case "rules":
|
|
756
|
-
exitCode = await runContext(args);
|
|
757
|
-
break;
|
|
758
|
-
case "dashboard":
|
|
759
|
-
exitCode = await runDashboard(args);
|
|
760
|
-
break;
|
|
761
|
-
case "demo":
|
|
762
|
-
exitCode = await runDemo(args);
|
|
763
|
-
break;
|
|
764
|
-
case "upgrade":
|
|
765
|
-
exitCode = await runUpgrade(args);
|
|
766
|
-
break;
|
|
767
|
-
case "certify":
|
|
768
|
-
exitCode = await runCertify(args, process.cwd());
|
|
769
|
-
break;
|
|
770
|
-
case "verify-agent-output":
|
|
771
|
-
exitCode = await runVerifyAgentOutput(args);
|
|
772
|
-
break;
|
|
773
|
-
case "fixpacks":
|
|
774
|
-
exitCode = await runFixPacks(args);
|
|
775
|
-
break;
|
|
776
|
-
case "audit":
|
|
777
|
-
exitCode = await runAudit(args);
|
|
778
|
-
break;
|
|
779
|
-
case "mdc":
|
|
780
|
-
exitCode = await runMdc(args);
|
|
781
|
-
break;
|
|
782
|
-
case "graph":
|
|
783
|
-
exitCode = await runGraph(args);
|
|
784
|
-
break;
|
|
785
|
-
case "permissions":
|
|
786
|
-
case "authz":
|
|
787
|
-
exitCode = await runPermissions(args);
|
|
788
|
-
break;
|
|
789
|
-
case "ctx":
|
|
790
|
-
case "truthpack":
|
|
791
|
-
// Check for subcommands
|
|
792
|
-
if (args[0] === "sync") {
|
|
793
|
-
exitCode = await runCtxSync({
|
|
794
|
-
repoRoot: process.cwd(),
|
|
795
|
-
fastifyEntry: args.find((a, i) => args[i-1] === '--fastify-entry')
|
|
796
|
-
});
|
|
797
|
-
break;
|
|
798
|
-
}
|
|
799
|
-
if (args[0] === "guard") {
|
|
800
|
-
exitCode = await runCtxGuard.main(args.slice(1));
|
|
801
|
-
break;
|
|
802
|
-
}
|
|
803
|
-
// Parse args for ctx command - use Route Truth v1
|
|
804
|
-
const fastifyEntryIdx = args.indexOf('--fastify-entry');
|
|
805
|
-
const fastifyEntry = fastifyEntryIdx !== -1 ? args[fastifyEntryIdx + 1] : undefined;
|
|
806
|
-
await runCtx({
|
|
807
|
-
repoRoot: process.cwd(),
|
|
808
|
-
fastifyEntry,
|
|
809
|
-
print: args.includes('--print'),
|
|
810
|
-
});
|
|
811
|
-
exitCode = 0;
|
|
812
|
-
break;
|
|
813
|
-
case "version":
|
|
814
|
-
console.log(`vibecheck v${VERSION}`);
|
|
815
|
-
break;
|
|
816
|
-
default:
|
|
817
|
-
// Try natural language parsing as fallback for unknown commands
|
|
818
|
-
const nlInput = [cmd, ...args].join(" ");
|
|
819
|
-
if (isNaturalLanguageCommand(nlInput)) {
|
|
820
|
-
exitCode = await runNaturalLanguage(nlInput);
|
|
821
|
-
} else {
|
|
822
|
-
process.stderr.write(`Unknown command: ${cmd}\n\n`);
|
|
823
|
-
printHelp();
|
|
824
|
-
exitCode = 1;
|
|
825
|
-
}
|
|
450
|
+
case "prove": exitCode = await runner({ ...context, url: getArgValue(cmdArgs, ["--url", "-u"]), auth: getArgValue(cmdArgs, ["--auth"]), storageState: getArgValue(cmdArgs, ["--storage-state"]), fastifyEntry: getArgValue(cmdArgs, ["--fastify-entry"]), maxFixRounds: parseInt(getArgValue(cmdArgs, ["--max-fix-rounds"]) || "3", 10), skipReality: cmdArgs.includes("--skip-reality"), skipFix: cmdArgs.includes("--skip-fix"), headed: cmdArgs.includes("--headed"), danger: cmdArgs.includes("--danger") }); break;
|
|
451
|
+
case "reality": exitCode = await runner({ ...context, url: getArgValue(cmdArgs, ["--url", "-u"]), auth: getArgValue(cmdArgs, ["--auth"]), storageState: getArgValue(cmdArgs, ["--storage-state"]), saveStorageState: getArgValue(cmdArgs, ["--save-storage-state"]), truthpack: getArgValue(cmdArgs, ["--truthpack"]), verifyAuth: cmdArgs.includes("--verify-auth"), headed: cmdArgs.includes("--headed"), danger: cmdArgs.includes("--danger") }); break;
|
|
452
|
+
case "watch": exitCode = await runner({ ...context, fastifyEntry: getArgValue(cmdArgs, ["--fastify-entry"]), debounceMs: parseInt(getArgValue(cmdArgs, ["--debounce"]) || "500", 10), clearScreen: !cmdArgs.includes("--no-clear") }); break;
|
|
453
|
+
case "ctx": case "truthpack":
|
|
454
|
+
if (cmdArgs[0] === "sync") { const { runCtxSync } = require("./runners/runCtxSync"); exitCode = await runCtxSync({ ...context, fastifyEntry: getArgValue(cmdArgs, ["--fastify-entry"]) }); }
|
|
455
|
+
else if (cmdArgs[0] === "guard") { const { runCtxGuard } = require("./runners/runCtxGuard"); exitCode = await runCtxGuard.main(cmdArgs.slice(1)); }
|
|
456
|
+
else if (cmdArgs[0] === "diff") { const { main: ctxDiffMain } = require("./runners/runCtxDiff"); exitCode = await ctxDiffMain(cmdArgs.slice(1)); }
|
|
457
|
+
else if (cmdArgs[0] === "search") { const { runContext } = require("./runners/runContext"); exitCode = await runContext(["--search", ...cmdArgs.slice(1)]); }
|
|
458
|
+
else { exitCode = await runner({ ...context, fastifyEntry: getArgValue(cmdArgs, ["--fastify-entry"]), print: cmdArgs.includes("--print") }); }
|
|
459
|
+
break;
|
|
460
|
+
case "install": exitCode = await runner(context); break;
|
|
461
|
+
case "status": exitCode = await runner({ ...context, json: cmdArgs.includes("--json") }); break;
|
|
462
|
+
case "pr": exitCode = await runner({ ...context, fastifyEntry: getArgValue(cmdArgs, ["--fastify-entry"]), out: getArgValue(cmdArgs, ["--out"]), failOnWarn: cmdArgs.includes("--fail-on-warn"), maxFindings: parseInt(getArgValue(cmdArgs, ["--max-findings"]) || "12", 10) }); break;
|
|
463
|
+
case "share": exitCode = await runner({ ...context, missionDir: getArgValue(cmdArgs, ["--mission-dir"]), outputDir: getArgValue(cmdArgs, ["--output-dir"]), prComment: cmdArgs.includes("--pr-comment") }); break;
|
|
464
|
+
default: exitCode = await runner(cmdArgs);
|
|
826
465
|
}
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
466
|
+
} catch (error) { console.error(formatError(error, config)); exitCode = 1; }
|
|
467
|
+
|
|
468
|
+
if (config.debug) console.log(`\n${c.dim}${sym.clock} Total: ${(performance.now() - startTime).toFixed(0)}ms${c.reset}`);
|
|
469
|
+
process.exit(exitCode);
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
473
|
+
// GRACEFUL SHUTDOWN
|
|
474
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
475
|
+
process.on("SIGINT", () => { console.log(`\n${c.yellow}${sym.warning}${c.reset} Interrupted`); process.exit(130); });
|
|
476
|
+
process.on("SIGTERM", () => { console.log(`\n${c.yellow}${sym.warning}${c.reset} Terminated`); process.exit(143); });
|
|
477
|
+
process.on("uncaughtException", (error) => { console.error(`\n${c.red}${sym.error} Uncaught Exception:${c.reset} ${error.message}`); if (process.env.VIBECHECK_DEBUG === "true") console.error(c.dim + error.stack + c.reset); process.exit(1); });
|
|
478
|
+
process.on("unhandledRejection", (reason) => { console.error(`\n${c.red}${sym.error} Unhandled Rejection:${c.reset} ${reason}`); process.exit(1); });
|
|
479
|
+
|
|
480
|
+
main().catch((error) => { console.error(`\n${c.red}${sym.error} Fatal:${c.reset} ${error.message}`); process.exit(1); });
|