@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.
Files changed (108) hide show
  1. package/bin/dev/run-v2-torture.js +30 -0
  2. package/bin/runners/context/index.js +1 -1
  3. package/bin/runners/lib/analyzers.js +38 -0
  4. package/bin/runners/lib/assets/vibecheck-logo.png +0 -0
  5. package/bin/runners/lib/contracts/auth-contract.js +8 -0
  6. package/bin/runners/lib/contracts/env-contract.js +3 -0
  7. package/bin/runners/lib/contracts/external-contract.js +10 -2
  8. package/bin/runners/lib/contracts/route-contract.js +7 -0
  9. package/bin/runners/lib/contracts.js +804 -0
  10. package/bin/runners/lib/detectors-v2.js +703 -0
  11. package/bin/runners/lib/drift.js +425 -0
  12. package/bin/runners/lib/entitlements-v2.js +3 -1
  13. package/bin/runners/lib/entitlements.js +11 -3
  14. package/bin/runners/lib/env-resolver.js +417 -0
  15. package/bin/runners/lib/extractors/client-calls.js +990 -0
  16. package/bin/runners/lib/extractors/fastify-route-dump.js +573 -0
  17. package/bin/runners/lib/extractors/fastify-routes.js +426 -0
  18. package/bin/runners/lib/extractors/index.js +363 -0
  19. package/bin/runners/lib/extractors/next-routes.js +524 -0
  20. package/bin/runners/lib/extractors/proof-graph.js +431 -0
  21. package/bin/runners/lib/extractors/route-matcher.js +451 -0
  22. package/bin/runners/lib/extractors/truthpack-v2.js +377 -0
  23. package/bin/runners/lib/extractors/ui-bindings.js +547 -0
  24. package/bin/runners/lib/findings-schema.js +281 -0
  25. package/bin/runners/lib/html-report.js +650 -0
  26. package/bin/runners/lib/missions/templates.js +45 -0
  27. package/bin/runners/lib/policy.js +295 -0
  28. package/bin/runners/lib/reality/correlation-detectors.js +359 -0
  29. package/bin/runners/lib/reality/index.js +318 -0
  30. package/bin/runners/lib/reality/request-hashing.js +416 -0
  31. package/bin/runners/lib/reality/request-mapper.js +453 -0
  32. package/bin/runners/lib/reality/safety-rails.js +463 -0
  33. package/bin/runners/lib/reality/semantic-snapshot.js +408 -0
  34. package/bin/runners/lib/reality/toast-detector.js +393 -0
  35. package/bin/runners/lib/report-html.js +5 -0
  36. package/bin/runners/lib/report-templates.js +5 -0
  37. package/bin/runners/lib/report.js +135 -0
  38. package/bin/runners/lib/route-truth.js +10 -10
  39. package/bin/runners/lib/schema-validator.js +350 -0
  40. package/bin/runners/lib/schemas/contracts.schema.json +160 -0
  41. package/bin/runners/lib/schemas/finding.schema.json +100 -0
  42. package/bin/runners/lib/schemas/mission-pack.schema.json +206 -0
  43. package/bin/runners/lib/schemas/proof-graph.schema.json +176 -0
  44. package/bin/runners/lib/schemas/reality-report.schema.json +162 -0
  45. package/bin/runners/lib/schemas/share-pack.schema.json +180 -0
  46. package/bin/runners/lib/schemas/ship-report.schema.json +117 -0
  47. package/bin/runners/lib/schemas/truthpack-v2.schema.json +303 -0
  48. package/bin/runners/lib/schemas/validator.js +438 -0
  49. package/bin/runners/lib/ui.js +562 -0
  50. package/bin/runners/lib/verdict-engine.js +628 -0
  51. package/bin/runners/runAIAgent.js +228 -1
  52. package/bin/runners/runBadge.js +181 -1
  53. package/bin/runners/runCtx.js +7 -2
  54. package/bin/runners/runCtxDiff.js +301 -0
  55. package/bin/runners/runGuard.js +168 -0
  56. package/bin/runners/runInitGha.js +78 -15
  57. package/bin/runners/runLabs.js +341 -0
  58. package/bin/runners/runLaunch.js +180 -1
  59. package/bin/runners/runMdc.js +203 -1
  60. package/bin/runners/runProof.zip +0 -0
  61. package/bin/runners/runProve.js +23 -0
  62. package/bin/runners/runReplay.js +114 -84
  63. package/bin/runners/runScan.js +111 -32
  64. package/bin/runners/runShip.js +23 -2
  65. package/bin/runners/runTruthpack.js +9 -7
  66. package/bin/runners/runValidate.js +161 -1
  67. package/bin/vibecheck.js +416 -770
  68. package/mcp-server/.guardrail/audit/audit.log.jsonl +2 -0
  69. package/mcp-server/.specs/architecture.mdc +90 -0
  70. package/mcp-server/.specs/security.mdc +30 -0
  71. package/mcp-server/README.md +252 -0
  72. package/mcp-server/agent-checkpoint.js +364 -0
  73. package/mcp-server/architect-tools.js +707 -0
  74. package/mcp-server/audit-mcp.js +206 -0
  75. package/mcp-server/codebase-architect-tools.js +838 -0
  76. package/mcp-server/consolidated-tools.js +804 -0
  77. package/mcp-server/hygiene-tools.js +428 -0
  78. package/mcp-server/index-v1.js +698 -0
  79. package/mcp-server/index.js +2092 -0
  80. package/mcp-server/index.old.js +4137 -0
  81. package/mcp-server/intelligence-tools.js +664 -0
  82. package/mcp-server/intent-drift-tools.js +873 -0
  83. package/mcp-server/mdc-generator.js +298 -0
  84. package/mcp-server/package-lock.json +165 -0
  85. package/mcp-server/package.json +47 -0
  86. package/mcp-server/premium-tools.js +1275 -0
  87. package/mcp-server/test-mcp.js +108 -0
  88. package/mcp-server/test-tools.js +36 -0
  89. package/mcp-server/tier-auth.js +147 -0
  90. package/mcp-server/tools/index.js +72 -0
  91. package/mcp-server/tools-reorganized.ts +244 -0
  92. package/mcp-server/truth-context.js +581 -0
  93. package/mcp-server/truth-firewall-tools.js +1500 -0
  94. package/mcp-server/vibecheck-2.0-tools.js +748 -0
  95. package/mcp-server/vibecheck-tools.js +1075 -0
  96. package/package.json +10 -8
  97. package/bin/guardrail.js +0 -834
  98. package/bin/runners/runAudit.js +0 -2
  99. package/bin/runners/runAutopilot.js +0 -2
  100. package/bin/runners/runCertify.js +0 -2
  101. package/bin/runners/runDashboard.js +0 -10
  102. package/bin/runners/runEnhancedShip.js +0 -2
  103. package/bin/runners/runFixPacks.js +0 -2
  104. package/bin/runners/runNaturalLanguage.js +0 -3
  105. package/bin/runners/runProof.js +0 -2
  106. package/bin/runners/runRealitySniff.js +0 -2
  107. package/bin/runners/runUpgrade.js +0 -2
  108. package/bin/runners/runVerifyAgentOutput.js +0 -2
@@ -1,2 +1,229 @@
1
- async function runAIAgent(args) { console.log("AI Agent not yet implemented"); return 0; }
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 };
@@ -1,2 +1,182 @@
1
- async function runBadge(args) { console.log("Badge generator not yet implemented"); return 0; }
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 `[![vibecheck](https://img.shields.io/badge/${label}-${message}-${color})](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 };
@@ -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 fastifyRoutes = (truthpack.routes?.server || []).filter(r =>
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}⚡ Fastify:${c.reset} ${fastifyRoutes} routes`);
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