@vibecheckai/cli 3.1.8 → 3.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/registry.js +106 -116
- package/bin/runners/context/generators/mcp.js +18 -0
- package/bin/runners/context/index.js +72 -4
- package/bin/runners/context/proof-context.js +293 -1
- package/bin/runners/context/security-scanner.js +311 -73
- package/bin/runners/lib/analyzers.js +607 -20
- package/bin/runners/lib/detectors-v2.js +172 -15
- package/bin/runners/lib/entitlements-v2.js +48 -1
- package/bin/runners/lib/evidence-pack.js +678 -0
- package/bin/runners/lib/html-proof-report.js +913 -0
- package/bin/runners/lib/missions/plan.js +231 -41
- package/bin/runners/lib/missions/templates.js +125 -0
- package/bin/runners/lib/scan-output.js +492 -253
- package/bin/runners/lib/ship-output.js +901 -641
- package/bin/runners/runCheckpoint.js +44 -3
- package/bin/runners/runContext.d.ts +4 -0
- package/bin/runners/runDoctor.js +10 -2
- package/bin/runners/runFix.js +51 -341
- package/bin/runners/runInit.js +11 -0
- package/bin/runners/runPolish.d.ts +4 -0
- package/bin/runners/runPolish.js +608 -29
- package/bin/runners/runProve.js +210 -25
- package/bin/runners/runReality.js +846 -101
- package/bin/runners/runScan.js +238 -4
- package/bin/runners/runShip.js +19 -3
- package/bin/runners/runWatch.js +14 -1
- package/bin/vibecheck.js +32 -2
- package/mcp-server/consolidated-tools.js +408 -42
- package/mcp-server/index.js +152 -15
- package/mcp-server/proof-tools.js +571 -0
- package/mcp-server/tier-auth.js +22 -19
- package/mcp-server/tools-v3.js +744 -0
- package/mcp-server/truth-firewall-tools.js +190 -4
- package/package.json +3 -1
- package/bin/runners/runInstall.js +0 -281
- package/bin/runners/runLabs.js +0 -341
|
@@ -0,0 +1,744 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* vibecheck MCP Tools v3 - Consolidated 10 Tools
|
|
3
|
+
*
|
|
4
|
+
* ALL tools require STARTER tier or above.
|
|
5
|
+
* No free MCP tools - this is a premium feature.
|
|
6
|
+
*
|
|
7
|
+
* 10 TOOLS (STARTER+):
|
|
8
|
+
*
|
|
9
|
+
* CORE (4):
|
|
10
|
+
* 1. vibecheck.ship - Get ship verdict with evidence
|
|
11
|
+
* 2. vibecheck.scan - Deep scan for issues
|
|
12
|
+
* 3. vibecheck.fix - Generate/apply fixes
|
|
13
|
+
* 4. vibecheck.prove - Full proof loop (PRO)
|
|
14
|
+
*
|
|
15
|
+
* TRUTH (3):
|
|
16
|
+
* 5. vibecheck.truthpack - Get ground truth (routes, env, auth, billing)
|
|
17
|
+
* 6. vibecheck.validate - Validate a claim with evidence
|
|
18
|
+
* 7. vibecheck.search - Search codebase with evidence
|
|
19
|
+
*
|
|
20
|
+
* OUTPUT (2):
|
|
21
|
+
* 8. vibecheck.report - Generate reports (HTML, SARIF, etc.)
|
|
22
|
+
* 9. vibecheck.allowlist - Manage false positive allowlist
|
|
23
|
+
*
|
|
24
|
+
* UTILITY (1):
|
|
25
|
+
* 10. vibecheck.status - Health check and status
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
import fs from 'fs/promises';
|
|
29
|
+
import path from 'path';
|
|
30
|
+
import { execSync } from 'child_process';
|
|
31
|
+
|
|
32
|
+
// =============================================================================
|
|
33
|
+
// TIER ENFORCEMENT
|
|
34
|
+
// =============================================================================
|
|
35
|
+
|
|
36
|
+
const TOOL_TIERS = {
|
|
37
|
+
'vibecheck.ship': 'starter',
|
|
38
|
+
'vibecheck.scan': 'starter',
|
|
39
|
+
'vibecheck.fix': 'starter',
|
|
40
|
+
'vibecheck.prove': 'pro',
|
|
41
|
+
'vibecheck.truthpack': 'starter',
|
|
42
|
+
'vibecheck.validate': 'starter',
|
|
43
|
+
'vibecheck.search': 'starter',
|
|
44
|
+
'vibecheck.report': 'starter',
|
|
45
|
+
'vibecheck.allowlist': 'starter',
|
|
46
|
+
'vibecheck.status': 'starter',
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
function checkTierAccess(toolName, userTier) {
|
|
50
|
+
const requiredTier = TOOL_TIERS[toolName] || 'starter';
|
|
51
|
+
const tierOrder = { free: 0, starter: 1, pro: 2, compliance: 3 };
|
|
52
|
+
|
|
53
|
+
const userLevel = tierOrder[userTier] || 0;
|
|
54
|
+
const requiredLevel = tierOrder[requiredTier] || 1;
|
|
55
|
+
|
|
56
|
+
if (userLevel < requiredLevel) {
|
|
57
|
+
return {
|
|
58
|
+
allowed: false,
|
|
59
|
+
error: `🔒 ${toolName} requires ${requiredTier.toUpperCase()} tier. You have: ${userTier.toUpperCase()}. Upgrade at vibecheck.dev/pricing`
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return { allowed: true };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// =============================================================================
|
|
67
|
+
// TOOL DEFINITIONS (10 tools)
|
|
68
|
+
// =============================================================================
|
|
69
|
+
|
|
70
|
+
export const MCP_TOOLS_V3 = [
|
|
71
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
72
|
+
// CORE TOOLS (4)
|
|
73
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
74
|
+
|
|
75
|
+
{
|
|
76
|
+
name: "vibecheck.ship",
|
|
77
|
+
description: `🚀 Get ship verdict: SHIP | WARN | BLOCK
|
|
78
|
+
|
|
79
|
+
Returns a verdict with evidence-backed findings. Use this to check if code is ready to ship.
|
|
80
|
+
|
|
81
|
+
Returns:
|
|
82
|
+
- verdict: SHIP (ready) | WARN (review) | BLOCK (must fix)
|
|
83
|
+
- score: 0-100 confidence score
|
|
84
|
+
- findings: Issues found with file/line evidence
|
|
85
|
+
- aiHallucinationScore: Risk of AI-generated issues
|
|
86
|
+
|
|
87
|
+
[STARTER tier required]`,
|
|
88
|
+
inputSchema: {
|
|
89
|
+
type: "object",
|
|
90
|
+
properties: {
|
|
91
|
+
projectPath: {
|
|
92
|
+
type: "string",
|
|
93
|
+
description: "Project path (default: current directory)",
|
|
94
|
+
},
|
|
95
|
+
strict: {
|
|
96
|
+
type: "boolean",
|
|
97
|
+
description: "Treat warnings as blockers",
|
|
98
|
+
default: false,
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
|
|
104
|
+
{
|
|
105
|
+
name: "vibecheck.scan",
|
|
106
|
+
description: `🔍 Deep scan for ship-killers
|
|
107
|
+
|
|
108
|
+
Scans code for:
|
|
109
|
+
- Missing routes (client refs to non-existent endpoints)
|
|
110
|
+
- Env gaps (used but undeclared env vars)
|
|
111
|
+
- Fake success (success UI without verification)
|
|
112
|
+
- Ghost auth (unprotected sensitive endpoints)
|
|
113
|
+
- Dead UI (buttons that do nothing)
|
|
114
|
+
|
|
115
|
+
[STARTER tier required]`,
|
|
116
|
+
inputSchema: {
|
|
117
|
+
type: "object",
|
|
118
|
+
properties: {
|
|
119
|
+
projectPath: {
|
|
120
|
+
type: "string",
|
|
121
|
+
description: "Project path",
|
|
122
|
+
},
|
|
123
|
+
categories: {
|
|
124
|
+
type: "array",
|
|
125
|
+
items: { type: "string" },
|
|
126
|
+
description: "Categories to scan: routes, env, auth, billing, security",
|
|
127
|
+
},
|
|
128
|
+
},
|
|
129
|
+
},
|
|
130
|
+
},
|
|
131
|
+
|
|
132
|
+
{
|
|
133
|
+
name: "vibecheck.fix",
|
|
134
|
+
description: `🔧 Generate or apply fixes for findings
|
|
135
|
+
|
|
136
|
+
Modes:
|
|
137
|
+
- plan: Generate fix plan with AI missions (default)
|
|
138
|
+
- apply: Apply patches automatically (PRO)
|
|
139
|
+
- loop: Fix → verify → fix until SHIP (PRO)
|
|
140
|
+
|
|
141
|
+
[STARTER: plan mode | PRO: apply/loop modes]`,
|
|
142
|
+
inputSchema: {
|
|
143
|
+
type: "object",
|
|
144
|
+
properties: {
|
|
145
|
+
projectPath: {
|
|
146
|
+
type: "string",
|
|
147
|
+
description: "Project path",
|
|
148
|
+
},
|
|
149
|
+
mode: {
|
|
150
|
+
type: "string",
|
|
151
|
+
enum: ["plan", "apply", "loop"],
|
|
152
|
+
description: "Fix mode (default: plan)",
|
|
153
|
+
default: "plan",
|
|
154
|
+
},
|
|
155
|
+
findingIds: {
|
|
156
|
+
type: "array",
|
|
157
|
+
items: { type: "string" },
|
|
158
|
+
description: "Specific finding IDs to fix (optional)",
|
|
159
|
+
},
|
|
160
|
+
},
|
|
161
|
+
},
|
|
162
|
+
},
|
|
163
|
+
|
|
164
|
+
{
|
|
165
|
+
name: "vibecheck.prove",
|
|
166
|
+
description: `🔬 Full proof loop with runtime verification
|
|
167
|
+
|
|
168
|
+
Orchestrates: truthpack → reality → ship → fix
|
|
169
|
+
Continues until SHIP verdict or stuck.
|
|
170
|
+
|
|
171
|
+
Includes:
|
|
172
|
+
- Browser verification with Playwright
|
|
173
|
+
- Video/trace recording
|
|
174
|
+
- Auth boundary testing
|
|
175
|
+
- Evidence pack generation
|
|
176
|
+
|
|
177
|
+
[PRO tier required]`,
|
|
178
|
+
inputSchema: {
|
|
179
|
+
type: "object",
|
|
180
|
+
properties: {
|
|
181
|
+
projectPath: {
|
|
182
|
+
type: "string",
|
|
183
|
+
description: "Project path",
|
|
184
|
+
},
|
|
185
|
+
url: {
|
|
186
|
+
type: "string",
|
|
187
|
+
description: "Base URL for runtime testing (e.g., http://localhost:3000)",
|
|
188
|
+
},
|
|
189
|
+
maxIterations: {
|
|
190
|
+
type: "number",
|
|
191
|
+
description: "Max fix iterations (default: 5)",
|
|
192
|
+
default: 5,
|
|
193
|
+
},
|
|
194
|
+
recordVideo: {
|
|
195
|
+
type: "boolean",
|
|
196
|
+
description: "Record video of browser sessions",
|
|
197
|
+
default: true,
|
|
198
|
+
},
|
|
199
|
+
},
|
|
200
|
+
},
|
|
201
|
+
},
|
|
202
|
+
|
|
203
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
204
|
+
// TRUTH TOOLS (3)
|
|
205
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
206
|
+
|
|
207
|
+
{
|
|
208
|
+
name: "vibecheck.truthpack",
|
|
209
|
+
description: `📦 Get ground truth about the codebase
|
|
210
|
+
|
|
211
|
+
Returns verified facts about:
|
|
212
|
+
- routes: Server routes and client references
|
|
213
|
+
- env: Environment variables (used, declared, gaps)
|
|
214
|
+
- auth: Authentication model and protected routes
|
|
215
|
+
- billing: Payment gates and enforcement
|
|
216
|
+
|
|
217
|
+
Every fact has file/line citations and confidence scores.
|
|
218
|
+
|
|
219
|
+
⚠️ Use this BEFORE making assertions about the codebase.
|
|
220
|
+
|
|
221
|
+
[STARTER tier required]`,
|
|
222
|
+
inputSchema: {
|
|
223
|
+
type: "object",
|
|
224
|
+
properties: {
|
|
225
|
+
projectPath: {
|
|
226
|
+
type: "string",
|
|
227
|
+
description: "Project path",
|
|
228
|
+
},
|
|
229
|
+
scope: {
|
|
230
|
+
type: "string",
|
|
231
|
+
enum: ["all", "routes", "env", "auth", "billing"],
|
|
232
|
+
description: "What to include (default: all)",
|
|
233
|
+
default: "all",
|
|
234
|
+
},
|
|
235
|
+
refresh: {
|
|
236
|
+
type: "boolean",
|
|
237
|
+
description: "Force rebuild (default: use cache)",
|
|
238
|
+
default: false,
|
|
239
|
+
},
|
|
240
|
+
},
|
|
241
|
+
},
|
|
242
|
+
},
|
|
243
|
+
|
|
244
|
+
{
|
|
245
|
+
name: "vibecheck.validate",
|
|
246
|
+
description: `🔍 Validate a claim with evidence
|
|
247
|
+
|
|
248
|
+
Returns: true | false | unknown
|
|
249
|
+
- true: Claim verified with evidence citations
|
|
250
|
+
- false: Claim disproven with counterexamples
|
|
251
|
+
- unknown: Insufficient evidence (DO NOT proceed)
|
|
252
|
+
|
|
253
|
+
Claim types:
|
|
254
|
+
- route_exists: Check if route exists
|
|
255
|
+
- env_var_exists: Check if env var is declared
|
|
256
|
+
- auth_enforced: Check if route is protected
|
|
257
|
+
- file_exists: Check if file exists
|
|
258
|
+
- function_exists: Check if function exists
|
|
259
|
+
|
|
260
|
+
[STARTER tier required]`,
|
|
261
|
+
inputSchema: {
|
|
262
|
+
type: "object",
|
|
263
|
+
properties: {
|
|
264
|
+
claim: {
|
|
265
|
+
type: "string",
|
|
266
|
+
enum: ["route_exists", "env_var_exists", "auth_enforced", "file_exists", "function_exists"],
|
|
267
|
+
description: "Type of claim to validate",
|
|
268
|
+
},
|
|
269
|
+
subject: {
|
|
270
|
+
type: "object",
|
|
271
|
+
description: "What the claim is about",
|
|
272
|
+
properties: {
|
|
273
|
+
path: { type: "string", description: "Route path or file path" },
|
|
274
|
+
method: { type: "string", description: "HTTP method (for routes)" },
|
|
275
|
+
name: { type: "string", description: "Name of env var/function" },
|
|
276
|
+
},
|
|
277
|
+
},
|
|
278
|
+
projectPath: {
|
|
279
|
+
type: "string",
|
|
280
|
+
description: "Project path",
|
|
281
|
+
},
|
|
282
|
+
},
|
|
283
|
+
required: ["claim", "subject"],
|
|
284
|
+
},
|
|
285
|
+
},
|
|
286
|
+
|
|
287
|
+
{
|
|
288
|
+
name: "vibecheck.search",
|
|
289
|
+
description: `🔎 Search codebase with evidence citations
|
|
290
|
+
|
|
291
|
+
Searches code and returns results with file/line citations.
|
|
292
|
+
Use for finding specific patterns, functions, or implementations.
|
|
293
|
+
|
|
294
|
+
[STARTER tier required]`,
|
|
295
|
+
inputSchema: {
|
|
296
|
+
type: "object",
|
|
297
|
+
properties: {
|
|
298
|
+
query: {
|
|
299
|
+
type: "string",
|
|
300
|
+
description: "Search query (regex supported)",
|
|
301
|
+
},
|
|
302
|
+
projectPath: {
|
|
303
|
+
type: "string",
|
|
304
|
+
description: "Project path",
|
|
305
|
+
},
|
|
306
|
+
filePattern: {
|
|
307
|
+
type: "string",
|
|
308
|
+
description: "File glob pattern (e.g., '**/*.ts')",
|
|
309
|
+
},
|
|
310
|
+
maxResults: {
|
|
311
|
+
type: "number",
|
|
312
|
+
description: "Max results to return (default: 20)",
|
|
313
|
+
default: 20,
|
|
314
|
+
},
|
|
315
|
+
},
|
|
316
|
+
required: ["query"],
|
|
317
|
+
},
|
|
318
|
+
},
|
|
319
|
+
|
|
320
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
321
|
+
// OUTPUT TOOLS (2)
|
|
322
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
323
|
+
|
|
324
|
+
{
|
|
325
|
+
name: "vibecheck.report",
|
|
326
|
+
description: `📄 Generate reports in various formats
|
|
327
|
+
|
|
328
|
+
Formats:
|
|
329
|
+
- html: Interactive HTML report
|
|
330
|
+
- md: Markdown report
|
|
331
|
+
- sarif: SARIF for GitHub code scanning
|
|
332
|
+
- json: Machine-readable JSON
|
|
333
|
+
|
|
334
|
+
[STARTER tier required]`,
|
|
335
|
+
inputSchema: {
|
|
336
|
+
type: "object",
|
|
337
|
+
properties: {
|
|
338
|
+
projectPath: {
|
|
339
|
+
type: "string",
|
|
340
|
+
description: "Project path",
|
|
341
|
+
},
|
|
342
|
+
format: {
|
|
343
|
+
type: "string",
|
|
344
|
+
enum: ["html", "md", "sarif", "json"],
|
|
345
|
+
description: "Output format (default: html)",
|
|
346
|
+
default: "html",
|
|
347
|
+
},
|
|
348
|
+
outputPath: {
|
|
349
|
+
type: "string",
|
|
350
|
+
description: "Output file path (optional)",
|
|
351
|
+
},
|
|
352
|
+
},
|
|
353
|
+
},
|
|
354
|
+
},
|
|
355
|
+
|
|
356
|
+
{
|
|
357
|
+
name: "vibecheck.allowlist",
|
|
358
|
+
description: `📝 Manage false positive allowlist
|
|
359
|
+
|
|
360
|
+
Actions:
|
|
361
|
+
- list: Show all allowlist entries
|
|
362
|
+
- add: Add finding to allowlist
|
|
363
|
+
- remove: Remove from allowlist
|
|
364
|
+
- check: Check if finding is allowlisted
|
|
365
|
+
|
|
366
|
+
[STARTER tier required]`,
|
|
367
|
+
inputSchema: {
|
|
368
|
+
type: "object",
|
|
369
|
+
properties: {
|
|
370
|
+
action: {
|
|
371
|
+
type: "string",
|
|
372
|
+
enum: ["list", "add", "remove", "check"],
|
|
373
|
+
description: "Action to perform",
|
|
374
|
+
},
|
|
375
|
+
findingId: {
|
|
376
|
+
type: "string",
|
|
377
|
+
description: "Finding ID (for add/remove/check)",
|
|
378
|
+
},
|
|
379
|
+
reason: {
|
|
380
|
+
type: "string",
|
|
381
|
+
description: "Reason for allowlisting (for add)",
|
|
382
|
+
},
|
|
383
|
+
projectPath: {
|
|
384
|
+
type: "string",
|
|
385
|
+
description: "Project path",
|
|
386
|
+
},
|
|
387
|
+
},
|
|
388
|
+
required: ["action"],
|
|
389
|
+
},
|
|
390
|
+
},
|
|
391
|
+
|
|
392
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
393
|
+
// UTILITY TOOLS (1)
|
|
394
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
395
|
+
|
|
396
|
+
{
|
|
397
|
+
name: "vibecheck.status",
|
|
398
|
+
description: `📊 Get vibecheck status and health
|
|
399
|
+
|
|
400
|
+
Returns:
|
|
401
|
+
- version: CLI version
|
|
402
|
+
- lastVerdict: Last ship verdict
|
|
403
|
+
- findingsCount: Current findings
|
|
404
|
+
- truthpackAge: When truthpack was last built
|
|
405
|
+
- config: Project configuration
|
|
406
|
+
|
|
407
|
+
[STARTER tier required]`,
|
|
408
|
+
inputSchema: {
|
|
409
|
+
type: "object",
|
|
410
|
+
properties: {
|
|
411
|
+
projectPath: {
|
|
412
|
+
type: "string",
|
|
413
|
+
description: "Project path",
|
|
414
|
+
},
|
|
415
|
+
},
|
|
416
|
+
},
|
|
417
|
+
},
|
|
418
|
+
];
|
|
419
|
+
|
|
420
|
+
// =============================================================================
|
|
421
|
+
// TOOL HANDLERS
|
|
422
|
+
// =============================================================================
|
|
423
|
+
|
|
424
|
+
export async function handleToolV3(toolName, args, context = {}) {
|
|
425
|
+
const userTier = context.tier || 'free';
|
|
426
|
+
|
|
427
|
+
// Check tier access
|
|
428
|
+
const access = checkTierAccess(toolName, userTier);
|
|
429
|
+
if (!access.allowed) {
|
|
430
|
+
return { error: access.error, tier: userTier, required: TOOL_TIERS[toolName] };
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
const projectPath = args.projectPath || process.cwd();
|
|
434
|
+
|
|
435
|
+
try {
|
|
436
|
+
switch (toolName) {
|
|
437
|
+
case 'vibecheck.ship':
|
|
438
|
+
return await runShip(projectPath, args);
|
|
439
|
+
|
|
440
|
+
case 'vibecheck.scan':
|
|
441
|
+
return await runScan(projectPath, args);
|
|
442
|
+
|
|
443
|
+
case 'vibecheck.fix':
|
|
444
|
+
return await runFix(projectPath, args, userTier);
|
|
445
|
+
|
|
446
|
+
case 'vibecheck.prove':
|
|
447
|
+
return await runProve(projectPath, args);
|
|
448
|
+
|
|
449
|
+
case 'vibecheck.truthpack':
|
|
450
|
+
return await getTruthpack(projectPath, args);
|
|
451
|
+
|
|
452
|
+
case 'vibecheck.validate':
|
|
453
|
+
return await validateClaim(projectPath, args);
|
|
454
|
+
|
|
455
|
+
case 'vibecheck.search':
|
|
456
|
+
return await searchCode(projectPath, args);
|
|
457
|
+
|
|
458
|
+
case 'vibecheck.report':
|
|
459
|
+
return await generateReport(projectPath, args);
|
|
460
|
+
|
|
461
|
+
case 'vibecheck.allowlist':
|
|
462
|
+
return await manageAllowlist(projectPath, args);
|
|
463
|
+
|
|
464
|
+
case 'vibecheck.status':
|
|
465
|
+
return await getStatus(projectPath);
|
|
466
|
+
|
|
467
|
+
default:
|
|
468
|
+
return { error: `Unknown tool: ${toolName}` };
|
|
469
|
+
}
|
|
470
|
+
} catch (error) {
|
|
471
|
+
return { error: error.message, stack: error.stack };
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
// =============================================================================
|
|
476
|
+
// TOOL IMPLEMENTATIONS
|
|
477
|
+
// =============================================================================
|
|
478
|
+
|
|
479
|
+
async function runShip(projectPath, args) {
|
|
480
|
+
const strictFlag = args.strict ? '--strict' : '';
|
|
481
|
+
const result = execSync(
|
|
482
|
+
`node "${path.join(projectPath, 'node_modules/.bin/vibecheck')}" ship --json ${strictFlag}`,
|
|
483
|
+
{ cwd: projectPath, encoding: 'utf8', timeout: 120000 }
|
|
484
|
+
);
|
|
485
|
+
return JSON.parse(result);
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
async function runScan(projectPath, args) {
|
|
489
|
+
const result = execSync(
|
|
490
|
+
`node "${path.join(projectPath, 'node_modules/.bin/vibecheck')}" scan --json`,
|
|
491
|
+
{ cwd: projectPath, encoding: 'utf8', timeout: 120000 }
|
|
492
|
+
);
|
|
493
|
+
return JSON.parse(result);
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
async function runFix(projectPath, args, userTier) {
|
|
497
|
+
const mode = args.mode || 'plan';
|
|
498
|
+
|
|
499
|
+
// Apply/loop modes require PRO
|
|
500
|
+
if ((mode === 'apply' || mode === 'loop') && userTier !== 'pro' && userTier !== 'compliance') {
|
|
501
|
+
return { error: `🔒 fix --${mode} requires PRO tier. Use 'plan' mode for STARTER.` };
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
const modeFlag = mode === 'plan' ? '--prompt-only' : mode === 'loop' ? '--loop --apply' : '--apply';
|
|
505
|
+
const result = execSync(
|
|
506
|
+
`node "${path.join(projectPath, 'node_modules/.bin/vibecheck')}" fix ${modeFlag} --json`,
|
|
507
|
+
{ cwd: projectPath, encoding: 'utf8', timeout: 300000 }
|
|
508
|
+
);
|
|
509
|
+
return JSON.parse(result);
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
async function runProve(projectPath, args) {
|
|
513
|
+
const url = args.url || '';
|
|
514
|
+
const videoFlag = args.recordVideo ? '--video' : '';
|
|
515
|
+
const urlFlag = url ? `--url "${url}"` : '';
|
|
516
|
+
|
|
517
|
+
const result = execSync(
|
|
518
|
+
`node "${path.join(projectPath, 'node_modules/.bin/vibecheck')}" prove ${urlFlag} ${videoFlag} --json`,
|
|
519
|
+
{ cwd: projectPath, encoding: 'utf8', timeout: 600000 }
|
|
520
|
+
);
|
|
521
|
+
return JSON.parse(result);
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
async function getTruthpack(projectPath, args) {
|
|
525
|
+
const truthpackPath = path.join(projectPath, '.vibecheck', 'truthpack.json');
|
|
526
|
+
|
|
527
|
+
if (args.refresh) {
|
|
528
|
+
execSync(
|
|
529
|
+
`node "${path.join(projectPath, 'node_modules/.bin/vibecheck')}" ctx build`,
|
|
530
|
+
{ cwd: projectPath, encoding: 'utf8', timeout: 60000 }
|
|
531
|
+
);
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
try {
|
|
535
|
+
const content = await fs.readFile(truthpackPath, 'utf8');
|
|
536
|
+
const truthpack = JSON.parse(content);
|
|
537
|
+
|
|
538
|
+
// Filter by scope if specified
|
|
539
|
+
if (args.scope && args.scope !== 'all') {
|
|
540
|
+
return { [args.scope]: truthpack[args.scope] };
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
return truthpack;
|
|
544
|
+
} catch (error) {
|
|
545
|
+
return { error: 'Truthpack not found. Run vibecheck init first.' };
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
async function validateClaim(projectPath, args) {
|
|
550
|
+
const { claim, subject } = args;
|
|
551
|
+
const truthpack = await getTruthpack(projectPath, { scope: 'all' });
|
|
552
|
+
|
|
553
|
+
if (truthpack.error) return truthpack;
|
|
554
|
+
|
|
555
|
+
switch (claim) {
|
|
556
|
+
case 'route_exists': {
|
|
557
|
+
const routes = truthpack.routes?.server || [];
|
|
558
|
+
const found = routes.find(r =>
|
|
559
|
+
r.path === subject.path &&
|
|
560
|
+
(!subject.method || r.method === subject.method || r.method === '*')
|
|
561
|
+
);
|
|
562
|
+
return {
|
|
563
|
+
result: found ? 'true' : 'false',
|
|
564
|
+
evidence: found ? [{ file: found.file, line: found.line }] : [],
|
|
565
|
+
claim,
|
|
566
|
+
subject,
|
|
567
|
+
};
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
case 'env_var_exists': {
|
|
571
|
+
const declared = new Set(truthpack.env?.declared || []);
|
|
572
|
+
return {
|
|
573
|
+
result: declared.has(subject.name) ? 'true' : 'false',
|
|
574
|
+
evidence: [],
|
|
575
|
+
claim,
|
|
576
|
+
subject,
|
|
577
|
+
};
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
case 'auth_enforced': {
|
|
581
|
+
const authRoutes = truthpack.auth?.protectedRoutes || [];
|
|
582
|
+
const found = authRoutes.find(r => r.path === subject.path);
|
|
583
|
+
return {
|
|
584
|
+
result: found ? 'true' : 'unknown',
|
|
585
|
+
evidence: found ? [{ file: found.file, line: found.line }] : [],
|
|
586
|
+
claim,
|
|
587
|
+
subject,
|
|
588
|
+
};
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
case 'file_exists': {
|
|
592
|
+
try {
|
|
593
|
+
await fs.access(path.join(projectPath, subject.path));
|
|
594
|
+
return { result: 'true', evidence: [{ file: subject.path }], claim, subject };
|
|
595
|
+
} catch {
|
|
596
|
+
return { result: 'false', evidence: [], claim, subject };
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
case 'function_exists': {
|
|
601
|
+
// Simple grep-based search
|
|
602
|
+
try {
|
|
603
|
+
const result = execSync(
|
|
604
|
+
`grep -rn "function ${subject.name}\\|const ${subject.name}\\|${subject.name} =" --include="*.ts" --include="*.js" .`,
|
|
605
|
+
{ cwd: projectPath, encoding: 'utf8', timeout: 10000 }
|
|
606
|
+
);
|
|
607
|
+
const lines = result.trim().split('\n').filter(Boolean);
|
|
608
|
+
return {
|
|
609
|
+
result: lines.length > 0 ? 'true' : 'false',
|
|
610
|
+
evidence: lines.slice(0, 3).map(l => {
|
|
611
|
+
const [file, line] = l.split(':');
|
|
612
|
+
return { file, line: parseInt(line) };
|
|
613
|
+
}),
|
|
614
|
+
claim,
|
|
615
|
+
subject,
|
|
616
|
+
};
|
|
617
|
+
} catch {
|
|
618
|
+
return { result: 'false', evidence: [], claim, subject };
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
default:
|
|
623
|
+
return { result: 'unknown', error: `Unknown claim type: ${claim}` };
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
async function searchCode(projectPath, args) {
|
|
628
|
+
const { query, filePattern, maxResults = 20 } = args;
|
|
629
|
+
|
|
630
|
+
try {
|
|
631
|
+
const globFlag = filePattern ? `--include="${filePattern}"` : '--include="*.ts" --include="*.js" --include="*.tsx" --include="*.jsx"';
|
|
632
|
+
const result = execSync(
|
|
633
|
+
`grep -rn "${query}" ${globFlag} . | head -${maxResults}`,
|
|
634
|
+
{ cwd: projectPath, encoding: 'utf8', timeout: 30000 }
|
|
635
|
+
);
|
|
636
|
+
|
|
637
|
+
const matches = result.trim().split('\n').filter(Boolean).map(line => {
|
|
638
|
+
const colonIndex = line.indexOf(':');
|
|
639
|
+
const secondColon = line.indexOf(':', colonIndex + 1);
|
|
640
|
+
return {
|
|
641
|
+
file: line.substring(0, colonIndex),
|
|
642
|
+
line: parseInt(line.substring(colonIndex + 1, secondColon)),
|
|
643
|
+
content: line.substring(secondColon + 1).trim(),
|
|
644
|
+
};
|
|
645
|
+
});
|
|
646
|
+
|
|
647
|
+
return { query, matches, total: matches.length };
|
|
648
|
+
} catch (error) {
|
|
649
|
+
return { query, matches: [], total: 0, error: error.message };
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
async function generateReport(projectPath, args) {
|
|
654
|
+
const format = args.format || 'html';
|
|
655
|
+
const result = execSync(
|
|
656
|
+
`node "${path.join(projectPath, 'node_modules/.bin/vibecheck')}" report --format ${format} --json`,
|
|
657
|
+
{ cwd: projectPath, encoding: 'utf8', timeout: 60000 }
|
|
658
|
+
);
|
|
659
|
+
return JSON.parse(result);
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
async function manageAllowlist(projectPath, args) {
|
|
663
|
+
const { action, findingId, reason } = args;
|
|
664
|
+
|
|
665
|
+
switch (action) {
|
|
666
|
+
case 'list':
|
|
667
|
+
return execSync(
|
|
668
|
+
`node "${path.join(projectPath, 'node_modules/.bin/vibecheck')}" scan --allowlist list --json`,
|
|
669
|
+
{ cwd: projectPath, encoding: 'utf8', timeout: 30000 }
|
|
670
|
+
);
|
|
671
|
+
|
|
672
|
+
case 'add':
|
|
673
|
+
if (!findingId || !reason) {
|
|
674
|
+
return { error: 'findingId and reason required for add' };
|
|
675
|
+
}
|
|
676
|
+
return execSync(
|
|
677
|
+
`node "${path.join(projectPath, 'node_modules/.bin/vibecheck')}" scan --allowlist add --id "${findingId}" --reason "${reason}"`,
|
|
678
|
+
{ cwd: projectPath, encoding: 'utf8', timeout: 30000 }
|
|
679
|
+
);
|
|
680
|
+
|
|
681
|
+
case 'remove':
|
|
682
|
+
if (!findingId) {
|
|
683
|
+
return { error: 'findingId required for remove' };
|
|
684
|
+
}
|
|
685
|
+
return execSync(
|
|
686
|
+
`node "${path.join(projectPath, 'node_modules/.bin/vibecheck')}" scan --allowlist remove --id "${findingId}"`,
|
|
687
|
+
{ cwd: projectPath, encoding: 'utf8', timeout: 30000 }
|
|
688
|
+
);
|
|
689
|
+
|
|
690
|
+
case 'check':
|
|
691
|
+
if (!findingId) {
|
|
692
|
+
return { error: 'findingId required for check' };
|
|
693
|
+
}
|
|
694
|
+
return execSync(
|
|
695
|
+
`node "${path.join(projectPath, 'node_modules/.bin/vibecheck')}" scan --allowlist check --id "${findingId}" --json`,
|
|
696
|
+
{ cwd: projectPath, encoding: 'utf8', timeout: 30000 }
|
|
697
|
+
);
|
|
698
|
+
|
|
699
|
+
default:
|
|
700
|
+
return { error: `Unknown action: ${action}` };
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
async function getStatus(projectPath) {
|
|
705
|
+
const configPath = path.join(projectPath, '.vibecheck', 'config.json');
|
|
706
|
+
const truthpackPath = path.join(projectPath, '.vibecheck', 'truthpack.json');
|
|
707
|
+
const resultsPath = path.join(projectPath, '.vibecheck', 'results', 'latest.json');
|
|
708
|
+
|
|
709
|
+
const status = {
|
|
710
|
+
version: '3.2.0',
|
|
711
|
+
projectPath,
|
|
712
|
+
initialized: false,
|
|
713
|
+
lastVerdict: null,
|
|
714
|
+
findingsCount: 0,
|
|
715
|
+
truthpackAge: null,
|
|
716
|
+
};
|
|
717
|
+
|
|
718
|
+
try {
|
|
719
|
+
await fs.access(configPath);
|
|
720
|
+
status.initialized = true;
|
|
721
|
+
} catch {}
|
|
722
|
+
|
|
723
|
+
try {
|
|
724
|
+
const stats = await fs.stat(truthpackPath);
|
|
725
|
+
status.truthpackAge = stats.mtime.toISOString();
|
|
726
|
+
} catch {}
|
|
727
|
+
|
|
728
|
+
try {
|
|
729
|
+
const results = JSON.parse(await fs.readFile(resultsPath, 'utf8'));
|
|
730
|
+
status.lastVerdict = results.verdict;
|
|
731
|
+
status.findingsCount = results.findings?.length || 0;
|
|
732
|
+
} catch {}
|
|
733
|
+
|
|
734
|
+
return status;
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
export { TOOL_TIERS, checkTierAccess };
|
|
738
|
+
|
|
739
|
+
export default {
|
|
740
|
+
MCP_TOOLS_V3,
|
|
741
|
+
handleToolV3,
|
|
742
|
+
TOOL_TIERS,
|
|
743
|
+
checkTierAccess,
|
|
744
|
+
};
|