@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
|
@@ -0,0 +1,804 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vibecheck MCP Consolidated Tools
|
|
3
|
+
*
|
|
4
|
+
* Reduced from 50+ tools to 15 focused tools that map to CLI commands.
|
|
5
|
+
* Each tool returns evidence-backed responses with file/line citations.
|
|
6
|
+
*
|
|
7
|
+
* Tool Categories:
|
|
8
|
+
* 1. Core Commands (5) - ship, scan, fix, verify, ctx
|
|
9
|
+
* 2. Truth Queries (5) - truthpack, routes, env, auth, billing
|
|
10
|
+
* 3. Evidence (3) - validate_claim, evidence, proof_graph
|
|
11
|
+
* 4. Utilities (2) - status, doctor
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { execSync, spawn } from 'child_process';
|
|
15
|
+
import fs from 'fs';
|
|
16
|
+
import path from 'path';
|
|
17
|
+
|
|
18
|
+
// ============================================================================
|
|
19
|
+
// TOOL DEFINITIONS (15 Core Tools)
|
|
20
|
+
// ============================================================================
|
|
21
|
+
|
|
22
|
+
export const CONSOLIDATED_TOOLS = [
|
|
23
|
+
// === CORE COMMANDS (5) ===
|
|
24
|
+
{
|
|
25
|
+
name: "vibecheck.ship",
|
|
26
|
+
description: "Get ship verdict: SHIP/WARN/BLOCK with evidence. Returns top blockers and fix suggestions.",
|
|
27
|
+
inputSchema: {
|
|
28
|
+
type: "object",
|
|
29
|
+
properties: {
|
|
30
|
+
projectPath: { type: "string", description: "Path to project root", default: "." },
|
|
31
|
+
strict: { type: "boolean", description: "Treat warnings as blockers", default: false },
|
|
32
|
+
json: { type: "boolean", description: "Return raw JSON output", default: false }
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
name: "vibecheck.scan",
|
|
38
|
+
description: "Deep scan for ship-killers: secrets, auth gaps, fake success, dead UI, billing bypasses.",
|
|
39
|
+
inputSchema: {
|
|
40
|
+
type: "object",
|
|
41
|
+
properties: {
|
|
42
|
+
projectPath: { type: "string", description: "Path to project root", default: "." },
|
|
43
|
+
profile: {
|
|
44
|
+
type: "string",
|
|
45
|
+
enum: ["quick", "full", "security", "billing"],
|
|
46
|
+
description: "Scan profile",
|
|
47
|
+
default: "quick"
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
name: "vibecheck.fix",
|
|
54
|
+
description: "Generate fix plan or apply patches for findings. Returns evidence-backed fix suggestions.",
|
|
55
|
+
inputSchema: {
|
|
56
|
+
type: "object",
|
|
57
|
+
properties: {
|
|
58
|
+
projectPath: { type: "string", description: "Path to project root", default: "." },
|
|
59
|
+
apply: { type: "boolean", description: "Apply patches (requires clean git)", default: false },
|
|
60
|
+
promptOnly: { type: "boolean", description: "Generate prompts only, no patches", default: false }
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
name: "vibecheck.verify",
|
|
66
|
+
description: "Runtime verification using Playwright. Tests UI behavior, auth flows, dead buttons.",
|
|
67
|
+
inputSchema: {
|
|
68
|
+
type: "object",
|
|
69
|
+
properties: {
|
|
70
|
+
url: { type: "string", description: "Base URL to verify (required)" },
|
|
71
|
+
auth: { type: "string", description: "Login credentials as email:password" },
|
|
72
|
+
paths: { type: "string", description: "Comma-separated paths to test" },
|
|
73
|
+
budget: { type: "string", enum: ["2m", "5m", "10m"], description: "Time budget", default: "5m" }
|
|
74
|
+
},
|
|
75
|
+
required: ["url"]
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
name: "vibecheck.ctx",
|
|
80
|
+
description: "Generate Truth Pack with routes, env, auth, billing facts. Evidence-backed context.",
|
|
81
|
+
inputSchema: {
|
|
82
|
+
type: "object",
|
|
83
|
+
properties: {
|
|
84
|
+
projectPath: { type: "string", description: "Path to project root", default: "." },
|
|
85
|
+
snapshot: { type: "boolean", description: "Save timestamped snapshot", default: false }
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
|
|
90
|
+
// === TRUTH QUERIES (5) ===
|
|
91
|
+
{
|
|
92
|
+
name: "vibecheck.get_truthpack",
|
|
93
|
+
description: "Get the full Truth Pack for the project. Use this BEFORE making claims about the codebase.",
|
|
94
|
+
inputSchema: {
|
|
95
|
+
type: "object",
|
|
96
|
+
properties: {
|
|
97
|
+
projectPath: { type: "string", description: "Path to project root", default: "." },
|
|
98
|
+
refresh: { type: "boolean", description: "Force regenerate truthpack", default: false }
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
name: "vibecheck.get_routes",
|
|
104
|
+
description: "Get all server and client routes with file/line evidence.",
|
|
105
|
+
inputSchema: {
|
|
106
|
+
type: "object",
|
|
107
|
+
properties: {
|
|
108
|
+
projectPath: { type: "string", description: "Path to project root", default: "." },
|
|
109
|
+
type: { type: "string", enum: ["server", "client", "all"], default: "all" }
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
name: "vibecheck.get_env",
|
|
115
|
+
description: "Get env vars: declared, used, mismatches. Evidence-backed.",
|
|
116
|
+
inputSchema: {
|
|
117
|
+
type: "object",
|
|
118
|
+
properties: {
|
|
119
|
+
projectPath: { type: "string", description: "Path to project root", default: "." }
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
name: "vibecheck.get_auth",
|
|
125
|
+
description: "Get auth model: guards, protected routes, unprotected routes, gaps.",
|
|
126
|
+
inputSchema: {
|
|
127
|
+
type: "object",
|
|
128
|
+
properties: {
|
|
129
|
+
projectPath: { type: "string", description: "Path to project root", default: "." }
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
name: "vibecheck.get_billing",
|
|
135
|
+
description: "Get billing model: gates, paid features, bypasses, enforcement gaps.",
|
|
136
|
+
inputSchema: {
|
|
137
|
+
type: "object",
|
|
138
|
+
properties: {
|
|
139
|
+
projectPath: { type: "string", description: "Path to project root", default: "." }
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
},
|
|
143
|
+
|
|
144
|
+
// === EVIDENCE (3) ===
|
|
145
|
+
{
|
|
146
|
+
name: "vibecheck.validate_claim",
|
|
147
|
+
description: "Validate a claim about the codebase. Returns true/false/unknown with evidence.",
|
|
148
|
+
inputSchema: {
|
|
149
|
+
type: "object",
|
|
150
|
+
properties: {
|
|
151
|
+
projectPath: { type: "string", description: "Path to project root", default: "." },
|
|
152
|
+
claim: { type: "string", description: "The claim to validate (required)" },
|
|
153
|
+
type: {
|
|
154
|
+
type: "string",
|
|
155
|
+
enum: ["route_exists", "env_var_used", "auth_enforced", "file_exists", "function_exists"],
|
|
156
|
+
description: "Type of claim"
|
|
157
|
+
}
|
|
158
|
+
},
|
|
159
|
+
required: ["claim"]
|
|
160
|
+
}
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
name: "vibecheck.get_evidence",
|
|
164
|
+
description: "Get file/line evidence for a specific finding or claim.",
|
|
165
|
+
inputSchema: {
|
|
166
|
+
type: "object",
|
|
167
|
+
properties: {
|
|
168
|
+
projectPath: { type: "string", description: "Path to project root", default: "." },
|
|
169
|
+
findingId: { type: "string", description: "Finding ID to get evidence for" },
|
|
170
|
+
file: { type: "string", description: "File path to extract evidence from" },
|
|
171
|
+
pattern: { type: "string", description: "Regex pattern to search for" }
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
name: "vibecheck.get_proof_graph",
|
|
177
|
+
description: "Get the proof graph: claims -> evidence -> gaps -> risk score.",
|
|
178
|
+
inputSchema: {
|
|
179
|
+
type: "object",
|
|
180
|
+
properties: {
|
|
181
|
+
projectPath: { type: "string", description: "Path to project root", default: "." }
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
},
|
|
185
|
+
|
|
186
|
+
// === SPEC-REQUIRED TOOLS (4) ===
|
|
187
|
+
{
|
|
188
|
+
name: "vibecheck.get_contracts",
|
|
189
|
+
description: "Get context contracts (routes, env, auth, external). Use with validate_plan to stop hallucinations.",
|
|
190
|
+
inputSchema: {
|
|
191
|
+
type: "object",
|
|
192
|
+
properties: {
|
|
193
|
+
projectPath: { type: "string", description: "Path to project root", default: "." }
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
},
|
|
197
|
+
{
|
|
198
|
+
name: "vibecheck.validate_plan",
|
|
199
|
+
description: "Validate an AI-generated plan against contracts. Returns violations for invented routes, missing env vars, auth mismatches.",
|
|
200
|
+
inputSchema: {
|
|
201
|
+
type: "object",
|
|
202
|
+
properties: {
|
|
203
|
+
projectPath: { type: "string", description: "Path to project root", default: "." },
|
|
204
|
+
plan: {
|
|
205
|
+
type: "array",
|
|
206
|
+
description: "Array of plan actions to validate",
|
|
207
|
+
items: {
|
|
208
|
+
type: "object",
|
|
209
|
+
properties: {
|
|
210
|
+
action: { type: "string" },
|
|
211
|
+
target: { type: "string" },
|
|
212
|
+
details: { type: "object" }
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
},
|
|
217
|
+
required: ["plan"]
|
|
218
|
+
}
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
name: "vibecheck.share",
|
|
222
|
+
description: "Build share pack from latest mission. Generates sanitized share.json, share.md, pr_comment.md.",
|
|
223
|
+
inputSchema: {
|
|
224
|
+
type: "object",
|
|
225
|
+
properties: {
|
|
226
|
+
projectPath: { type: "string", description: "Path to project root", default: "." },
|
|
227
|
+
missionDir: { type: "string", description: "Specific mission directory (default: latest)" }
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
},
|
|
231
|
+
{
|
|
232
|
+
name: "vibecheck.pr_comment",
|
|
233
|
+
description: "Render PR comment markdown from last ship report. Ready to paste into GitHub PR.",
|
|
234
|
+
inputSchema: {
|
|
235
|
+
type: "object",
|
|
236
|
+
properties: {
|
|
237
|
+
projectPath: { type: "string", description: "Path to project root", default: "." },
|
|
238
|
+
maxFindings: { type: "number", description: "Max findings to include", default: 12 }
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
},
|
|
242
|
+
|
|
243
|
+
// === UTILITIES (2) ===
|
|
244
|
+
{
|
|
245
|
+
name: "vibecheck.status",
|
|
246
|
+
description: "Get current vibecheck status: last verdict, findings count, truthpack freshness.",
|
|
247
|
+
inputSchema: {
|
|
248
|
+
type: "object",
|
|
249
|
+
properties: {
|
|
250
|
+
projectPath: { type: "string", description: "Path to project root", default: "." }
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
},
|
|
254
|
+
{
|
|
255
|
+
name: "vibecheck.doctor",
|
|
256
|
+
description: "Environment health check. Verifies dependencies, configs, permissions.",
|
|
257
|
+
inputSchema: {
|
|
258
|
+
type: "object",
|
|
259
|
+
properties: {
|
|
260
|
+
projectPath: { type: "string", description: "Path to project root", default: "." }
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
];
|
|
265
|
+
|
|
266
|
+
// ============================================================================
|
|
267
|
+
// TOOL HANDLERS
|
|
268
|
+
// ============================================================================
|
|
269
|
+
|
|
270
|
+
export async function handleConsolidatedTool(name, args) {
|
|
271
|
+
const projectPath = args.projectPath || process.cwd();
|
|
272
|
+
|
|
273
|
+
switch (name) {
|
|
274
|
+
// Core Commands
|
|
275
|
+
case "vibecheck.ship":
|
|
276
|
+
return await runCliCommand("ship", projectPath, args);
|
|
277
|
+
case "vibecheck.scan":
|
|
278
|
+
return await runCliCommand("scan", projectPath, args);
|
|
279
|
+
case "vibecheck.fix":
|
|
280
|
+
return await runCliCommand("fix", projectPath, args);
|
|
281
|
+
case "vibecheck.verify":
|
|
282
|
+
return await runCliCommand("verify", projectPath, args);
|
|
283
|
+
case "vibecheck.ctx":
|
|
284
|
+
return await runCliCommand("ctx", projectPath, args);
|
|
285
|
+
|
|
286
|
+
// Truth Queries
|
|
287
|
+
case "vibecheck.get_truthpack":
|
|
288
|
+
return await getTruthpack(projectPath, args.refresh);
|
|
289
|
+
case "vibecheck.get_routes":
|
|
290
|
+
return await getRoutes(projectPath, args.type);
|
|
291
|
+
case "vibecheck.get_env":
|
|
292
|
+
return await getEnv(projectPath);
|
|
293
|
+
case "vibecheck.get_auth":
|
|
294
|
+
return await getAuth(projectPath);
|
|
295
|
+
case "vibecheck.get_billing":
|
|
296
|
+
return await getBilling(projectPath);
|
|
297
|
+
|
|
298
|
+
// Evidence
|
|
299
|
+
case "vibecheck.validate_claim":
|
|
300
|
+
return await validateClaim(projectPath, args.claim, args.type);
|
|
301
|
+
case "vibecheck.get_evidence":
|
|
302
|
+
return await getEvidence(projectPath, args);
|
|
303
|
+
case "vibecheck.get_proof_graph":
|
|
304
|
+
return await getProofGraph(projectPath);
|
|
305
|
+
|
|
306
|
+
// Utilities
|
|
307
|
+
case "vibecheck.status":
|
|
308
|
+
return await getStatus(projectPath);
|
|
309
|
+
case "vibecheck.doctor":
|
|
310
|
+
return await runCliCommand("doctor", projectPath, args);
|
|
311
|
+
|
|
312
|
+
// Spec-required tools
|
|
313
|
+
case "vibecheck.get_contracts":
|
|
314
|
+
return await getContracts(projectPath);
|
|
315
|
+
case "vibecheck.validate_plan":
|
|
316
|
+
return await validatePlan(projectPath, args.plan);
|
|
317
|
+
case "vibecheck.share":
|
|
318
|
+
return await buildShare(projectPath, args.missionDir);
|
|
319
|
+
case "vibecheck.pr_comment":
|
|
320
|
+
return await renderPRComment(projectPath, args.maxFindings);
|
|
321
|
+
|
|
322
|
+
default:
|
|
323
|
+
return { error: `Unknown tool: ${name}`, available: CONSOLIDATED_TOOLS.map(t => t.name) };
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// ============================================================================
|
|
328
|
+
// IMPLEMENTATION HELPERS
|
|
329
|
+
// ============================================================================
|
|
330
|
+
|
|
331
|
+
async function runCliCommand(cmd, projectPath, args) {
|
|
332
|
+
const binPath = path.join(__dirname, '..', 'bin', 'vibecheck.js');
|
|
333
|
+
let cmdArgs = [cmd];
|
|
334
|
+
|
|
335
|
+
// Add flags based on args
|
|
336
|
+
if (args.strict) cmdArgs.push('--strict');
|
|
337
|
+
if (args.json) cmdArgs.push('--json');
|
|
338
|
+
if (args.apply) cmdArgs.push('--apply');
|
|
339
|
+
if (args.promptOnly) cmdArgs.push('--prompt-only');
|
|
340
|
+
if (args.snapshot) cmdArgs.push('--snapshot');
|
|
341
|
+
if (args.url) cmdArgs.push('--url', args.url);
|
|
342
|
+
if (args.auth) cmdArgs.push('--auth', args.auth);
|
|
343
|
+
if (args.paths) cmdArgs.push('--paths', args.paths);
|
|
344
|
+
if (args.budget) cmdArgs.push('--budget', args.budget);
|
|
345
|
+
if (args.profile) cmdArgs.push('--profile', args.profile);
|
|
346
|
+
|
|
347
|
+
try {
|
|
348
|
+
const result = execSync(`node "${binPath}" ${cmdArgs.join(' ')}`, {
|
|
349
|
+
cwd: projectPath,
|
|
350
|
+
encoding: 'utf8',
|
|
351
|
+
timeout: 120000
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
// Try to parse JSON output
|
|
355
|
+
try {
|
|
356
|
+
return JSON.parse(result);
|
|
357
|
+
} catch {
|
|
358
|
+
return { output: result, success: true };
|
|
359
|
+
}
|
|
360
|
+
} catch (error) {
|
|
361
|
+
return {
|
|
362
|
+
error: error.message,
|
|
363
|
+
exitCode: error.status,
|
|
364
|
+
output: error.stdout || error.stderr
|
|
365
|
+
};
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
async function getTruthpack(projectPath, refresh = false) {
|
|
370
|
+
// Spec path: .vibecheck/truthpack.json
|
|
371
|
+
const specPath = path.join(projectPath, '.vibecheck', 'truthpack.json');
|
|
372
|
+
// Legacy path: .vibecheck/truth/truthpack.json
|
|
373
|
+
const legacyPath = path.join(projectPath, '.vibecheck', 'truth', 'truthpack.json');
|
|
374
|
+
|
|
375
|
+
const truthpackPath = fs.existsSync(specPath) ? specPath : legacyPath;
|
|
376
|
+
|
|
377
|
+
if (refresh || !fs.existsSync(truthpackPath)) {
|
|
378
|
+
await runCliCommand('ctx', projectPath, {});
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
// Try spec path first after potential regeneration
|
|
382
|
+
for (const tpPath of [specPath, legacyPath]) {
|
|
383
|
+
try {
|
|
384
|
+
const truthpack = JSON.parse(fs.readFileSync(tpPath, 'utf8'));
|
|
385
|
+
return {
|
|
386
|
+
data: truthpack,
|
|
387
|
+
evidence: [{ file: tpPath.replace(projectPath, '.'), line: 1 }],
|
|
388
|
+
confidence: 1.0,
|
|
389
|
+
freshness: truthpack.generatedAt
|
|
390
|
+
};
|
|
391
|
+
} catch {}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
return { error: 'Truthpack not found. Run vibecheck ctx first.', gaps: ['truthpack_missing'] };
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
async function getRoutes(projectPath, type = 'all') {
|
|
398
|
+
const truthpack = await getTruthpack(projectPath);
|
|
399
|
+
if (truthpack.error) return truthpack;
|
|
400
|
+
|
|
401
|
+
const routes = truthpack.data.routes || {};
|
|
402
|
+
|
|
403
|
+
if (type === 'server') {
|
|
404
|
+
return { data: routes.server || [], evidence: [], confidence: 0.9 };
|
|
405
|
+
} else if (type === 'client') {
|
|
406
|
+
return { data: routes.clientRefs || [], evidence: [], confidence: 0.85 };
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
return {
|
|
410
|
+
data: {
|
|
411
|
+
server: routes.server || [],
|
|
412
|
+
client: routes.clientRefs || [],
|
|
413
|
+
gaps: routes.gaps || []
|
|
414
|
+
},
|
|
415
|
+
evidence: [],
|
|
416
|
+
confidence: 0.9
|
|
417
|
+
};
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
async function getEnv(projectPath) {
|
|
421
|
+
const truthpack = await getTruthpack(projectPath);
|
|
422
|
+
if (truthpack.error) return truthpack;
|
|
423
|
+
|
|
424
|
+
return {
|
|
425
|
+
data: truthpack.data.env || {},
|
|
426
|
+
evidence: [],
|
|
427
|
+
confidence: 0.95
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
async function getAuth(projectPath) {
|
|
432
|
+
const truthpack = await getTruthpack(projectPath);
|
|
433
|
+
if (truthpack.error) return truthpack;
|
|
434
|
+
|
|
435
|
+
return {
|
|
436
|
+
data: truthpack.data.auth || {},
|
|
437
|
+
evidence: [],
|
|
438
|
+
confidence: 0.85
|
|
439
|
+
};
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
async function getBilling(projectPath) {
|
|
443
|
+
const truthpack = await getTruthpack(projectPath);
|
|
444
|
+
if (truthpack.error) return truthpack;
|
|
445
|
+
|
|
446
|
+
return {
|
|
447
|
+
data: truthpack.data.billing || {},
|
|
448
|
+
evidence: [],
|
|
449
|
+
confidence: 0.8
|
|
450
|
+
};
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
async function validateClaim(projectPath, claim, type) {
|
|
454
|
+
// Use the claim verifier runner
|
|
455
|
+
const binPath = path.join(__dirname, '..', 'bin', 'runners', 'runClaimVerifier.js');
|
|
456
|
+
|
|
457
|
+
try {
|
|
458
|
+
const { validateClaim: verify } = require(binPath);
|
|
459
|
+
const result = await verify(projectPath, { claim, type });
|
|
460
|
+
return result;
|
|
461
|
+
} catch (error) {
|
|
462
|
+
// Fallback to simple validation
|
|
463
|
+
const truthpack = await getTruthpack(projectPath);
|
|
464
|
+
if (truthpack.error) return { valid: 'unknown', reason: truthpack.error };
|
|
465
|
+
|
|
466
|
+
// Simple claim validation based on type
|
|
467
|
+
if (type === 'route_exists') {
|
|
468
|
+
const routes = truthpack.data.routes?.server || [];
|
|
469
|
+
const exists = routes.some(r => r.path === claim || r.path.includes(claim));
|
|
470
|
+
return {
|
|
471
|
+
valid: exists,
|
|
472
|
+
confidence: exists ? 0.9 : 0.7,
|
|
473
|
+
evidence: exists ? routes.filter(r => r.path.includes(claim)).slice(0, 3) : [],
|
|
474
|
+
gaps: exists ? [] : [`Route ${claim} not found in server routes`]
|
|
475
|
+
};
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
if (type === 'env_var_used') {
|
|
479
|
+
const vars = truthpack.data.env?.vars || [];
|
|
480
|
+
const found = vars.find(v => v.name === claim);
|
|
481
|
+
return {
|
|
482
|
+
valid: !!found,
|
|
483
|
+
confidence: found ? 0.95 : 0.8,
|
|
484
|
+
evidence: found ? [{ file: found.file, line: found.line }] : [],
|
|
485
|
+
gaps: found ? [] : [`Env var ${claim} not found in codebase`]
|
|
486
|
+
};
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
if (type === 'file_exists') {
|
|
490
|
+
const filePath = path.join(projectPath, claim);
|
|
491
|
+
const exists = fs.existsSync(filePath);
|
|
492
|
+
return {
|
|
493
|
+
valid: exists,
|
|
494
|
+
confidence: 1.0,
|
|
495
|
+
evidence: exists ? [{ file: claim, line: 1 }] : [],
|
|
496
|
+
gaps: exists ? [] : [`File ${claim} does not exist`]
|
|
497
|
+
};
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
return { valid: 'unknown', reason: 'Claim type not supported', type };
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
async function getEvidence(projectPath, args) {
|
|
505
|
+
if (args.findingId) {
|
|
506
|
+
// Get evidence for a specific finding from last ship report
|
|
507
|
+
const shipPath = path.join(projectPath, '.vibecheck', 'last_ship.json');
|
|
508
|
+
try {
|
|
509
|
+
const report = JSON.parse(fs.readFileSync(shipPath, 'utf8'));
|
|
510
|
+
const finding = report.findings?.find(f => f.id === args.findingId);
|
|
511
|
+
if (finding) {
|
|
512
|
+
return {
|
|
513
|
+
data: finding.evidence || [],
|
|
514
|
+
finding,
|
|
515
|
+
confidence: 0.9
|
|
516
|
+
};
|
|
517
|
+
}
|
|
518
|
+
} catch {}
|
|
519
|
+
return { error: 'Finding not found', findingId: args.findingId };
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
if (args.file && args.pattern) {
|
|
523
|
+
// Search for pattern in file
|
|
524
|
+
const filePath = path.join(projectPath, args.file);
|
|
525
|
+
try {
|
|
526
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
527
|
+
const regex = new RegExp(args.pattern, 'gi');
|
|
528
|
+
const matches = [];
|
|
529
|
+
let match;
|
|
530
|
+
while ((match = regex.exec(content)) !== null) {
|
|
531
|
+
const line = content.substring(0, match.index).split('\n').length;
|
|
532
|
+
matches.push({
|
|
533
|
+
file: args.file,
|
|
534
|
+
line,
|
|
535
|
+
snippet: match[0],
|
|
536
|
+
confidence: 0.95
|
|
537
|
+
});
|
|
538
|
+
}
|
|
539
|
+
return { data: matches, confidence: 0.9 };
|
|
540
|
+
} catch (error) {
|
|
541
|
+
return { error: error.message };
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
return { error: 'Provide either findingId or file+pattern' };
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
async function getProofGraph(projectPath) {
|
|
549
|
+
const proofPath = path.join(projectPath, '.vibecheck', 'proof-graph.json');
|
|
550
|
+
|
|
551
|
+
try {
|
|
552
|
+
const proofGraph = JSON.parse(fs.readFileSync(proofPath, 'utf8'));
|
|
553
|
+
return {
|
|
554
|
+
data: proofGraph,
|
|
555
|
+
confidence: 0.9
|
|
556
|
+
};
|
|
557
|
+
} catch {
|
|
558
|
+
// Generate if not exists
|
|
559
|
+
await runCliCommand('ship', projectPath, {});
|
|
560
|
+
try {
|
|
561
|
+
const proofGraph = JSON.parse(fs.readFileSync(proofPath, 'utf8'));
|
|
562
|
+
return { data: proofGraph, confidence: 0.9 };
|
|
563
|
+
} catch {
|
|
564
|
+
return { error: 'Proof graph not available. Run vibecheck ship first.' };
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
async function getStatus(projectPath) {
|
|
570
|
+
const shipPath = path.join(projectPath, '.vibecheck', 'last_ship.json');
|
|
571
|
+
// Check both spec path and legacy path
|
|
572
|
+
const truthpackPath = path.join(projectPath, '.vibecheck', 'truthpack.json');
|
|
573
|
+
const legacyTruthpackPath = path.join(projectPath, '.vibecheck', 'truth', 'truthpack.json');
|
|
574
|
+
|
|
575
|
+
const status = {
|
|
576
|
+
hasShipReport: false,
|
|
577
|
+
hasTruthpack: false,
|
|
578
|
+
lastVerdict: null,
|
|
579
|
+
findingsCount: 0,
|
|
580
|
+
truthpackAge: null
|
|
581
|
+
};
|
|
582
|
+
|
|
583
|
+
try {
|
|
584
|
+
const report = JSON.parse(fs.readFileSync(shipPath, 'utf8'));
|
|
585
|
+
status.hasShipReport = true;
|
|
586
|
+
status.lastVerdict = report.meta?.verdict;
|
|
587
|
+
status.findingsCount = report.findings?.length || 0;
|
|
588
|
+
} catch {}
|
|
589
|
+
|
|
590
|
+
// Try spec path first, then legacy
|
|
591
|
+
for (const tpPath of [truthpackPath, legacyTruthpackPath]) {
|
|
592
|
+
try {
|
|
593
|
+
const truthpack = JSON.parse(fs.readFileSync(tpPath, 'utf8'));
|
|
594
|
+
status.hasTruthpack = true;
|
|
595
|
+
status.truthpackAge = truthpack.generatedAt;
|
|
596
|
+
break;
|
|
597
|
+
} catch {}
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
return { data: status, confidence: 1.0 };
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
// ============================================================================
|
|
604
|
+
// SPEC-REQUIRED TOOL IMPLEMENTATIONS
|
|
605
|
+
// ============================================================================
|
|
606
|
+
|
|
607
|
+
async function getContracts(projectPath) {
|
|
608
|
+
const contractDir = path.join(projectPath, '.vibecheck', 'contracts');
|
|
609
|
+
const contracts = {};
|
|
610
|
+
|
|
611
|
+
const files = ['routes.json', 'env.json', 'auth.json', 'external.json'];
|
|
612
|
+
|
|
613
|
+
for (const file of files) {
|
|
614
|
+
const filePath = path.join(contractDir, file);
|
|
615
|
+
try {
|
|
616
|
+
contracts[file.replace('.json', '')] = JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
617
|
+
} catch {}
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
if (Object.keys(contracts).length === 0) {
|
|
621
|
+
return {
|
|
622
|
+
error: 'No contracts found. Run vibecheck ctx sync first.',
|
|
623
|
+
hint: 'vibecheck ctx sync generates contracts from your truthpack'
|
|
624
|
+
};
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
return {
|
|
628
|
+
data: contracts,
|
|
629
|
+
files: Object.keys(contracts).map(k => `.vibecheck/contracts/${k}.json`),
|
|
630
|
+
confidence: 1.0
|
|
631
|
+
};
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
async function validatePlan(projectPath, plan) {
|
|
635
|
+
if (!plan || !Array.isArray(plan)) {
|
|
636
|
+
return { error: 'Plan must be an array of actions' };
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
// Load contracts
|
|
640
|
+
const contractsResult = await getContracts(projectPath);
|
|
641
|
+
if (contractsResult.error) {
|
|
642
|
+
return {
|
|
643
|
+
valid: 'unknown',
|
|
644
|
+
reason: contractsResult.error,
|
|
645
|
+
hint: 'Generate contracts first with: vibecheck ctx sync'
|
|
646
|
+
};
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
const contracts = contractsResult.data;
|
|
650
|
+
const violations = [];
|
|
651
|
+
const requiredContractEdits = [];
|
|
652
|
+
|
|
653
|
+
for (const action of plan) {
|
|
654
|
+
// Check route references
|
|
655
|
+
if (action.action === 'fetch' || action.action === 'api_call' || action.target?.startsWith('/api/')) {
|
|
656
|
+
const routeTarget = action.target || action.details?.endpoint;
|
|
657
|
+
if (routeTarget && contracts.routes?.routes) {
|
|
658
|
+
const routeExists = contracts.routes.routes.some(r =>
|
|
659
|
+
r.path === routeTarget ||
|
|
660
|
+
r.path.replace(/:\w+/g, '[^/]+').match(new RegExp(`^${routeTarget.replace(/:\w+/g, '[^/]+')}$`))
|
|
661
|
+
);
|
|
662
|
+
if (!routeExists) {
|
|
663
|
+
violations.push({
|
|
664
|
+
type: 'route_not_in_contract',
|
|
665
|
+
action: action.action,
|
|
666
|
+
target: routeTarget,
|
|
667
|
+
message: `Route ${routeTarget} not found in routes contract`,
|
|
668
|
+
severity: 'BLOCK'
|
|
669
|
+
});
|
|
670
|
+
requiredContractEdits.push({
|
|
671
|
+
contract: 'routes',
|
|
672
|
+
action: 'add_route',
|
|
673
|
+
data: { path: routeTarget, method: action.details?.method || 'GET' }
|
|
674
|
+
});
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
// Check env var references
|
|
680
|
+
if (action.action === 'use_env' || action.details?.env) {
|
|
681
|
+
const envVar = action.target || action.details?.env;
|
|
682
|
+
if (envVar && contracts.env?.vars) {
|
|
683
|
+
const envExists = contracts.env.vars.some(v => v.name === envVar);
|
|
684
|
+
if (!envExists) {
|
|
685
|
+
violations.push({
|
|
686
|
+
type: 'env_not_in_contract',
|
|
687
|
+
action: action.action,
|
|
688
|
+
target: envVar,
|
|
689
|
+
message: `Env var ${envVar} not declared in env contract`,
|
|
690
|
+
severity: 'WARN'
|
|
691
|
+
});
|
|
692
|
+
requiredContractEdits.push({
|
|
693
|
+
contract: 'env',
|
|
694
|
+
action: 'add_var',
|
|
695
|
+
data: { name: envVar, required: true }
|
|
696
|
+
});
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
return {
|
|
703
|
+
valid: violations.length === 0,
|
|
704
|
+
violations,
|
|
705
|
+
requiredContractEdits,
|
|
706
|
+
checkedActions: plan.length,
|
|
707
|
+
confidence: 0.9
|
|
708
|
+
};
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
async function buildShare(projectPath, missionDir) {
|
|
712
|
+
const binPath = path.join(__dirname, '..', 'bin', 'vibecheck.js');
|
|
713
|
+
let cmdArgs = ['share'];
|
|
714
|
+
if (missionDir) cmdArgs.push('--mission-dir', missionDir);
|
|
715
|
+
|
|
716
|
+
try {
|
|
717
|
+
const result = execSync(`node "${binPath}" ${cmdArgs.join(' ')}`, {
|
|
718
|
+
cwd: projectPath,
|
|
719
|
+
encoding: 'utf8',
|
|
720
|
+
timeout: 30000
|
|
721
|
+
});
|
|
722
|
+
|
|
723
|
+
// Read generated files
|
|
724
|
+
const shareDir = path.join(projectPath, '.vibecheck', 'missions');
|
|
725
|
+
const files = {};
|
|
726
|
+
|
|
727
|
+
for (const file of ['share.json', 'share.md', 'pr_comment.md']) {
|
|
728
|
+
// Find latest mission dir
|
|
729
|
+
try {
|
|
730
|
+
const dirs = fs.readdirSync(shareDir).filter(d => d.match(/^\d+$/)).sort().reverse();
|
|
731
|
+
if (dirs.length > 0) {
|
|
732
|
+
const latestShare = path.join(shareDir, dirs[0], 'share', file);
|
|
733
|
+
if (fs.existsSync(latestShare)) {
|
|
734
|
+
files[file] = fs.readFileSync(latestShare, 'utf8');
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
} catch {}
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
return {
|
|
741
|
+
success: true,
|
|
742
|
+
output: result,
|
|
743
|
+
files,
|
|
744
|
+
confidence: 1.0
|
|
745
|
+
};
|
|
746
|
+
} catch (error) {
|
|
747
|
+
return { error: error.message };
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
async function renderPRComment(projectPath, maxFindings = 12) {
|
|
752
|
+
const shipPath = path.join(projectPath, '.vibecheck', 'last_ship.json');
|
|
753
|
+
|
|
754
|
+
try {
|
|
755
|
+
const report = JSON.parse(fs.readFileSync(shipPath, 'utf8'));
|
|
756
|
+
const verdict = report.meta?.verdict || 'unknown';
|
|
757
|
+
const findings = report.findings || [];
|
|
758
|
+
|
|
759
|
+
// Build PR comment markdown
|
|
760
|
+
const severityCounts = { BLOCK: 0, WARN: 0, INFO: 0 };
|
|
761
|
+
for (const f of findings) {
|
|
762
|
+
severityCounts[f.severity] = (severityCounts[f.severity] || 0) + 1;
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
const verdictEmoji = verdict === 'SHIP' ? '✅' : verdict === 'WARN' ? '⚠️' : '🛑';
|
|
766
|
+
|
|
767
|
+
let md = `## vibecheck — ${verdictEmoji} ${verdict}\n\n`;
|
|
768
|
+
md += `**Reality summary:** BLOCK=${severityCounts.BLOCK} • WARN=${severityCounts.WARN}\n\n`;
|
|
769
|
+
|
|
770
|
+
const topFindings = findings
|
|
771
|
+
.filter(f => f.severity === 'BLOCK' || f.severity === 'WARN')
|
|
772
|
+
.slice(0, maxFindings);
|
|
773
|
+
|
|
774
|
+
if (topFindings.length === 0) {
|
|
775
|
+
md += `✅ No blockers. Ship it.\n`;
|
|
776
|
+
} else {
|
|
777
|
+
md += `### Top findings\n`;
|
|
778
|
+
for (const f of topFindings) {
|
|
779
|
+
md += `- **${f.severity}** \`${f.id}\` — ${f.title}\n`;
|
|
780
|
+
const ev = (f.evidence || [])[0];
|
|
781
|
+
if (ev?.file) {
|
|
782
|
+
md += ` - Evidence: \`${ev.file}:${ev.lines}\` (${ev.reason})\n`;
|
|
783
|
+
}
|
|
784
|
+
const hint = (f.fixHints || [])[0];
|
|
785
|
+
if (hint) md += ` - Fix: ${hint}\n`;
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
if (findings.length > topFindings.length) {
|
|
789
|
+
md += `\n_…and ${findings.length - topFindings.length} more finding(s). See \`.vibecheck/last_ship.json\` for full details._\n`;
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
return {
|
|
794
|
+
markdown: md,
|
|
795
|
+
verdict,
|
|
796
|
+
findingsCount: findings.length,
|
|
797
|
+
confidence: 1.0
|
|
798
|
+
};
|
|
799
|
+
} catch (error) {
|
|
800
|
+
return { error: 'No ship report found. Run vibecheck ship first.' };
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
export default { CONSOLIDATED_TOOLS, handleConsolidatedTool };
|