@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,571 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vibecheck MCP Proof Tools
|
|
3
|
+
*
|
|
4
|
+
* Proof-specific tools for AI assistants:
|
|
5
|
+
* - vibecheck.prove - Run full proof loop
|
|
6
|
+
* - vibecheck.prove_status - Get current proof status
|
|
7
|
+
* - vibecheck.get_evidence - Get evidence for specific finding
|
|
8
|
+
* - vibecheck.check_flaky - Check if a finding is flaky
|
|
9
|
+
* - vibecheck.allowlist_add - Add finding to allowlist with reason
|
|
10
|
+
* - vibecheck.get_proof_graph - Get visual proof graph
|
|
11
|
+
* - vibecheck.evidence_pack - Generate or retrieve evidence pack
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { execSync, spawn } from 'child_process';
|
|
15
|
+
import fs from 'fs';
|
|
16
|
+
import path from 'path';
|
|
17
|
+
|
|
18
|
+
// ============================================================================
|
|
19
|
+
// PROOF TOOL DEFINITIONS
|
|
20
|
+
// ============================================================================
|
|
21
|
+
|
|
22
|
+
export const PROOF_TOOLS = [
|
|
23
|
+
{
|
|
24
|
+
name: "vibecheck.prove",
|
|
25
|
+
description: "Run full proof loop: ctx → reality → ship → fix. Returns undeniable evidence that your app works. Use AFTER making changes to verify they're real.",
|
|
26
|
+
inputSchema: {
|
|
27
|
+
type: "object",
|
|
28
|
+
properties: {
|
|
29
|
+
url: { type: "string", description: "Base URL to test (e.g., http://localhost:3000)" },
|
|
30
|
+
projectPath: { type: "string", description: "Path to project root", default: "." },
|
|
31
|
+
auth: { type: "string", description: "Login credentials as email:password" },
|
|
32
|
+
skipFix: { type: "boolean", description: "Don't auto-fix, just diagnose", default: false },
|
|
33
|
+
stabilityRuns: { type: "number", description: "Number of runs for flakiness detection", default: 1 },
|
|
34
|
+
evidencePack: { type: "boolean", description: "Generate evidence pack", default: true }
|
|
35
|
+
},
|
|
36
|
+
required: ["url"]
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
name: "vibecheck.prove_status",
|
|
41
|
+
description: "Get the status and results of the last proof run. Returns verdict, findings, and artifact paths.",
|
|
42
|
+
inputSchema: {
|
|
43
|
+
type: "object",
|
|
44
|
+
properties: {
|
|
45
|
+
projectPath: { type: "string", description: "Path to project root", default: "." }
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
name: "vibecheck.get_evidence",
|
|
51
|
+
description: "Get detailed evidence for a specific finding including screenshots, video timestamps, and code context.",
|
|
52
|
+
inputSchema: {
|
|
53
|
+
type: "object",
|
|
54
|
+
properties: {
|
|
55
|
+
projectPath: { type: "string", description: "Path to project root", default: "." },
|
|
56
|
+
findingId: { type: "string", description: "Finding ID or fingerprint to get evidence for" }
|
|
57
|
+
},
|
|
58
|
+
required: ["findingId"]
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
name: "vibecheck.check_flaky",
|
|
63
|
+
description: "Check if a finding is flaky (inconsistent across runs). Returns stability data if available.",
|
|
64
|
+
inputSchema: {
|
|
65
|
+
type: "object",
|
|
66
|
+
properties: {
|
|
67
|
+
projectPath: { type: "string", description: "Path to project root", default: "." },
|
|
68
|
+
findingId: { type: "string", description: "Finding ID to check for flakiness" }
|
|
69
|
+
},
|
|
70
|
+
required: ["findingId"]
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
name: "vibecheck.allowlist_add",
|
|
75
|
+
description: "Add a finding to the allowlist with a reason. Use when a finding is acceptable and shouldn't block shipping.",
|
|
76
|
+
inputSchema: {
|
|
77
|
+
type: "object",
|
|
78
|
+
properties: {
|
|
79
|
+
projectPath: { type: "string", description: "Path to project root", default: "." },
|
|
80
|
+
findingId: { type: "string", description: "Finding ID or fingerprint to allowlist" },
|
|
81
|
+
reason: { type: "string", description: "Why this finding is acceptable" },
|
|
82
|
+
whyAllowed: { type: "string", description: "Detailed explanation of why this is allowed" },
|
|
83
|
+
expiresAt: { type: "string", description: "Optional expiration date (ISO format)" }
|
|
84
|
+
},
|
|
85
|
+
required: ["findingId", "reason", "whyAllowed"]
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
name: "vibecheck.get_proof_graph",
|
|
90
|
+
description: "Get the proof graph showing claims, evidence, and gaps. Visual representation of what's been verified.",
|
|
91
|
+
inputSchema: {
|
|
92
|
+
type: "object",
|
|
93
|
+
properties: {
|
|
94
|
+
projectPath: { type: "string", description: "Path to project root", default: "." },
|
|
95
|
+
format: { type: "string", enum: ["json", "mermaid"], description: "Output format", default: "json" }
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
name: "vibecheck.evidence_pack",
|
|
101
|
+
description: "Generate or retrieve the latest evidence pack with all proof artifacts (videos, traces, screenshots).",
|
|
102
|
+
inputSchema: {
|
|
103
|
+
type: "object",
|
|
104
|
+
properties: {
|
|
105
|
+
projectPath: { type: "string", description: "Path to project root", default: "." },
|
|
106
|
+
generate: { type: "boolean", description: "Generate new pack from latest results", default: false },
|
|
107
|
+
includeMedia: { type: "boolean", description: "Include video/screenshot paths", default: true }
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
];
|
|
112
|
+
|
|
113
|
+
// ============================================================================
|
|
114
|
+
// TOOL HANDLERS
|
|
115
|
+
// ============================================================================
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Run full proof loop
|
|
119
|
+
*/
|
|
120
|
+
async function handleProve(args) {
|
|
121
|
+
const { url, projectPath = '.', auth, skipFix = false, stabilityRuns = 1, evidencePack = true } = args;
|
|
122
|
+
|
|
123
|
+
if (!url) {
|
|
124
|
+
return wrapResponse(null, { error: 'URL is required for prove command' });
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const cmdParts = ['vibecheck', 'prove', '--url', url, '--json'];
|
|
128
|
+
if (auth) cmdParts.push('--auth', auth);
|
|
129
|
+
if (skipFix) cmdParts.push('--skip-fix');
|
|
130
|
+
if (stabilityRuns > 1) cmdParts.push('--stability-runs', String(stabilityRuns));
|
|
131
|
+
if (evidencePack) cmdParts.push('--evidence-pack');
|
|
132
|
+
|
|
133
|
+
try {
|
|
134
|
+
const result = execSync(cmdParts.join(' '), {
|
|
135
|
+
cwd: projectPath,
|
|
136
|
+
encoding: 'utf8',
|
|
137
|
+
timeout: 600000, // 10 minutes
|
|
138
|
+
maxBuffer: 50 * 1024 * 1024
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
const parsed = JSON.parse(result);
|
|
142
|
+
return wrapResponse({
|
|
143
|
+
verdict: parsed.result?.finalVerdict || parsed.verdict,
|
|
144
|
+
findings: parsed.result?.findings || [],
|
|
145
|
+
coverage: parsed.result?.coverage,
|
|
146
|
+
duration: parsed.result?.duration,
|
|
147
|
+
artifacts: parsed.artifacts,
|
|
148
|
+
evidencePack: parsed.result?.evidencePackPath
|
|
149
|
+
}, {
|
|
150
|
+
evidence: (parsed.result?.findings || []).slice(0, 5).map(f => ({
|
|
151
|
+
file: f.file || f.page,
|
|
152
|
+
line: f.line,
|
|
153
|
+
snippet: f.title,
|
|
154
|
+
confidence: f.confidence || 0.9
|
|
155
|
+
}))
|
|
156
|
+
});
|
|
157
|
+
} catch (err) {
|
|
158
|
+
// Try to parse partial output
|
|
159
|
+
try {
|
|
160
|
+
const stdout = err.stdout?.toString() || '';
|
|
161
|
+
const lines = stdout.split('\n');
|
|
162
|
+
const jsonLine = lines.find(l => l.startsWith('{'));
|
|
163
|
+
if (jsonLine) {
|
|
164
|
+
const parsed = JSON.parse(jsonLine);
|
|
165
|
+
return wrapResponse(parsed, {
|
|
166
|
+
error: `Prove completed with exit code ${err.status}`
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
} catch {}
|
|
170
|
+
|
|
171
|
+
return wrapResponse(null, { error: `Prove failed: ${err.message}` });
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Get last proof status
|
|
177
|
+
*/
|
|
178
|
+
async function handleProveStatus(args) {
|
|
179
|
+
const { projectPath = '.' } = args;
|
|
180
|
+
|
|
181
|
+
const reportPath = path.join(projectPath, '.vibecheck', 'prove', 'last_prove.json');
|
|
182
|
+
|
|
183
|
+
if (!fs.existsSync(reportPath)) {
|
|
184
|
+
return wrapResponse(null, { error: 'No proof run found. Run vibecheck.prove first.' });
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
try {
|
|
188
|
+
const report = JSON.parse(fs.readFileSync(reportPath, 'utf8'));
|
|
189
|
+
|
|
190
|
+
// Check for evidence packs
|
|
191
|
+
const evidencePacksDir = path.join(projectPath, '.vibecheck', 'evidence-packs');
|
|
192
|
+
let latestPack = null;
|
|
193
|
+
if (fs.existsSync(evidencePacksDir)) {
|
|
194
|
+
const packs = fs.readdirSync(evidencePacksDir).sort().reverse();
|
|
195
|
+
if (packs.length > 0) {
|
|
196
|
+
latestPack = path.join(evidencePacksDir, packs[0]);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return wrapResponse({
|
|
201
|
+
verdict: report.finalVerdict,
|
|
202
|
+
meta: report.meta,
|
|
203
|
+
timeline: report.timeline,
|
|
204
|
+
findingsCount: report.findings?.length || 0,
|
|
205
|
+
blockers: (report.findings || []).filter(f => f.severity === 'BLOCK').length,
|
|
206
|
+
warnings: (report.findings || []).filter(f => f.severity === 'WARN').length,
|
|
207
|
+
coverage: report.coverage,
|
|
208
|
+
artifacts: report.artifacts,
|
|
209
|
+
evidencePackPath: latestPack,
|
|
210
|
+
reportPath
|
|
211
|
+
});
|
|
212
|
+
} catch (err) {
|
|
213
|
+
return wrapResponse(null, { error: `Failed to read proof status: ${err.message}` });
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Get evidence for specific finding
|
|
219
|
+
*/
|
|
220
|
+
async function handleGetEvidence(args) {
|
|
221
|
+
const { projectPath = '.', findingId } = args;
|
|
222
|
+
|
|
223
|
+
// Load latest reports to find the finding
|
|
224
|
+
const reportPaths = [
|
|
225
|
+
path.join(projectPath, '.vibecheck', 'prove', 'last_prove.json'),
|
|
226
|
+
path.join(projectPath, '.vibecheck', 'reality', 'last_reality.json'),
|
|
227
|
+
path.join(projectPath, '.vibecheck', 'ship', 'last_ship.json')
|
|
228
|
+
];
|
|
229
|
+
|
|
230
|
+
let finding = null;
|
|
231
|
+
let source = null;
|
|
232
|
+
|
|
233
|
+
for (const reportPath of reportPaths) {
|
|
234
|
+
if (fs.existsSync(reportPath)) {
|
|
235
|
+
try {
|
|
236
|
+
const report = JSON.parse(fs.readFileSync(reportPath, 'utf8'));
|
|
237
|
+
const findings = report.findings || [];
|
|
238
|
+
finding = findings.find(f =>
|
|
239
|
+
f.id === findingId ||
|
|
240
|
+
f.fingerprint === findingId ||
|
|
241
|
+
f.id?.includes(findingId) ||
|
|
242
|
+
f.fingerprint?.startsWith(findingId)
|
|
243
|
+
);
|
|
244
|
+
if (finding) {
|
|
245
|
+
source = reportPath;
|
|
246
|
+
break;
|
|
247
|
+
}
|
|
248
|
+
} catch {}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
if (!finding) {
|
|
253
|
+
return wrapResponse(null, { error: `Finding not found: ${findingId}` });
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// Build comprehensive evidence
|
|
257
|
+
const evidence = {
|
|
258
|
+
finding,
|
|
259
|
+
source,
|
|
260
|
+
what: {
|
|
261
|
+
type: finding.category,
|
|
262
|
+
title: finding.title,
|
|
263
|
+
detector: finding.detector || finding.category,
|
|
264
|
+
confidence: finding.confidence
|
|
265
|
+
},
|
|
266
|
+
where: {
|
|
267
|
+
file: finding.file,
|
|
268
|
+
line: finding.line,
|
|
269
|
+
page: finding.page,
|
|
270
|
+
url: finding.url
|
|
271
|
+
},
|
|
272
|
+
why: {
|
|
273
|
+
reason: finding.reason,
|
|
274
|
+
severity: finding.severity,
|
|
275
|
+
isBlocker: finding.severity === 'BLOCK'
|
|
276
|
+
},
|
|
277
|
+
artifacts: {
|
|
278
|
+
screenshot: finding.screenshot,
|
|
279
|
+
video: finding.video,
|
|
280
|
+
trace: finding.trace
|
|
281
|
+
},
|
|
282
|
+
stability: finding.stability
|
|
283
|
+
};
|
|
284
|
+
|
|
285
|
+
return wrapResponse(evidence, {
|
|
286
|
+
evidence: [{
|
|
287
|
+
file: finding.file || finding.page,
|
|
288
|
+
line: finding.line,
|
|
289
|
+
snippet: finding.reason || finding.title,
|
|
290
|
+
confidence: finding.confidence || 0.9
|
|
291
|
+
}]
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Check if finding is flaky
|
|
297
|
+
*/
|
|
298
|
+
async function handleCheckFlaky(args) {
|
|
299
|
+
const { projectPath = '.', findingId } = args;
|
|
300
|
+
|
|
301
|
+
// Get the finding's stability data
|
|
302
|
+
const evidenceResult = await handleGetEvidence({ projectPath, findingId });
|
|
303
|
+
|
|
304
|
+
if (!evidenceResult.ok) {
|
|
305
|
+
return evidenceResult;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
const finding = evidenceResult.data.finding;
|
|
309
|
+
const stability = finding.stability;
|
|
310
|
+
|
|
311
|
+
if (!stability) {
|
|
312
|
+
return wrapResponse({
|
|
313
|
+
findingId,
|
|
314
|
+
hasStabilityData: false,
|
|
315
|
+
message: 'No stability data available. Run prove with --stability-runs > 1 to detect flakiness.'
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
return wrapResponse({
|
|
320
|
+
findingId,
|
|
321
|
+
hasStabilityData: true,
|
|
322
|
+
isFlaky: stability.isFlaky,
|
|
323
|
+
occurrenceRate: stability.occurrenceRate,
|
|
324
|
+
flakinessScore: stability.flakinessScore,
|
|
325
|
+
appearedInRuns: stability.appearedInRuns,
|
|
326
|
+
totalRuns: stability.totalRuns,
|
|
327
|
+
recommendation: stability.isFlaky
|
|
328
|
+
? 'This finding is flaky - it may be due to timing issues or race conditions. Consider adding stability measures.'
|
|
329
|
+
: 'This finding is stable - it appears consistently across runs.'
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* Add finding to allowlist
|
|
335
|
+
*/
|
|
336
|
+
async function handleAllowlistAdd(args) {
|
|
337
|
+
const { projectPath = '.', findingId, reason, whyAllowed, expiresAt } = args;
|
|
338
|
+
|
|
339
|
+
try {
|
|
340
|
+
// Load the unified allowlist module
|
|
341
|
+
const allowlistModule = require('../packages/cli/src/allowlist/unified-allowlist');
|
|
342
|
+
const { UnifiedAllowlist } = allowlistModule;
|
|
343
|
+
|
|
344
|
+
const allowlist = new UnifiedAllowlist(projectPath);
|
|
345
|
+
|
|
346
|
+
// Find the original finding to get its details
|
|
347
|
+
const evidenceResult = await handleGetEvidence({ projectPath, findingId });
|
|
348
|
+
|
|
349
|
+
if (evidenceResult.ok && evidenceResult.data.finding) {
|
|
350
|
+
const finding = evidenceResult.data.finding;
|
|
351
|
+
|
|
352
|
+
allowlist.addFinding(finding, {
|
|
353
|
+
reason,
|
|
354
|
+
whyAllowed,
|
|
355
|
+
approvedBy: 'AI Assistant (MCP)',
|
|
356
|
+
expiresAt
|
|
357
|
+
});
|
|
358
|
+
} else {
|
|
359
|
+
// Add with just the fingerprint
|
|
360
|
+
allowlist.add({
|
|
361
|
+
fingerprint: findingId,
|
|
362
|
+
category: 'other',
|
|
363
|
+
reason,
|
|
364
|
+
what: `Finding ${findingId}`,
|
|
365
|
+
whyAllowed,
|
|
366
|
+
approvedBy: 'AI Assistant (MCP)',
|
|
367
|
+
expiresAt
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
allowlist.save();
|
|
372
|
+
|
|
373
|
+
return wrapResponse({
|
|
374
|
+
success: true,
|
|
375
|
+
message: `Finding ${findingId} added to allowlist`,
|
|
376
|
+
reason,
|
|
377
|
+
whyAllowed,
|
|
378
|
+
expiresAt: expiresAt || 'Never'
|
|
379
|
+
});
|
|
380
|
+
} catch (err) {
|
|
381
|
+
return wrapResponse(null, { error: `Failed to add to allowlist: ${err.message}` });
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
/**
|
|
386
|
+
* Get proof graph
|
|
387
|
+
*/
|
|
388
|
+
async function handleGetProofGraph(args) {
|
|
389
|
+
const { projectPath = '.', format = 'json' } = args;
|
|
390
|
+
|
|
391
|
+
const proofGraphPath = path.join(projectPath, '.vibecheck', 'proof-graph.json');
|
|
392
|
+
|
|
393
|
+
if (!fs.existsSync(proofGraphPath)) {
|
|
394
|
+
// Try to find in evidence packs
|
|
395
|
+
const evidencePacksDir = path.join(projectPath, '.vibecheck', 'evidence-packs');
|
|
396
|
+
if (fs.existsSync(evidencePacksDir)) {
|
|
397
|
+
const packs = fs.readdirSync(evidencePacksDir).sort().reverse();
|
|
398
|
+
for (const pack of packs) {
|
|
399
|
+
const packGraphPath = path.join(evidencePacksDir, pack, 'proof-graph.json');
|
|
400
|
+
if (fs.existsSync(packGraphPath)) {
|
|
401
|
+
const graph = JSON.parse(fs.readFileSync(packGraphPath, 'utf8'));
|
|
402
|
+
return formatProofGraph(graph, format);
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
return wrapResponse(null, { error: 'No proof graph found. Run vibecheck.prove first.' });
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
const graph = JSON.parse(fs.readFileSync(proofGraphPath, 'utf8'));
|
|
411
|
+
return formatProofGraph(graph, format);
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
function formatProofGraph(graph, format) {
|
|
415
|
+
if (format === 'mermaid') {
|
|
416
|
+
// Convert to mermaid diagram
|
|
417
|
+
const lines = ['graph TD'];
|
|
418
|
+
|
|
419
|
+
if (graph.verifiedClaims) {
|
|
420
|
+
for (const claim of graph.verifiedClaims) {
|
|
421
|
+
const claimId = `claim_${claim.id || Math.random().toString(36).slice(2, 8)}`;
|
|
422
|
+
lines.push(` ${claimId}[${claim.claim || claim.title}]`);
|
|
423
|
+
lines.push(` ${claimId} -->|verified| evidence_${claimId}`);
|
|
424
|
+
lines.push(` evidence_${claimId}((Evidence))`);
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
if (graph.gaps) {
|
|
429
|
+
for (const gap of graph.gaps) {
|
|
430
|
+
const gapId = `gap_${Math.random().toString(36).slice(2, 8)}`;
|
|
431
|
+
lines.push(` ${gapId}[/${gap.description || gap}/]`);
|
|
432
|
+
lines.push(` style ${gapId} fill:#f99`);
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
return wrapResponse({
|
|
437
|
+
format: 'mermaid',
|
|
438
|
+
diagram: lines.join('\n')
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
return wrapResponse({
|
|
443
|
+
format: 'json',
|
|
444
|
+
graph
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
/**
|
|
449
|
+
* Generate or get evidence pack
|
|
450
|
+
*/
|
|
451
|
+
async function handleEvidencePack(args) {
|
|
452
|
+
const { projectPath = '.', generate = false, includeMedia = true } = args;
|
|
453
|
+
|
|
454
|
+
if (generate) {
|
|
455
|
+
try {
|
|
456
|
+
const evidencePackModule = require('../packages/cli/src/evidence/evidence-pack');
|
|
457
|
+
const { generateEvidencePack } = evidencePackModule;
|
|
458
|
+
|
|
459
|
+
const result = await generateEvidencePack({
|
|
460
|
+
projectPath,
|
|
461
|
+
sourceType: 'prove',
|
|
462
|
+
includeVideos: includeMedia,
|
|
463
|
+
includeTraces: includeMedia,
|
|
464
|
+
includeScreenshots: includeMedia,
|
|
465
|
+
includeHar: true
|
|
466
|
+
});
|
|
467
|
+
|
|
468
|
+
return wrapResponse({
|
|
469
|
+
generated: true,
|
|
470
|
+
packPath: result.packPath,
|
|
471
|
+
manifest: result.manifest
|
|
472
|
+
});
|
|
473
|
+
} catch (err) {
|
|
474
|
+
return wrapResponse(null, { error: `Failed to generate evidence pack: ${err.message}` });
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
// Return latest evidence pack
|
|
479
|
+
const evidencePacksDir = path.join(projectPath, '.vibecheck', 'evidence-packs');
|
|
480
|
+
|
|
481
|
+
if (!fs.existsSync(evidencePacksDir)) {
|
|
482
|
+
return wrapResponse(null, {
|
|
483
|
+
error: 'No evidence packs found. Run vibecheck.prove with --evidence-pack or use generate: true.'
|
|
484
|
+
});
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
const packs = fs.readdirSync(evidencePacksDir).sort().reverse();
|
|
488
|
+
if (packs.length === 0) {
|
|
489
|
+
return wrapResponse(null, { error: 'No evidence packs found.' });
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
const latestPackDir = path.join(evidencePacksDir, packs[0]);
|
|
493
|
+
const manifestPath = path.join(latestPackDir, 'manifest.json');
|
|
494
|
+
|
|
495
|
+
if (!fs.existsSync(manifestPath)) {
|
|
496
|
+
return wrapResponse(null, { error: 'Evidence pack manifest not found.' });
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
|
|
500
|
+
|
|
501
|
+
return wrapResponse({
|
|
502
|
+
generated: false,
|
|
503
|
+
packPath: latestPackDir,
|
|
504
|
+
manifest,
|
|
505
|
+
htmlViewer: fs.existsSync(path.join(latestPackDir, 'summary.html'))
|
|
506
|
+
? path.join(latestPackDir, 'summary.html')
|
|
507
|
+
: null
|
|
508
|
+
});
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
// ============================================================================
|
|
512
|
+
// RESPONSE WRAPPER
|
|
513
|
+
// ============================================================================
|
|
514
|
+
|
|
515
|
+
function wrapResponse(data, options = {}) {
|
|
516
|
+
const { evidence = [], cached = false, error = null } = options;
|
|
517
|
+
|
|
518
|
+
if (error) {
|
|
519
|
+
return {
|
|
520
|
+
ok: false,
|
|
521
|
+
error: typeof error === 'string' ? error : error.message,
|
|
522
|
+
data: null,
|
|
523
|
+
evidence: [],
|
|
524
|
+
metadata: {
|
|
525
|
+
timestamp: new Date().toISOString(),
|
|
526
|
+
cached: false
|
|
527
|
+
}
|
|
528
|
+
};
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
return {
|
|
532
|
+
ok: true,
|
|
533
|
+
data,
|
|
534
|
+
evidence: evidence.map(e => ({
|
|
535
|
+
file: e.file || null,
|
|
536
|
+
line: e.line || e.lines || null,
|
|
537
|
+
snippet: e.snippet || e.code || null,
|
|
538
|
+
confidence: e.confidence || 0.9,
|
|
539
|
+
reason: e.reason || null
|
|
540
|
+
})),
|
|
541
|
+
metadata: {
|
|
542
|
+
timestamp: new Date().toISOString(),
|
|
543
|
+
cached
|
|
544
|
+
}
|
|
545
|
+
};
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
// ============================================================================
|
|
549
|
+
// HANDLER DISPATCH
|
|
550
|
+
// ============================================================================
|
|
551
|
+
|
|
552
|
+
export async function handleProofTool(toolName, args) {
|
|
553
|
+
const handlers = {
|
|
554
|
+
'vibecheck.prove': handleProve,
|
|
555
|
+
'vibecheck.prove_status': handleProveStatus,
|
|
556
|
+
'vibecheck.get_evidence': handleGetEvidence,
|
|
557
|
+
'vibecheck.check_flaky': handleCheckFlaky,
|
|
558
|
+
'vibecheck.allowlist_add': handleAllowlistAdd,
|
|
559
|
+
'vibecheck.get_proof_graph': handleGetProofGraph,
|
|
560
|
+
'vibecheck.evidence_pack': handleEvidencePack
|
|
561
|
+
};
|
|
562
|
+
|
|
563
|
+
const handler = handlers[toolName];
|
|
564
|
+
if (!handler) {
|
|
565
|
+
return wrapResponse(null, { error: `Unknown proof tool: ${toolName}` });
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
return handler(args);
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
export default { PROOF_TOOLS, handleProofTool };
|
package/mcp-server/tier-auth.js
CHANGED
|
@@ -41,7 +41,12 @@ export const TIERS = {
|
|
|
41
41
|
mcpRateLimit: 10, // requests per minute
|
|
42
42
|
},
|
|
43
43
|
// MCP tools allowed on FREE
|
|
44
|
-
mcpTools: [
|
|
44
|
+
mcpTools: [
|
|
45
|
+
'vibecheck.get_truthpack',
|
|
46
|
+
'vibecheck.validate_claim',
|
|
47
|
+
'vibecheck.compile_context',
|
|
48
|
+
'vibecheck.search_evidence',
|
|
49
|
+
],
|
|
45
50
|
},
|
|
46
51
|
starter: {
|
|
47
52
|
name: 'STARTER',
|
|
@@ -74,17 +79,17 @@ export const TIERS = {
|
|
|
74
79
|
fixApplyPatches: false,
|
|
75
80
|
mcpRateLimit: 60, // requests per minute
|
|
76
81
|
},
|
|
77
|
-
// MCP tools allowed on STARTER (
|
|
82
|
+
// MCP tools allowed on STARTER (curated)
|
|
78
83
|
mcpTools: [
|
|
79
|
-
'vibecheck.
|
|
80
|
-
'vibecheck.get_truthpack',
|
|
84
|
+
'vibecheck.ctx',
|
|
81
85
|
'vibecheck.scan',
|
|
82
|
-
'vibecheck.
|
|
83
|
-
'vibecheck.
|
|
84
|
-
'vibecheck.get_findings',
|
|
85
|
-
'vibecheck.contracts_diff',
|
|
86
|
+
'vibecheck.ship',
|
|
87
|
+
'vibecheck.get_truthpack',
|
|
86
88
|
'vibecheck.validate_claim',
|
|
87
89
|
'vibecheck.compile_context',
|
|
90
|
+
'vibecheck.search_evidence',
|
|
91
|
+
'vibecheck.find_counterexamples',
|
|
92
|
+
'vibecheck.check_invariants',
|
|
88
93
|
],
|
|
89
94
|
},
|
|
90
95
|
pro: {
|
|
@@ -114,19 +119,17 @@ export const TIERS = {
|
|
|
114
119
|
fixApplyPatches: true,
|
|
115
120
|
mcpRateLimit: -1, // unlimited
|
|
116
121
|
},
|
|
117
|
-
// MCP tools allowed on PRO (
|
|
122
|
+
// MCP tools allowed on PRO (curated)
|
|
118
123
|
mcpTools: [
|
|
119
|
-
|
|
120
|
-
'vibecheck.
|
|
121
|
-
'vibecheck.verify_patch',
|
|
122
|
-
'vibecheck.explain_evidence',
|
|
123
|
-
'vibecheck.fix',
|
|
124
|
-
'vibecheck.proof',
|
|
125
|
-
'vibecheck.prove',
|
|
124
|
+
'vibecheck.ctx',
|
|
125
|
+
'vibecheck.scan',
|
|
126
126
|
'vibecheck.ship',
|
|
127
|
-
'vibecheck.
|
|
128
|
-
'vibecheck.
|
|
129
|
-
'vibecheck.
|
|
127
|
+
'vibecheck.get_truthpack',
|
|
128
|
+
'vibecheck.validate_claim',
|
|
129
|
+
'vibecheck.compile_context',
|
|
130
|
+
'vibecheck.search_evidence',
|
|
131
|
+
'vibecheck.find_counterexamples',
|
|
132
|
+
'vibecheck.check_invariants',
|
|
130
133
|
],
|
|
131
134
|
},
|
|
132
135
|
compliance: {
|