@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
|
@@ -1,2 +1,229 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* vibecheck ai-test - AI Agent for autonomous app testing
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const fs = require("fs");
|
|
6
|
+
const path = require("path");
|
|
7
|
+
|
|
8
|
+
const c = {
|
|
9
|
+
reset: "\x1b[0m",
|
|
10
|
+
bold: "\x1b[1m",
|
|
11
|
+
dim: "\x1b[2m",
|
|
12
|
+
green: "\x1b[32m",
|
|
13
|
+
yellow: "\x1b[33m",
|
|
14
|
+
red: "\x1b[31m",
|
|
15
|
+
cyan: "\x1b[36m",
|
|
16
|
+
magenta: "\x1b[35m",
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
function parseArgs(args) {
|
|
20
|
+
const opts = {
|
|
21
|
+
help: false,
|
|
22
|
+
url: null,
|
|
23
|
+
maxSteps: 20,
|
|
24
|
+
headed: false,
|
|
25
|
+
verbose: false,
|
|
26
|
+
json: false,
|
|
27
|
+
scenario: null,
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
for (let i = 0; i < args.length; i++) {
|
|
31
|
+
const arg = args[i];
|
|
32
|
+
switch (arg) {
|
|
33
|
+
case "--help":
|
|
34
|
+
case "-h":
|
|
35
|
+
opts.help = true;
|
|
36
|
+
break;
|
|
37
|
+
case "--url":
|
|
38
|
+
case "-u":
|
|
39
|
+
opts.url = args[++i];
|
|
40
|
+
break;
|
|
41
|
+
case "--max-steps":
|
|
42
|
+
opts.maxSteps = parseInt(args[++i]);
|
|
43
|
+
break;
|
|
44
|
+
case "--headed":
|
|
45
|
+
opts.headed = true;
|
|
46
|
+
break;
|
|
47
|
+
case "--verbose":
|
|
48
|
+
case "-v":
|
|
49
|
+
opts.verbose = true;
|
|
50
|
+
break;
|
|
51
|
+
case "--json":
|
|
52
|
+
opts.json = true;
|
|
53
|
+
break;
|
|
54
|
+
case "--scenario":
|
|
55
|
+
case "-s":
|
|
56
|
+
opts.scenario = args[++i];
|
|
57
|
+
break;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return opts;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function printHelp() {
|
|
65
|
+
console.log(`
|
|
66
|
+
${c.bold}vibecheck ai-test${c.reset} - AI Agent for autonomous app testing
|
|
67
|
+
|
|
68
|
+
${c.bold}USAGE${c.reset}
|
|
69
|
+
vibecheck ai-test --url <url> [options]
|
|
70
|
+
|
|
71
|
+
${c.bold}OPTIONS${c.reset}
|
|
72
|
+
--help, -h Show this help
|
|
73
|
+
--url, -u <url> Target URL (required)
|
|
74
|
+
--max-steps <n> Maximum exploration steps (default: 20)
|
|
75
|
+
--headed Run browser in headed mode
|
|
76
|
+
--verbose, -v Show detailed output
|
|
77
|
+
--json Output results as JSON
|
|
78
|
+
--scenario, -s <s> Test scenario (login, checkout, signup)
|
|
79
|
+
|
|
80
|
+
${c.bold}SCENARIOS${c.reset}
|
|
81
|
+
login Test login flow
|
|
82
|
+
checkout Test checkout/payment flow
|
|
83
|
+
signup Test user registration
|
|
84
|
+
explore General exploration (default)
|
|
85
|
+
|
|
86
|
+
${c.bold}EXAMPLES${c.reset}
|
|
87
|
+
vibecheck ai-test --url http://localhost:3000
|
|
88
|
+
vibecheck ai-test --url http://localhost:3000 --scenario login
|
|
89
|
+
vibecheck ai-test --url http://localhost:3000 --headed --verbose
|
|
90
|
+
`);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
async function runAIAgent(args) {
|
|
94
|
+
const opts = parseArgs(args);
|
|
95
|
+
|
|
96
|
+
if (opts.help) {
|
|
97
|
+
printHelp();
|
|
98
|
+
return 0;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (!opts.url) {
|
|
102
|
+
console.log(`${c.red}✗${c.reset} --url is required\n`);
|
|
103
|
+
console.log(`${c.dim}Example: vibecheck ai-test --url http://localhost:3000${c.reset}\n`);
|
|
104
|
+
return 1;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
console.log(`
|
|
108
|
+
${c.bold}${c.magenta}🤖 AI AGENT TEST${c.reset}
|
|
109
|
+
${c.dim}─────────────────────────────────────────${c.reset}
|
|
110
|
+
Target: ${c.cyan}${opts.url}${c.reset}
|
|
111
|
+
Scenario: ${opts.scenario || "explore"}
|
|
112
|
+
Max steps: ${opts.maxSteps}
|
|
113
|
+
`);
|
|
114
|
+
|
|
115
|
+
// Check for Playwright
|
|
116
|
+
let playwright;
|
|
117
|
+
try {
|
|
118
|
+
playwright = require("playwright");
|
|
119
|
+
} catch (e) {
|
|
120
|
+
console.log(`${c.yellow}⚠${c.reset} Playwright not installed`);
|
|
121
|
+
console.log(`${c.dim} Run: npm install -D playwright${c.reset}\n`);
|
|
122
|
+
return 1;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const findings = [];
|
|
126
|
+
const visited = new Set();
|
|
127
|
+
const actions = [];
|
|
128
|
+
|
|
129
|
+
try {
|
|
130
|
+
const browser = await playwright.chromium.launch({ headless: !opts.headed });
|
|
131
|
+
const context = await browser.newContext();
|
|
132
|
+
const page = await context.newPage();
|
|
133
|
+
|
|
134
|
+
// Track console errors
|
|
135
|
+
page.on("console", (msg) => {
|
|
136
|
+
if (msg.type() === "error") {
|
|
137
|
+
findings.push({
|
|
138
|
+
type: "console_error",
|
|
139
|
+
message: msg.text(),
|
|
140
|
+
url: page.url(),
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
// Track network errors
|
|
146
|
+
page.on("requestfailed", (request) => {
|
|
147
|
+
findings.push({
|
|
148
|
+
type: "network_error",
|
|
149
|
+
url: request.url(),
|
|
150
|
+
failure: request.failure()?.errorText,
|
|
151
|
+
});
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
console.log(`${c.cyan}▸${c.reset} Navigating to ${opts.url}...`);
|
|
155
|
+
await page.goto(opts.url, { waitUntil: "networkidle" });
|
|
156
|
+
visited.add(opts.url);
|
|
157
|
+
actions.push({ type: "navigate", url: opts.url });
|
|
158
|
+
|
|
159
|
+
// Simple exploration loop
|
|
160
|
+
for (let step = 0; step < opts.maxSteps; step++) {
|
|
161
|
+
if (opts.verbose) {
|
|
162
|
+
console.log(`${c.dim} Step ${step + 1}/${opts.maxSteps}${c.reset}`);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Find clickable elements
|
|
166
|
+
const buttons = await page.$$("button:visible, a:visible, [role='button']:visible");
|
|
167
|
+
|
|
168
|
+
if (buttons.length === 0) break;
|
|
169
|
+
|
|
170
|
+
// Click a random button
|
|
171
|
+
const randomBtn = buttons[Math.floor(Math.random() * buttons.length)];
|
|
172
|
+
try {
|
|
173
|
+
const text = await randomBtn.textContent();
|
|
174
|
+
await randomBtn.click({ timeout: 3000 });
|
|
175
|
+
actions.push({ type: "click", text: text?.trim().slice(0, 50) });
|
|
176
|
+
|
|
177
|
+
if (opts.verbose) {
|
|
178
|
+
console.log(`${c.green}✓${c.reset} Clicked: ${text?.trim().slice(0, 30)}`);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
await page.waitForLoadState("networkidle", { timeout: 5000 }).catch(() => {});
|
|
182
|
+
|
|
183
|
+
const currentUrl = page.url();
|
|
184
|
+
if (!visited.has(currentUrl)) {
|
|
185
|
+
visited.add(currentUrl);
|
|
186
|
+
if (opts.verbose) {
|
|
187
|
+
console.log(`${c.cyan}→${c.reset} New page: ${currentUrl}`);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
} catch (e) {
|
|
191
|
+
// Element may have disappeared
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
await browser.close();
|
|
196
|
+
|
|
197
|
+
// Report
|
|
198
|
+
console.log(`
|
|
199
|
+
${c.bold}─────────────────────────────────────────${c.reset}
|
|
200
|
+
${c.bold}RESULTS${c.reset}
|
|
201
|
+
Pages visited: ${visited.size}
|
|
202
|
+
Actions taken: ${actions.length}
|
|
203
|
+
Issues found: ${findings.length}
|
|
204
|
+
`);
|
|
205
|
+
|
|
206
|
+
if (findings.length > 0) {
|
|
207
|
+
console.log(`${c.bold}Issues:${c.reset}`);
|
|
208
|
+
for (const f of findings.slice(0, 10)) {
|
|
209
|
+
console.log(` ${c.red}✗${c.reset} [${f.type}] ${f.message || f.url}`);
|
|
210
|
+
}
|
|
211
|
+
if (findings.length > 10) {
|
|
212
|
+
console.log(` ${c.dim}... and ${findings.length - 10} more${c.reset}`);
|
|
213
|
+
}
|
|
214
|
+
console.log();
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
if (opts.json) {
|
|
218
|
+
console.log(JSON.stringify({ visited: [...visited], actions, findings }, null, 2));
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
return findings.length > 0 ? 1 : 0;
|
|
222
|
+
|
|
223
|
+
} catch (error) {
|
|
224
|
+
console.error(`${c.red}✗${c.reset} AI Agent failed: ${error.message}`);
|
|
225
|
+
return 1;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
2
229
|
module.exports = { runAIAgent };
|
package/bin/runners/runBadge.js
CHANGED
|
@@ -1,2 +1,182 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* vibecheck badge - Generate ship badges for your project
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const fs = require("fs");
|
|
6
|
+
const path = require("path");
|
|
7
|
+
|
|
8
|
+
const c = {
|
|
9
|
+
reset: "\x1b[0m",
|
|
10
|
+
bold: "\x1b[1m",
|
|
11
|
+
dim: "\x1b[2m",
|
|
12
|
+
green: "\x1b[32m",
|
|
13
|
+
yellow: "\x1b[33m",
|
|
14
|
+
red: "\x1b[31m",
|
|
15
|
+
cyan: "\x1b[36m",
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
function parseArgs(args) {
|
|
19
|
+
const opts = {
|
|
20
|
+
help: false,
|
|
21
|
+
output: null,
|
|
22
|
+
format: "markdown",
|
|
23
|
+
verdict: null,
|
|
24
|
+
score: null,
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
for (let i = 0; i < args.length; i++) {
|
|
28
|
+
const arg = args[i];
|
|
29
|
+
switch (arg) {
|
|
30
|
+
case "--help":
|
|
31
|
+
case "-h":
|
|
32
|
+
opts.help = true;
|
|
33
|
+
break;
|
|
34
|
+
case "--output":
|
|
35
|
+
case "-o":
|
|
36
|
+
opts.output = args[++i];
|
|
37
|
+
break;
|
|
38
|
+
case "--format":
|
|
39
|
+
case "-f":
|
|
40
|
+
opts.format = args[++i];
|
|
41
|
+
break;
|
|
42
|
+
case "--verdict":
|
|
43
|
+
opts.verdict = args[++i];
|
|
44
|
+
break;
|
|
45
|
+
case "--score":
|
|
46
|
+
opts.score = parseInt(args[++i]);
|
|
47
|
+
break;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return opts;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function printHelp() {
|
|
55
|
+
console.log(`
|
|
56
|
+
${c.bold}vibecheck badge${c.reset} - Generate ship badges for your project
|
|
57
|
+
|
|
58
|
+
${c.bold}USAGE${c.reset}
|
|
59
|
+
vibecheck badge [options]
|
|
60
|
+
|
|
61
|
+
${c.bold}OPTIONS${c.reset}
|
|
62
|
+
--help, -h Show this help
|
|
63
|
+
--output, -o <file> Output file (default: stdout)
|
|
64
|
+
--format, -f <fmt> Format: markdown, html, svg (default: markdown)
|
|
65
|
+
--verdict <v> Override verdict: SHIP, WARN, BLOCK
|
|
66
|
+
--score <n> Override score (0-100)
|
|
67
|
+
|
|
68
|
+
${c.bold}EXAMPLES${c.reset}
|
|
69
|
+
vibecheck badge # Generate from last scan
|
|
70
|
+
vibecheck badge -o README.md # Append to README
|
|
71
|
+
vibecheck badge --format svg # SVG badge
|
|
72
|
+
vibecheck badge --verdict SHIP # Force SHIP badge
|
|
73
|
+
`);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function getVerdictColor(verdict) {
|
|
77
|
+
switch (verdict) {
|
|
78
|
+
case "SHIP": return "brightgreen";
|
|
79
|
+
case "WARN": return "yellow";
|
|
80
|
+
case "BLOCK": return "red";
|
|
81
|
+
default: return "lightgrey";
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function generateMarkdownBadge(verdict, score) {
|
|
86
|
+
const color = getVerdictColor(verdict);
|
|
87
|
+
const label = "vibecheck";
|
|
88
|
+
const message = verdict === "SHIP" ? `${verdict}%20✓` : verdict;
|
|
89
|
+
return `[](https://vibecheckai.dev)`;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function generateHtmlBadge(verdict, score) {
|
|
93
|
+
const color = verdict === "SHIP" ? "#4CAF50" : verdict === "WARN" ? "#FFC107" : "#F44336";
|
|
94
|
+
return `<a href="https://vibecheckai.dev"><img src="https://img.shields.io/badge/vibecheck-${verdict}-${color.replace("#", "")}" alt="vibecheck ${verdict}"></a>`;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function generateSvgBadge(verdict, score) {
|
|
98
|
+
const color = verdict === "SHIP" ? "#4CAF50" : verdict === "WARN" ? "#FFC107" : "#F44336";
|
|
99
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" width="100" height="20">
|
|
100
|
+
<linearGradient id="b" x2="0" y2="100%">
|
|
101
|
+
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
|
|
102
|
+
<stop offset="1" stop-opacity=".1"/>
|
|
103
|
+
</linearGradient>
|
|
104
|
+
<mask id="a"><rect width="100" height="20" rx="3" fill="#fff"/></mask>
|
|
105
|
+
<g mask="url(#a)">
|
|
106
|
+
<rect width="60" height="20" fill="#555"/>
|
|
107
|
+
<rect x="60" width="40" height="20" fill="${color}"/>
|
|
108
|
+
<rect width="100" height="20" fill="url(#b)"/>
|
|
109
|
+
</g>
|
|
110
|
+
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
|
|
111
|
+
<text x="30" y="15" fill="#010101" fill-opacity=".3">vibecheck</text>
|
|
112
|
+
<text x="30" y="14">vibecheck</text>
|
|
113
|
+
<text x="80" y="15" fill="#010101" fill-opacity=".3">${verdict}</text>
|
|
114
|
+
<text x="80" y="14">${verdict}</text>
|
|
115
|
+
</g>
|
|
116
|
+
</svg>`;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
async function runBadge(args) {
|
|
120
|
+
const opts = parseArgs(args);
|
|
121
|
+
|
|
122
|
+
if (opts.help) {
|
|
123
|
+
printHelp();
|
|
124
|
+
return 0;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Try to load last scan result
|
|
128
|
+
let verdict = opts.verdict || "SHIP";
|
|
129
|
+
let score = opts.score || 100;
|
|
130
|
+
|
|
131
|
+
const lastScanPath = path.join(process.cwd(), ".vibecheck", "last_scan.json");
|
|
132
|
+
if (!opts.verdict && fs.existsSync(lastScanPath)) {
|
|
133
|
+
try {
|
|
134
|
+
const lastScan = JSON.parse(fs.readFileSync(lastScanPath, "utf8"));
|
|
135
|
+
verdict = lastScan.verdict || "SHIP";
|
|
136
|
+
score = lastScan.score || 100;
|
|
137
|
+
} catch (e) {
|
|
138
|
+
// Use defaults
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Generate badge
|
|
143
|
+
let badge;
|
|
144
|
+
switch (opts.format) {
|
|
145
|
+
case "html":
|
|
146
|
+
badge = generateHtmlBadge(verdict, score);
|
|
147
|
+
break;
|
|
148
|
+
case "svg":
|
|
149
|
+
badge = generateSvgBadge(verdict, score);
|
|
150
|
+
break;
|
|
151
|
+
default:
|
|
152
|
+
badge = generateMarkdownBadge(verdict, score);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Output
|
|
156
|
+
if (opts.output) {
|
|
157
|
+
const outputPath = path.resolve(opts.output);
|
|
158
|
+
if (fs.existsSync(outputPath)) {
|
|
159
|
+
const content = fs.readFileSync(outputPath, "utf8");
|
|
160
|
+
// Replace existing badge or append
|
|
161
|
+
if (content.includes("vibecheck")) {
|
|
162
|
+
const updated = content.replace(/\[!\[vibecheck\].*?\]\(https:\/\/vibecheckai\.dev\)/g, badge);
|
|
163
|
+
fs.writeFileSync(outputPath, updated);
|
|
164
|
+
console.log(`${c.green}✓${c.reset} Updated badge in ${outputPath}`);
|
|
165
|
+
} else {
|
|
166
|
+
fs.writeFileSync(outputPath, content + "\n\n" + badge + "\n");
|
|
167
|
+
console.log(`${c.green}✓${c.reset} Added badge to ${outputPath}`);
|
|
168
|
+
}
|
|
169
|
+
} else {
|
|
170
|
+
fs.writeFileSync(outputPath, badge + "\n");
|
|
171
|
+
console.log(`${c.green}✓${c.reset} Created ${outputPath} with badge`);
|
|
172
|
+
}
|
|
173
|
+
} else {
|
|
174
|
+
console.log(`\n${c.bold}Generated Badge:${c.reset}\n`);
|
|
175
|
+
console.log(badge);
|
|
176
|
+
console.log(`\n${c.dim}Copy and paste into your README.md${c.reset}\n`);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return 0;
|
|
180
|
+
}
|
|
181
|
+
|
|
2
182
|
module.exports = { runBadge };
|
package/bin/runners/runCtx.js
CHANGED
|
@@ -131,9 +131,14 @@ async function runCtx(args) {
|
|
|
131
131
|
const nextRoutes = (truthpack.routes?.server || []).filter(r =>
|
|
132
132
|
r.handler?.includes("app/api") || r.handler?.includes("pages/api")
|
|
133
133
|
).length;
|
|
134
|
-
const
|
|
134
|
+
const otherRoutes = (truthpack.routes?.server || []).filter(r =>
|
|
135
135
|
!r.handler?.includes("app/api") && !r.handler?.includes("pages/api")
|
|
136
136
|
).length;
|
|
137
|
+
|
|
138
|
+
// Detect framework from routes evidence or truthpack meta
|
|
139
|
+
const detectedFramework = truthpack.meta?.framework ||
|
|
140
|
+
(truthpack.routes?.server || []).find(r => r.evidence?.[0]?.reason)?.evidence?.[0]?.reason?.split(' ')[0] ||
|
|
141
|
+
'Express';
|
|
137
142
|
const clientRefs = truthpack.routes?.clientRefs?.length || 0;
|
|
138
143
|
const gaps = truthpack.routes?.gaps?.length || 0;
|
|
139
144
|
|
|
@@ -153,7 +158,7 @@ async function runCtx(args) {
|
|
|
153
158
|
console.log('');
|
|
154
159
|
console.log(`${c.cyan}┌─ Routes ──────────────────────────────────────────────────────────────┐${c.reset}`);
|
|
155
160
|
console.log(`${c.cyan}│${c.reset} ${c.bold}▲ Next.js:${c.reset} ${nextRoutes} routes`);
|
|
156
|
-
console.log(`${c.cyan}│${c.reset} ${c.bold}⚡
|
|
161
|
+
console.log(`${c.cyan}│${c.reset} ${c.bold}⚡ ${detectedFramework}:${c.reset} ${otherRoutes} routes`);
|
|
157
162
|
console.log(`${c.cyan}│${c.reset} ${c.dim}Client refs:${c.reset} ${clientRefs} ${gaps > 0 ? `${c.yellow}⚠ ${gaps} gaps${c.reset}` : `${c.green}✓ no gaps${c.reset}`}`);
|
|
158
163
|
console.log(`${c.cyan}└───────────────────────────────────────────────────────────────────────┘${c.reset}`);
|
|
159
164
|
|