@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/runners/runMdc.js
CHANGED
|
@@ -1,2 +1,204 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* vibecheck mdc - Generate MDC (Markdown Context) documentation
|
|
3
|
+
*
|
|
4
|
+
* Generates .mdc specification files for AI coding agents.
|
|
5
|
+
* These files help AI understand your codebase architecture.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const path = require("path");
|
|
9
|
+
const fs = require("fs");
|
|
10
|
+
|
|
11
|
+
// ANSI colors
|
|
12
|
+
const c = {
|
|
13
|
+
reset: "\x1b[0m",
|
|
14
|
+
dim: "\x1b[2m",
|
|
15
|
+
bold: "\x1b[1m",
|
|
16
|
+
cyan: "\x1b[36m",
|
|
17
|
+
green: "\x1b[32m",
|
|
18
|
+
yellow: "\x1b[33m",
|
|
19
|
+
red: "\x1b[31m",
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
function printHelp() {
|
|
23
|
+
console.log(`
|
|
24
|
+
${c.cyan}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${c.reset}
|
|
25
|
+
${c.bold}vibecheck mdc${c.reset} - Generate MDC Specifications
|
|
26
|
+
${c.cyan}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${c.reset}
|
|
27
|
+
|
|
28
|
+
${c.green}USAGE${c.reset}
|
|
29
|
+
vibecheck mdc [options]
|
|
30
|
+
|
|
31
|
+
${c.yellow}OPTIONS${c.reset}
|
|
32
|
+
--output, -o <dir> Output directory (default: .specs)
|
|
33
|
+
--categories <list> Categories: architecture,security,data-flow,design-system
|
|
34
|
+
--depth <level> Analysis depth: shallow, medium, deep (default: medium)
|
|
35
|
+
--min-score <n> Minimum importance score 0-100 (default: 70)
|
|
36
|
+
--no-examples Skip code examples
|
|
37
|
+
--json Output JSON instead of MDC files
|
|
38
|
+
|
|
39
|
+
${c.dim}EXAMPLE${c.reset}
|
|
40
|
+
vibecheck mdc --output .cursor/rules --categories architecture,security
|
|
41
|
+
|
|
42
|
+
${c.dim}MDC files help AI coding agents understand your codebase structure.${c.reset}
|
|
43
|
+
`);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async function runMdc(args = []) {
|
|
47
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
48
|
+
printHelp();
|
|
49
|
+
return 0;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Parse arguments
|
|
53
|
+
const outputIdx = args.findIndex(a => a === "--output" || a === "-o");
|
|
54
|
+
const outputDir = outputIdx !== -1 ? args[outputIdx + 1] : ".specs";
|
|
55
|
+
|
|
56
|
+
const catIdx = args.findIndex(a => a === "--categories");
|
|
57
|
+
const categories = catIdx !== -1 ? args[catIdx + 1] : null;
|
|
58
|
+
|
|
59
|
+
const depthIdx = args.findIndex(a => a === "--depth");
|
|
60
|
+
const depth = depthIdx !== -1 ? args[depthIdx + 1] : "medium";
|
|
61
|
+
|
|
62
|
+
const minScoreIdx = args.findIndex(a => a === "--min-score");
|
|
63
|
+
const minScore = minScoreIdx !== -1 ? parseInt(args[minScoreIdx + 1]) : 70;
|
|
64
|
+
|
|
65
|
+
const noExamples = args.includes("--no-examples");
|
|
66
|
+
const jsonOutput = args.includes("--json");
|
|
67
|
+
|
|
68
|
+
console.log(`
|
|
69
|
+
${c.cyan}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${c.reset}
|
|
70
|
+
${c.bold}📄 VIBECHECK MDC${c.reset} - Generating Specifications
|
|
71
|
+
${c.cyan}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${c.reset}
|
|
72
|
+
`);
|
|
73
|
+
|
|
74
|
+
const repoRoot = process.cwd();
|
|
75
|
+
|
|
76
|
+
// Try to use the context module for MDC generation
|
|
77
|
+
try {
|
|
78
|
+
const { runContext } = require("./runContext");
|
|
79
|
+
|
|
80
|
+
// Build args for context command which can generate rules
|
|
81
|
+
const contextArgs = ["--output", outputDir];
|
|
82
|
+
if (categories) {
|
|
83
|
+
contextArgs.push("--categories", categories);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
console.log(`${c.dim}▸ Analyzing codebase...${c.reset}`);
|
|
87
|
+
|
|
88
|
+
// Generate context which includes MDC-compatible output
|
|
89
|
+
const exitCode = await runContext(contextArgs);
|
|
90
|
+
|
|
91
|
+
if (exitCode === 0) {
|
|
92
|
+
console.log(`
|
|
93
|
+
${c.green}✓${c.reset} MDC specifications generated in ${c.cyan}${outputDir}/${c.reset}
|
|
94
|
+
|
|
95
|
+
${c.dim}Generated files can be used with:${c.reset}
|
|
96
|
+
• Cursor (.cursor/rules/)
|
|
97
|
+
• Windsurf (.windsurf/rules/)
|
|
98
|
+
• GitHub Copilot (.github/copilot-instructions.md)
|
|
99
|
+
• Claude (.claude/project-context.md)
|
|
100
|
+
`);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return exitCode;
|
|
104
|
+
} catch (e) {
|
|
105
|
+
// Fallback: generate basic MDC from truthpack
|
|
106
|
+
console.log(`${c.dim}▸ Using basic MDC generation...${c.reset}`);
|
|
107
|
+
|
|
108
|
+
try {
|
|
109
|
+
const truthpackPath = path.join(repoRoot, ".vibecheck", "truth", "truthpack.json");
|
|
110
|
+
|
|
111
|
+
if (!fs.existsSync(truthpackPath)) {
|
|
112
|
+
console.log(`${c.yellow}⚠${c.reset} No truthpack found. Run ${c.cyan}vibecheck ctx${c.reset} first.`);
|
|
113
|
+
return 1;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const truthpack = JSON.parse(fs.readFileSync(truthpackPath, "utf8"));
|
|
117
|
+
|
|
118
|
+
// Generate basic MDC content
|
|
119
|
+
const mdcContent = generateBasicMDC(truthpack, repoRoot);
|
|
120
|
+
|
|
121
|
+
// Ensure output directory exists
|
|
122
|
+
const fullOutputDir = path.join(repoRoot, outputDir);
|
|
123
|
+
if (!fs.existsSync(fullOutputDir)) {
|
|
124
|
+
fs.mkdirSync(fullOutputDir, { recursive: true });
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Write MDC file
|
|
128
|
+
const mdcPath = path.join(fullOutputDir, "architecture.mdc");
|
|
129
|
+
fs.writeFileSync(mdcPath, mdcContent, "utf8");
|
|
130
|
+
|
|
131
|
+
console.log(`${c.green}✓${c.reset} Generated ${c.cyan}${outputDir}/architecture.mdc${c.reset}`);
|
|
132
|
+
return 0;
|
|
133
|
+
} catch (err) {
|
|
134
|
+
console.error(`${c.red}✗${c.reset} MDC generation failed:`, err.message);
|
|
135
|
+
return 1;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function generateBasicMDC(truthpack, repoRoot) {
|
|
141
|
+
const routes = truthpack.routes?.server || [];
|
|
142
|
+
const env = truthpack.env?.vars || [];
|
|
143
|
+
const auth = truthpack.auth || {};
|
|
144
|
+
|
|
145
|
+
let content = `---
|
|
146
|
+
description: Auto-generated architecture context from vibecheck
|
|
147
|
+
globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx"]
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
# Project Architecture
|
|
151
|
+
|
|
152
|
+
## API Routes (${routes.length} total)
|
|
153
|
+
|
|
154
|
+
`;
|
|
155
|
+
|
|
156
|
+
// Group routes by method
|
|
157
|
+
const byMethod = {};
|
|
158
|
+
routes.forEach(r => {
|
|
159
|
+
const method = r.method || "GET";
|
|
160
|
+
if (!byMethod[method]) byMethod[method] = [];
|
|
161
|
+
byMethod[method].push(r);
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
for (const [method, rs] of Object.entries(byMethod)) {
|
|
165
|
+
content += `### ${method}\n`;
|
|
166
|
+
rs.slice(0, 10).forEach(r => {
|
|
167
|
+
content += `- \`${r.path}\`\n`;
|
|
168
|
+
});
|
|
169
|
+
if (rs.length > 10) {
|
|
170
|
+
content += `- ... and ${rs.length - 10} more\n`;
|
|
171
|
+
}
|
|
172
|
+
content += "\n";
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (env.length > 0) {
|
|
176
|
+
content += `## Environment Variables (${env.length} total)\n\n`;
|
|
177
|
+
env.slice(0, 15).forEach(e => {
|
|
178
|
+
content += `- \`${e.name}\` - ${e.required ? "required" : "optional"}\n`;
|
|
179
|
+
});
|
|
180
|
+
if (env.length > 15) {
|
|
181
|
+
content += `- ... and ${env.length - 15} more\n`;
|
|
182
|
+
}
|
|
183
|
+
content += "\n";
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
if (auth.nextMiddleware?.length > 0 || auth.fastify?.hooks?.length > 0) {
|
|
187
|
+
content += `## Authentication\n\n`;
|
|
188
|
+
if (auth.nextMiddleware?.length > 0) {
|
|
189
|
+
content += `- Next.js middleware: ${auth.nextMiddleware.length} rules\n`;
|
|
190
|
+
}
|
|
191
|
+
if (auth.fastify?.hooks?.length > 0) {
|
|
192
|
+
content += `- Fastify hooks: ${auth.fastify.hooks.length} hooks\n`;
|
|
193
|
+
}
|
|
194
|
+
content += "\n";
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
content += `---
|
|
198
|
+
*Generated by vibecheck on ${new Date().toISOString().split("T")[0]}*
|
|
199
|
+
`;
|
|
200
|
+
|
|
201
|
+
return content;
|
|
202
|
+
}
|
|
203
|
+
|
|
2
204
|
module.exports = { runMdc };
|
|
Binary file
|
package/bin/runners/runProve.js
CHANGED
|
@@ -17,6 +17,7 @@ const fs = require("fs");
|
|
|
17
17
|
const path = require("path");
|
|
18
18
|
const { buildTruthpack, writeTruthpack, detectFastifyEntry } = require("./lib/truth");
|
|
19
19
|
const { shipCore } = require("./runShip");
|
|
20
|
+
const { findContractDrift, loadContracts, hasContracts, getDriftSummary } = require("./lib/drift");
|
|
20
21
|
|
|
21
22
|
let runReality;
|
|
22
23
|
try {
|
|
@@ -105,6 +106,28 @@ ${c.cyan}${c.bold}╔═══════════════════
|
|
|
105
106
|
});
|
|
106
107
|
|
|
107
108
|
console.log(` ${c.green}✓${c.reset} Truthpack refreshed (${truthpack.routes?.server?.length || 0} routes, ${truthpack.env?.vars?.length || 0} env vars)`);
|
|
109
|
+
|
|
110
|
+
// Check for contract drift after truthpack refresh
|
|
111
|
+
if (hasContracts(root)) {
|
|
112
|
+
const contracts = loadContracts(root);
|
|
113
|
+
const driftFindings = findContractDrift(contracts, truthpack);
|
|
114
|
+
const driftSummary = getDriftSummary(driftFindings);
|
|
115
|
+
|
|
116
|
+
if (driftSummary.hasDrift) {
|
|
117
|
+
console.log(` ${driftSummary.blocks > 0 ? c.yellow + '⚠️' : c.dim + 'ℹ️'} Contract drift: ${driftSummary.blocks} blocks, ${driftSummary.warns} warnings${c.reset}`);
|
|
118
|
+
timeline.push({
|
|
119
|
+
step: "1.5",
|
|
120
|
+
action: "drift_check",
|
|
121
|
+
status: driftSummary.verdict,
|
|
122
|
+
blocks: driftSummary.blocks,
|
|
123
|
+
warns: driftSummary.warns
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
if (driftSummary.blocks > 0) {
|
|
127
|
+
console.log(` ${c.dim}Run 'vibecheck ctx sync' to update contracts${c.reset}`);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
108
131
|
} catch (err) {
|
|
109
132
|
timeline.push({ step: 1, action: "ctx", status: "error", error: err.message });
|
|
110
133
|
console.log(` ${c.yellow}⚠️ Context refresh failed: ${err.message}${c.reset}`);
|
package/bin/runners/runReplay.js
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
1
|
/**
|
|
4
2
|
* Vibecheck Replay CLI
|
|
5
3
|
*
|
|
@@ -13,13 +11,11 @@
|
|
|
13
11
|
* vibecheck replay import <file> Import a replay capsule
|
|
14
12
|
*/
|
|
15
13
|
|
|
16
|
-
const {
|
|
17
|
-
const chalk = require('chalk');
|
|
18
|
-
const fs = require('fs').promises;
|
|
14
|
+
const { Command } = require('commander');
|
|
19
15
|
const path = require('path');
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
16
|
+
|
|
17
|
+
// Lazy-load heavy dependencies
|
|
18
|
+
let chalk, fs, chromium, createReplayEngine, version;
|
|
23
19
|
|
|
24
20
|
// Global options
|
|
25
21
|
let verbose = false;
|
|
@@ -28,10 +24,28 @@ let outputDir = path.join(process.cwd(), '.vibecheck', 'replays');
|
|
|
28
24
|
// Initialize replay engine
|
|
29
25
|
let replayEngine;
|
|
30
26
|
|
|
27
|
+
/**
|
|
28
|
+
* Load heavy dependencies on demand
|
|
29
|
+
*/
|
|
30
|
+
function loadDeps() {
|
|
31
|
+
if (!chalk) {
|
|
32
|
+
chalk = require('chalk');
|
|
33
|
+
fs = require('fs').promises;
|
|
34
|
+
chromium = require('playwright').chromium;
|
|
35
|
+
createReplayEngine = require('./lib/replay').createReplayEngine;
|
|
36
|
+
try {
|
|
37
|
+
version = require('../../package.json').version;
|
|
38
|
+
} catch {
|
|
39
|
+
version = '0.0.0';
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
31
44
|
/**
|
|
32
45
|
* Initialize the replay engine
|
|
33
46
|
*/
|
|
34
47
|
async function initEngine() {
|
|
48
|
+
loadDeps();
|
|
35
49
|
if (!replayEngine) {
|
|
36
50
|
replayEngine = await createReplayEngine({
|
|
37
51
|
basePath: process.cwd(),
|
|
@@ -45,6 +59,7 @@ async function initEngine() {
|
|
|
45
59
|
* Record a user session
|
|
46
60
|
*/
|
|
47
61
|
async function recordSession(url, options) {
|
|
62
|
+
loadDeps();
|
|
48
63
|
console.log(chalk.blue(`\n🚀 Starting recording session for ${url}\n`));
|
|
49
64
|
|
|
50
65
|
const browser = await chromium.launch({
|
|
@@ -115,6 +130,7 @@ async function recordSession(url, options) {
|
|
|
115
130
|
* Replay a recorded session
|
|
116
131
|
*/
|
|
117
132
|
async function replaySession(capsuleId, options) {
|
|
133
|
+
loadDeps();
|
|
118
134
|
console.log(chalk.blue(`\n▶️ Replaying capsule: ${capsuleId}\n`));
|
|
119
135
|
|
|
120
136
|
const browser = await chromium.launch({
|
|
@@ -381,89 +397,103 @@ async function importCapsule(filePath) {
|
|
|
381
397
|
}
|
|
382
398
|
}
|
|
383
399
|
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
400
|
+
/**
|
|
401
|
+
* Main entry point for replay command
|
|
402
|
+
* Only parses args when explicitly called
|
|
403
|
+
*/
|
|
404
|
+
async function runReplay(args = []) {
|
|
405
|
+
loadDeps();
|
|
406
|
+
|
|
407
|
+
const program = new Command();
|
|
408
|
+
|
|
409
|
+
program
|
|
410
|
+
.name('vibecheck replay')
|
|
411
|
+
.description('Record and replay user interactions for testing and debugging')
|
|
412
|
+
.version(version || '0.0.0')
|
|
413
|
+
.option('-v, --verbose', 'Enable verbose output', false)
|
|
414
|
+
.option('--output-dir <dir>', 'Directory to save replay files', outputDir)
|
|
415
|
+
.hook('preAction', (thisCommand) => {
|
|
416
|
+
verbose = thisCommand.opts().verbose;
|
|
417
|
+
outputDir = thisCommand.opts().outputDir || outputDir;
|
|
418
|
+
});
|
|
395
419
|
|
|
396
|
-
// Record command
|
|
397
|
-
program
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
420
|
+
// Record command
|
|
421
|
+
program
|
|
422
|
+
.command('record <url>')
|
|
423
|
+
.description('Record a user session')
|
|
424
|
+
.option('-n, --name <name>', 'Name for this recording')
|
|
425
|
+
.option('-d, --description <description>', 'Description of the recording')
|
|
426
|
+
.option('--tags <tags>', 'Comma-separated list of tags')
|
|
427
|
+
.option('--duration <seconds>', 'Auto-stop after specified seconds')
|
|
428
|
+
.option('--headed', 'Run browser in headed mode', false)
|
|
429
|
+
.option('--devtools', 'Open devtools', false)
|
|
430
|
+
.option('--video', 'Record video', false)
|
|
431
|
+
.action((url, options) => {
|
|
432
|
+
recordSession(url, options).catch(console.error);
|
|
433
|
+
});
|
|
410
434
|
|
|
411
|
-
// Play command
|
|
412
|
-
program
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
435
|
+
// Play command
|
|
436
|
+
program
|
|
437
|
+
.command('play <capsule>')
|
|
438
|
+
.description('Replay a recorded session')
|
|
439
|
+
.option('--speed <speed>', 'Playback speed (1.0 = normal, 2.0 = 2x, etc.)', parseFloat, 1.0)
|
|
440
|
+
.option('--headed', 'Run browser in headed mode', false)
|
|
441
|
+
.option('--devtools', 'Open devtools', false)
|
|
442
|
+
.option('--stop-on-failure', 'Stop on first failure', false)
|
|
443
|
+
.action((capsule, options) => {
|
|
444
|
+
replaySession(capsule, options).catch(console.error);
|
|
445
|
+
});
|
|
422
446
|
|
|
423
|
-
// List command
|
|
424
|
-
program
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
447
|
+
// List command
|
|
448
|
+
program
|
|
449
|
+
.command('list')
|
|
450
|
+
.description('List available replay capsules')
|
|
451
|
+
.action(() => {
|
|
452
|
+
listCapsules().catch(console.error);
|
|
453
|
+
});
|
|
430
454
|
|
|
431
|
-
// Show command
|
|
432
|
-
program
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
455
|
+
// Show command
|
|
456
|
+
program
|
|
457
|
+
.command('show <id>')
|
|
458
|
+
.description('Show details of a replay capsule')
|
|
459
|
+
.action((id) => {
|
|
460
|
+
showCapsule(id).catch(console.error);
|
|
461
|
+
});
|
|
438
462
|
|
|
439
|
-
// Delete command
|
|
440
|
-
program
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
463
|
+
// Delete command
|
|
464
|
+
program
|
|
465
|
+
.command('delete <id>')
|
|
466
|
+
.description('Delete a replay capsule')
|
|
467
|
+
.action((id) => {
|
|
468
|
+
deleteCapsule(id).catch(console.error);
|
|
469
|
+
});
|
|
446
470
|
|
|
447
|
-
// Export command
|
|
448
|
-
program
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
471
|
+
// Export command
|
|
472
|
+
program
|
|
473
|
+
.command('export <id> <file>')
|
|
474
|
+
.description('Export a replay capsule to a file')
|
|
475
|
+
.action((id, file) => {
|
|
476
|
+
exportCapsule(id, file).catch(console.error);
|
|
477
|
+
});
|
|
454
478
|
|
|
455
|
-
// Import command
|
|
456
|
-
program
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
479
|
+
// Import command
|
|
480
|
+
program
|
|
481
|
+
.command('import <file>')
|
|
482
|
+
.description('Import a replay capsule from a file')
|
|
483
|
+
.action((file) => {
|
|
484
|
+
importCapsule(file).catch(console.error);
|
|
485
|
+
});
|
|
462
486
|
|
|
463
|
-
//
|
|
464
|
-
|
|
465
|
-
|
|
487
|
+
// Parse the provided args (prepend fake node/script path for commander)
|
|
488
|
+
const argv = ['node', 'vibecheck-replay', ...args];
|
|
489
|
+
|
|
490
|
+
if (args.length === 0) {
|
|
491
|
+
program.help();
|
|
492
|
+
return 0;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
await program.parseAsync(argv);
|
|
496
|
+
return 0;
|
|
466
497
|
}
|
|
467
498
|
|
|
468
|
-
|
|
469
|
-
program.parse(process.argv);
|
|
499
|
+
module.exports = { runReplay };
|
package/bin/runners/runScan.js
CHANGED
|
@@ -478,8 +478,65 @@ async function runScan(args) {
|
|
|
478
478
|
}
|
|
479
479
|
|
|
480
480
|
try {
|
|
481
|
-
// Import systems
|
|
482
|
-
|
|
481
|
+
// Import systems - try TypeScript compiled first, fallback to JS runtime
|
|
482
|
+
let scanRouteIntegrity;
|
|
483
|
+
let useFallbackScanner = false;
|
|
484
|
+
|
|
485
|
+
try {
|
|
486
|
+
scanRouteIntegrity = require('../../dist/lib/route-integrity').scanRouteIntegrity;
|
|
487
|
+
} catch (e) {
|
|
488
|
+
// Fallback to JS-based scanner using truth.js and analyzers.js
|
|
489
|
+
useFallbackScanner = true;
|
|
490
|
+
const { buildTruthpack } = require('./lib/truth');
|
|
491
|
+
const { findMissingRoutes, findEnvGaps, findFakeSuccess, findGhostAuth } = require('./lib/analyzers');
|
|
492
|
+
|
|
493
|
+
scanRouteIntegrity = async function({ projectPath, layers, baseUrl, verbose }) {
|
|
494
|
+
// Build truthpack for route analysis
|
|
495
|
+
const truthpack = await buildTruthpack({ repoRoot: projectPath });
|
|
496
|
+
|
|
497
|
+
// Run analyzers
|
|
498
|
+
const findings = [];
|
|
499
|
+
findings.push(...findMissingRoutes(truthpack));
|
|
500
|
+
findings.push(...findEnvGaps(truthpack));
|
|
501
|
+
findings.push(...findFakeSuccess(projectPath));
|
|
502
|
+
findings.push(...findGhostAuth(truthpack, projectPath));
|
|
503
|
+
|
|
504
|
+
// Convert to scan format matching TypeScript scanner output
|
|
505
|
+
const shipBlockers = findings.map((f, i) => ({
|
|
506
|
+
id: f.id || `finding-${i}`,
|
|
507
|
+
ruleId: f.category,
|
|
508
|
+
category: f.category,
|
|
509
|
+
severity: f.severity === 'BLOCK' ? 'critical' : f.severity === 'WARN' ? 'warning' : 'info',
|
|
510
|
+
title: f.title,
|
|
511
|
+
message: f.title,
|
|
512
|
+
description: f.why,
|
|
513
|
+
file: f.evidence?.[0]?.file || '',
|
|
514
|
+
line: parseInt(f.evidence?.[0]?.lines?.split('-')[0]) || 1,
|
|
515
|
+
evidence: f.evidence || [],
|
|
516
|
+
fixHints: f.fixHints || [],
|
|
517
|
+
autofixAvailable: false,
|
|
518
|
+
verdict: f.severity === 'BLOCK' ? 'FAIL' : 'WARN',
|
|
519
|
+
}));
|
|
520
|
+
|
|
521
|
+
// Return structure matching TypeScript scanner
|
|
522
|
+
return {
|
|
523
|
+
report: {
|
|
524
|
+
shipBlockers,
|
|
525
|
+
realitySniffFindings: [],
|
|
526
|
+
routeMap: truthpack.routes,
|
|
527
|
+
summary: {
|
|
528
|
+
total: shipBlockers.length,
|
|
529
|
+
critical: shipBlockers.filter(f => f.severity === 'critical').length,
|
|
530
|
+
warning: shipBlockers.filter(f => f.severity === 'warning').length,
|
|
531
|
+
},
|
|
532
|
+
verdict: shipBlockers.some(f => f.severity === 'critical') ? 'BLOCK' :
|
|
533
|
+
shipBlockers.some(f => f.severity === 'warning') ? 'WARN' : 'SHIP'
|
|
534
|
+
},
|
|
535
|
+
outputPaths: {},
|
|
536
|
+
truthpack
|
|
537
|
+
};
|
|
538
|
+
};
|
|
539
|
+
}
|
|
483
540
|
|
|
484
541
|
// Try to import new unified output system (may not be compiled yet)
|
|
485
542
|
let buildVerdictOutput, normalizeFinding, formatStandardOutput, formatScanOutput, getExitCode, CacheManager;
|
|
@@ -648,35 +705,6 @@ async function runScan(args) {
|
|
|
648
705
|
|
|
649
706
|
const { report, outputPaths } = result;
|
|
650
707
|
|
|
651
|
-
// Normalize findings with stable IDs
|
|
652
|
-
const existingIDs = new Set();
|
|
653
|
-
const normalizedFindings = [];
|
|
654
|
-
|
|
655
|
-
// Normalize route integrity findings
|
|
656
|
-
if (report.shipBlockers) {
|
|
657
|
-
for (let i = 0; i < report.shipBlockers.length; i++) {
|
|
658
|
-
const blocker = report.shipBlockers[i];
|
|
659
|
-
const category = blocker.category || 'ROUTE';
|
|
660
|
-
const normalized = normalizeFinding(blocker, category, i, existingIDs);
|
|
661
|
-
normalizedFindings.push(normalized);
|
|
662
|
-
}
|
|
663
|
-
}
|
|
664
|
-
|
|
665
|
-
// Normalize Reality Sniff findings if present
|
|
666
|
-
if (report.realitySniffFindings) {
|
|
667
|
-
for (let i = 0; i < report.realitySniffFindings.length; i++) {
|
|
668
|
-
const finding = report.realitySniffFindings[i];
|
|
669
|
-
const category = finding.ruleId?.startsWith('auth') ? 'AUTH' : 'REALITY';
|
|
670
|
-
const normalized = normalizeFinding(finding, category, normalizedFindings.length, existingIDs);
|
|
671
|
-
normalizedFindings.push(normalized);
|
|
672
|
-
}
|
|
673
|
-
}
|
|
674
|
-
|
|
675
|
-
// Add detection engine findings (Dead UI, Billing, Fake Success)
|
|
676
|
-
for (const finding of detectionFindings) {
|
|
677
|
-
normalizedFindings.push(finding);
|
|
678
|
-
}
|
|
679
|
-
|
|
680
708
|
// Use new unified output if available, otherwise fallback to old format
|
|
681
709
|
if (useUnifiedOutput && buildVerdictOutput && normalizeFinding) {
|
|
682
710
|
// Normalize findings with stable IDs
|
|
@@ -778,7 +806,58 @@ async function runScan(args) {
|
|
|
778
806
|
});
|
|
779
807
|
|
|
780
808
|
return getExitCode(verdict);
|
|
781
|
-
}
|
|
809
|
+
} else {
|
|
810
|
+
// Legacy fallback output when unified output system isn't available
|
|
811
|
+
const findings = [...(report.shipBlockers || []), ...detectionFindings];
|
|
812
|
+
const criticalCount = findings.filter(f => f.severity === 'critical' || f.severity === 'BLOCK').length;
|
|
813
|
+
const warningCount = findings.filter(f => f.severity === 'warning' || f.severity === 'WARN').length;
|
|
814
|
+
|
|
815
|
+
const verdict = criticalCount > 0 ? 'BLOCK' : warningCount > 0 ? 'WARN' : 'SHIP';
|
|
816
|
+
|
|
817
|
+
// Print simple output
|
|
818
|
+
console.log();
|
|
819
|
+
console.log(` ${c.bold}═══════════════════════════════════════════════════════════════════${c.reset}`);
|
|
820
|
+
|
|
821
|
+
if (verdict === 'SHIP') {
|
|
822
|
+
console.log(` ${c.bgGreen}${c.white}${c.bold} ✓ SHIP ${c.reset} ${c.green}Ready to ship!${c.reset}`);
|
|
823
|
+
} else if (verdict === 'WARN') {
|
|
824
|
+
console.log(` ${c.bgYellow}${c.black}${c.bold} ⚠ WARN ${c.reset} ${c.yellow}Review recommended before shipping${c.reset}`);
|
|
825
|
+
} else {
|
|
826
|
+
console.log(` ${c.bgRed}${c.white}${c.bold} ✗ BLOCK ${c.reset} ${c.red}Issues must be fixed before shipping${c.reset}`);
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
console.log(` ${c.bold}═══════════════════════════════════════════════════════════════════${c.reset}`);
|
|
830
|
+
console.log();
|
|
831
|
+
|
|
832
|
+
if (findings.length > 0) {
|
|
833
|
+
console.log(` ${c.bold}Findings (${findings.length})${c.reset}`);
|
|
834
|
+
console.log();
|
|
835
|
+
|
|
836
|
+
for (const finding of findings.slice(0, 10)) {
|
|
837
|
+
const severityIcon = finding.severity === 'critical' || finding.severity === 'BLOCK'
|
|
838
|
+
? `${c.red}✗${c.reset}`
|
|
839
|
+
: `${c.yellow}⚠${c.reset}`;
|
|
840
|
+
console.log(` ${severityIcon} ${finding.title || finding.message}`);
|
|
841
|
+
if (finding.file) {
|
|
842
|
+
console.log(` ${c.dim}${finding.file}${finding.line ? `:${finding.line}` : ''}${c.reset}`);
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
if (findings.length > 10) {
|
|
847
|
+
console.log(` ${c.dim}... and ${findings.length - 10} more findings${c.reset}`);
|
|
848
|
+
}
|
|
849
|
+
console.log();
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
// Emit audit event
|
|
853
|
+
emitScanComplete(projectPath, verdict === 'SHIP' ? 'success' : 'failure', {
|
|
854
|
+
score: verdict === 'SHIP' ? 100 : verdict === 'WARN' ? 70 : 40,
|
|
855
|
+
issueCount: criticalCount + warningCount,
|
|
856
|
+
durationMs: timings.total,
|
|
857
|
+
});
|
|
858
|
+
|
|
859
|
+
return verdict === 'SHIP' ? 0 : verdict === 'WARN' ? 1 : 2;
|
|
860
|
+
}
|
|
782
861
|
|
|
783
862
|
} catch (error) {
|
|
784
863
|
stopSpinner(`Scan failed: ${error.message}`, false);
|