@vibecheckai/cli 3.0.4 → 3.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. package/bin/dev/run-v2-torture.js +30 -0
  2. package/bin/runners/context/index.js +1 -1
  3. package/bin/runners/lib/analyzers.js +38 -0
  4. package/bin/runners/lib/assets/vibecheck-logo.png +0 -0
  5. package/bin/runners/lib/contracts/auth-contract.js +8 -0
  6. package/bin/runners/lib/contracts/env-contract.js +3 -0
  7. package/bin/runners/lib/contracts/external-contract.js +10 -2
  8. package/bin/runners/lib/contracts/route-contract.js +7 -0
  9. package/bin/runners/lib/contracts.js +804 -0
  10. package/bin/runners/lib/detectors-v2.js +703 -0
  11. package/bin/runners/lib/drift.js +425 -0
  12. package/bin/runners/lib/entitlements-v2.js +3 -1
  13. package/bin/runners/lib/entitlements.js +11 -3
  14. package/bin/runners/lib/env-resolver.js +417 -0
  15. package/bin/runners/lib/extractors/client-calls.js +990 -0
  16. package/bin/runners/lib/extractors/fastify-route-dump.js +573 -0
  17. package/bin/runners/lib/extractors/fastify-routes.js +426 -0
  18. package/bin/runners/lib/extractors/index.js +363 -0
  19. package/bin/runners/lib/extractors/next-routes.js +524 -0
  20. package/bin/runners/lib/extractors/proof-graph.js +431 -0
  21. package/bin/runners/lib/extractors/route-matcher.js +451 -0
  22. package/bin/runners/lib/extractors/truthpack-v2.js +377 -0
  23. package/bin/runners/lib/extractors/ui-bindings.js +547 -0
  24. package/bin/runners/lib/findings-schema.js +281 -0
  25. package/bin/runners/lib/html-report.js +650 -0
  26. package/bin/runners/lib/missions/templates.js +45 -0
  27. package/bin/runners/lib/policy.js +295 -0
  28. package/bin/runners/lib/reality/correlation-detectors.js +359 -0
  29. package/bin/runners/lib/reality/index.js +318 -0
  30. package/bin/runners/lib/reality/request-hashing.js +416 -0
  31. package/bin/runners/lib/reality/request-mapper.js +453 -0
  32. package/bin/runners/lib/reality/safety-rails.js +463 -0
  33. package/bin/runners/lib/reality/semantic-snapshot.js +408 -0
  34. package/bin/runners/lib/reality/toast-detector.js +393 -0
  35. package/bin/runners/lib/report-html.js +5 -0
  36. package/bin/runners/lib/report-templates.js +5 -0
  37. package/bin/runners/lib/report.js +135 -0
  38. package/bin/runners/lib/route-truth.js +10 -10
  39. package/bin/runners/lib/schema-validator.js +350 -0
  40. package/bin/runners/lib/schemas/contracts.schema.json +160 -0
  41. package/bin/runners/lib/schemas/finding.schema.json +100 -0
  42. package/bin/runners/lib/schemas/mission-pack.schema.json +206 -0
  43. package/bin/runners/lib/schemas/proof-graph.schema.json +176 -0
  44. package/bin/runners/lib/schemas/reality-report.schema.json +162 -0
  45. package/bin/runners/lib/schemas/share-pack.schema.json +180 -0
  46. package/bin/runners/lib/schemas/ship-report.schema.json +117 -0
  47. package/bin/runners/lib/schemas/truthpack-v2.schema.json +303 -0
  48. package/bin/runners/lib/schemas/validator.js +438 -0
  49. package/bin/runners/lib/ui.js +562 -0
  50. package/bin/runners/lib/verdict-engine.js +628 -0
  51. package/bin/runners/runAIAgent.js +228 -1
  52. package/bin/runners/runBadge.js +181 -1
  53. package/bin/runners/runCtx.js +7 -2
  54. package/bin/runners/runCtxDiff.js +301 -0
  55. package/bin/runners/runGuard.js +168 -0
  56. package/bin/runners/runInitGha.js +78 -15
  57. package/bin/runners/runLabs.js +341 -0
  58. package/bin/runners/runLaunch.js +180 -1
  59. package/bin/runners/runMdc.js +203 -1
  60. package/bin/runners/runProof.zip +0 -0
  61. package/bin/runners/runProve.js +23 -0
  62. package/bin/runners/runReplay.js +114 -84
  63. package/bin/runners/runScan.js +111 -32
  64. package/bin/runners/runShip.js +23 -2
  65. package/bin/runners/runTruthpack.js +9 -7
  66. package/bin/runners/runValidate.js +161 -1
  67. package/bin/vibecheck.js +416 -770
  68. package/mcp-server/.guardrail/audit/audit.log.jsonl +2 -0
  69. package/mcp-server/.specs/architecture.mdc +90 -0
  70. package/mcp-server/.specs/security.mdc +30 -0
  71. package/mcp-server/README.md +252 -0
  72. package/mcp-server/agent-checkpoint.js +364 -0
  73. package/mcp-server/architect-tools.js +707 -0
  74. package/mcp-server/audit-mcp.js +206 -0
  75. package/mcp-server/codebase-architect-tools.js +838 -0
  76. package/mcp-server/consolidated-tools.js +804 -0
  77. package/mcp-server/hygiene-tools.js +428 -0
  78. package/mcp-server/index-v1.js +698 -0
  79. package/mcp-server/index.js +2092 -0
  80. package/mcp-server/index.old.js +4137 -0
  81. package/mcp-server/intelligence-tools.js +664 -0
  82. package/mcp-server/intent-drift-tools.js +873 -0
  83. package/mcp-server/mdc-generator.js +298 -0
  84. package/mcp-server/package-lock.json +165 -0
  85. package/mcp-server/package.json +47 -0
  86. package/mcp-server/premium-tools.js +1275 -0
  87. package/mcp-server/test-mcp.js +108 -0
  88. package/mcp-server/test-tools.js +36 -0
  89. package/mcp-server/tier-auth.js +147 -0
  90. package/mcp-server/tools/index.js +72 -0
  91. package/mcp-server/tools-reorganized.ts +244 -0
  92. package/mcp-server/truth-context.js +581 -0
  93. package/mcp-server/truth-firewall-tools.js +1500 -0
  94. package/mcp-server/vibecheck-2.0-tools.js +748 -0
  95. package/mcp-server/vibecheck-tools.js +1075 -0
  96. package/package.json +10 -8
  97. package/bin/guardrail.js +0 -834
  98. package/bin/runners/runAudit.js +0 -2
  99. package/bin/runners/runAutopilot.js +0 -2
  100. package/bin/runners/runCertify.js +0 -2
  101. package/bin/runners/runDashboard.js +0 -10
  102. package/bin/runners/runEnhancedShip.js +0 -2
  103. package/bin/runners/runFixPacks.js +0 -2
  104. package/bin/runners/runNaturalLanguage.js +0 -3
  105. package/bin/runners/runProof.js +0 -2
  106. package/bin/runners/runRealitySniff.js +0 -2
  107. package/bin/runners/runUpgrade.js +0 -2
  108. package/bin/runners/runVerifyAgentOutput.js +0 -2
@@ -0,0 +1,2092 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * vibecheck MCP Server v2.0 - Clean Product Surface
5
+ *
6
+ * 6 Public Tools (maps to CLI):
7
+ * vibecheck.scan - Find truth
8
+ * vibecheck.gate - Enforce truth in CI
9
+ * vibecheck.fix - Apply safe patches
10
+ * vibecheck.proof - Premium verification (mocks, reality)
11
+ * vibecheck.report - Access artifacts
12
+ * vibecheck.status - Health and config info
13
+ *
14
+ * Everything else is parameters on these tools.
15
+ */
16
+
17
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
18
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
19
+ import {
20
+ CallToolRequestSchema,
21
+ ListToolsRequestSchema,
22
+ ListResourcesRequestSchema,
23
+ ReadResourceRequestSchema,
24
+ } from "@modelcontextprotocol/sdk/types.js";
25
+
26
+ import fs from "fs/promises";
27
+ import path from "path";
28
+ import { fileURLToPath } from "url";
29
+ import { execSync } from "child_process";
30
+
31
+ const __filename = fileURLToPath(import.meta.url);
32
+ const __dirname = path.dirname(__filename);
33
+
34
+ const VERSION = "2.1.0";
35
+
36
+ // Import intelligence tools
37
+ import {
38
+ INTELLIGENCE_TOOLS,
39
+ handleIntelligenceTool,
40
+ } from "./intelligence-tools.js";
41
+
42
+ // Import AI vibecheck tools
43
+ import {
44
+ VIBECHECK_TOOLS,
45
+ handleVibecheckTool,
46
+ } from "./vibecheck-tools.js";
47
+
48
+ // Import agent checkpoint tools
49
+ import {
50
+ AGENT_CHECKPOINT_TOOLS,
51
+ handleCheckpointTool,
52
+ } from "./agent-checkpoint.js";
53
+
54
+ // Import architect tools
55
+ import {
56
+ ARCHITECT_TOOLS,
57
+ handleArchitectTool,
58
+ } from "./architect-tools.js";
59
+
60
+ // Import codebase architect tools
61
+ import {
62
+ CODEBASE_ARCHITECT_TOOLS,
63
+ handleCodebaseArchitectTool,
64
+ } from "./codebase-architect-tools.js";
65
+
66
+ // Import vibecheck 2.0 tools
67
+ import {
68
+ VIBECHECK_2_TOOLS,
69
+ handleVibecheck2Tool,
70
+ } from "./vibecheck-2.0-tools.js";
71
+
72
+ // Import intent drift tools
73
+ import {
74
+ intentDriftTools,
75
+ } from "./intent-drift-tools.js";
76
+
77
+ // Import audit trail for MCP
78
+ import { emitToolInvoke, emitToolComplete } from "./audit-mcp.js";
79
+
80
+ // Import MDC generator
81
+ import { mdcGeneratorTool, handleMDCGeneration } from "./mdc-generator.js";
82
+
83
+ // Import Truth Context tools (Evidence Pack / Truth Pack)
84
+ import { TRUTH_CONTEXT_TOOLS, handleTruthContextTool } from "./truth-context.js";
85
+
86
+ // Import Truth Firewall tools (Hallucination Stopper)
87
+ import { TRUTH_FIREWALL_TOOLS, handleTruthFirewallTool } from "./truth-firewall-tools.js";
88
+
89
+ // Import Consolidated Tools (15 focused tools - recommended surface)
90
+ import { CONSOLIDATED_TOOLS, handleConsolidatedTool } from "./consolidated-tools.js";
91
+
92
+ // ============================================================================
93
+ // TOOL DEFINITIONS - Public Tools (Clean Product Surface)
94
+ // ============================================================================
95
+
96
+ // RECOMMENDED: Use consolidated tools (15 focused, evidence-backed tools)
97
+ // These map directly to CLI commands and return file/line citations
98
+ const USE_CONSOLIDATED_TOOLS = process.env.VIBECHECK_MCP_CONSOLIDATED !== 'false';
99
+
100
+ const TOOLS = USE_CONSOLIDATED_TOOLS ? [
101
+ // 15 Consolidated Tools - recommended for new integrations
102
+ ...CONSOLIDATED_TOOLS,
103
+ // Keep Truth Firewall for backward compatibility
104
+ ...TRUTH_FIREWALL_TOOLS,
105
+ ] : [
106
+ // Legacy: Full tool set (50+ tools) - for backward compatibility
107
+ // PRIORITY: Truth Firewall tools (Hallucination Stopper) - agents MUST use these
108
+ ...TRUTH_FIREWALL_TOOLS, // vibecheck.get_truthpack, vibecheck.validate_claim, vibecheck.compile_context, etc.
109
+
110
+ // Truth Context tools (Evidence-Backed AI)
111
+ ...TRUTH_CONTEXT_TOOLS, // vibecheck.ctx, vibecheck.verify_claim, vibecheck.evidence
112
+
113
+ ...INTELLIGENCE_TOOLS, // Add all intelligence suite tools
114
+ ...VIBECHECK_TOOLS, // Add AI vibecheck tools (verify, quality, smells, etc.)
115
+ ...AGENT_CHECKPOINT_TOOLS, // Add agent checkpoint tools
116
+ ...ARCHITECT_TOOLS, // Add architect review/suggest tools
117
+ ...CODEBASE_ARCHITECT_TOOLS, // Add codebase-aware architect tools
118
+ ...VIBECHECK_2_TOOLS, // Add vibecheck 2.0 consolidated tools
119
+ ...intentDriftTools, // Add intent drift guard tools
120
+ mdcGeneratorTool, // Add MDC generator tool
121
+ // 1. SHIP - Quick health check (vibe coder friendly)
122
+ {
123
+ name: "vibecheck.ship",
124
+ description:
125
+ "🚀 Quick health check — 'Is my app ready?' Plain English, traffic light score",
126
+ inputSchema: {
127
+ type: "object",
128
+ properties: {
129
+ projectPath: {
130
+ type: "string",
131
+ description: "Path to project root",
132
+ default: ".",
133
+ },
134
+ fix: {
135
+ type: "boolean",
136
+ description: "Auto-fix problems where possible",
137
+ default: false,
138
+ },
139
+ },
140
+ },
141
+ },
142
+
143
+ // 2. SCAN - Deep technical analysis
144
+ {
145
+ name: "vibecheck.scan",
146
+ description:
147
+ "🔍 Deep scan — technical analysis of secrets, auth, mocks, routes (detailed output)",
148
+ inputSchema: {
149
+ type: "object",
150
+ properties: {
151
+ projectPath: {
152
+ type: "string",
153
+ description: "Path to project root",
154
+ default: ".",
155
+ },
156
+ profile: {
157
+ type: "string",
158
+ enum: ["quick", "full", "ship", "ci", "security", "compliance", "ai"],
159
+ description:
160
+ "Check profile: quick, full, ship, ci, security, compliance, ai",
161
+ default: "quick",
162
+ },
163
+ only: {
164
+ type: "array",
165
+ items: { type: "string" },
166
+ description:
167
+ "Run only specific checks: integrity, security, hygiene, contracts, auth, routes, mocks, compliance, ai",
168
+ },
169
+ format: {
170
+ type: "string",
171
+ enum: ["text", "json", "html", "sarif"],
172
+ description: "Output format",
173
+ default: "text",
174
+ },
175
+ },
176
+ },
177
+ },
178
+
179
+ // 3. VERIFY - Runtime verification with Playwright
180
+ {
181
+ name: "vibecheck.verify",
182
+ description:
183
+ "🧪 Runtime Verify — clicks buttons, fills forms, finds Dead UI with Playwright",
184
+ inputSchema: {
185
+ type: "object",
186
+ properties: {
187
+ url: {
188
+ type: "string",
189
+ description: "Target URL to test (required)",
190
+ },
191
+ auth: {
192
+ type: "string",
193
+ description: "Auth credentials (email:password)",
194
+ },
195
+ flows: {
196
+ type: "array",
197
+ items: { type: "string" },
198
+ description: "Flow packs to test: auth, ui, forms, billing",
199
+ },
200
+ headed: {
201
+ type: "boolean",
202
+ description: "Run browser in visible mode",
203
+ default: false,
204
+ },
205
+ record: {
206
+ type: "boolean",
207
+ description: "Record video of test run",
208
+ default: false,
209
+ },
210
+ },
211
+ required: ["url"],
212
+ },
213
+ },
214
+
215
+ // 3b. REALITY v2 - Two-Pass Auth Verification + Dead UI Crawler
216
+ {
217
+ name: "vibecheck.reality",
218
+ description:
219
+ "🧪 Reality Mode v2 — Two-pass auth verification: crawl anon, then auth. Finds Dead UI, HTTP errors, auth coverage gaps.",
220
+ inputSchema: {
221
+ type: "object",
222
+ properties: {
223
+ url: {
224
+ type: "string",
225
+ description: "Target URL to test (required)",
226
+ },
227
+ auth: {
228
+ type: "string",
229
+ description: "Auth credentials (email:password) for login attempt",
230
+ },
231
+ verifyAuth: {
232
+ type: "boolean",
233
+ description: "Enable two-pass auth verification (anon + auth)",
234
+ default: false,
235
+ },
236
+ storageState: {
237
+ type: "string",
238
+ description: "Path to Playwright storageState.json for pre-authenticated session",
239
+ },
240
+ saveStorageState: {
241
+ type: "string",
242
+ description: "Path to save storageState after successful login",
243
+ },
244
+ truthpack: {
245
+ type: "string",
246
+ description: "Path to truthpack.json for auth matcher verification",
247
+ },
248
+ headed: {
249
+ type: "boolean",
250
+ description: "Run browser in visible mode",
251
+ default: false,
252
+ },
253
+ maxPages: {
254
+ type: "number",
255
+ description: "Max pages to visit per pass (default: 18)",
256
+ default: 18,
257
+ },
258
+ maxDepth: {
259
+ type: "number",
260
+ description: "Max link depth to crawl (default: 2)",
261
+ default: 2,
262
+ },
263
+ danger: {
264
+ type: "boolean",
265
+ description: "Allow clicking risky buttons (delete, cancel, etc.)",
266
+ default: false,
267
+ },
268
+ },
269
+ required: ["url"],
270
+ },
271
+ },
272
+
273
+ // 4. AI-TEST - AI Agent testing
274
+ {
275
+ name: "vibecheckai.dev-test",
276
+ description:
277
+ "🤖 AI Agent — autonomous testing that explores your app and generates fix prompts",
278
+ inputSchema: {
279
+ type: "object",
280
+ properties: {
281
+ url: {
282
+ type: "string",
283
+ description: "Target URL to test (required)",
284
+ },
285
+ goal: {
286
+ type: "string",
287
+ description: "Natural language goal for the AI agent",
288
+ default: "Test all features and find issues",
289
+ },
290
+ headed: {
291
+ type: "boolean",
292
+ description: "Run browser in visible mode",
293
+ default: false,
294
+ },
295
+ },
296
+ required: ["url"],
297
+ },
298
+ },
299
+
300
+ // 3. GATE - Enforce truth in CI
301
+ {
302
+ name: "vibecheck.gate",
303
+ description: "🚦 Enforce truth in CI — fail builds on policy violations",
304
+ inputSchema: {
305
+ type: "object",
306
+ properties: {
307
+ projectPath: {
308
+ type: "string",
309
+ default: ".",
310
+ },
311
+ policy: {
312
+ type: "string",
313
+ enum: ["default", "strict", "ci"],
314
+ description: "Policy strictness level",
315
+ default: "strict",
316
+ },
317
+ sarif: {
318
+ type: "boolean",
319
+ description: "Generate SARIF for GitHub Code Scanning",
320
+ default: true,
321
+ },
322
+ },
323
+ },
324
+ },
325
+
326
+ // 5. FIX - Fix Missions v1
327
+ {
328
+ name: "vibecheck.fix",
329
+ description:
330
+ "🔧 Fix Missions v1 — AI-powered surgical fixes with proof verification loop",
331
+ inputSchema: {
332
+ type: "object",
333
+ properties: {
334
+ projectPath: {
335
+ type: "string",
336
+ default: ".",
337
+ },
338
+ promptOnly: {
339
+ type: "boolean",
340
+ description: "Generate mission prompts only (no edits)",
341
+ default: false,
342
+ },
343
+ apply: {
344
+ type: "boolean",
345
+ description: "Apply patches returned by the model",
346
+ default: false,
347
+ },
348
+ autopilot: {
349
+ type: "boolean",
350
+ description: "Loop: fix → verify → fix until SHIP or stuck",
351
+ default: false,
352
+ },
353
+ share: {
354
+ type: "boolean",
355
+ description: "Generate share bundle for review",
356
+ default: false,
357
+ },
358
+ maxMissions: {
359
+ type: "number",
360
+ description: "Max missions to plan (default: 8)",
361
+ default: 8,
362
+ },
363
+ maxSteps: {
364
+ type: "number",
365
+ description: "Max autopilot steps (default: 10)",
366
+ default: 10,
367
+ },
368
+ },
369
+ },
370
+ },
371
+
372
+ // 6. SHARE - Generate share bundle from fix missions
373
+ {
374
+ name: "vibecheck.share",
375
+ description: "📦 Share Bundle — generate PR comment / review bundle from latest fix missions",
376
+ inputSchema: {
377
+ type: "object",
378
+ properties: {
379
+ projectPath: {
380
+ type: "string",
381
+ default: ".",
382
+ },
383
+ prComment: {
384
+ type: "boolean",
385
+ description: "Output GitHub PR comment format",
386
+ default: false,
387
+ },
388
+ out: {
389
+ type: "string",
390
+ description: "Write output to file path",
391
+ },
392
+ },
393
+ },
394
+ },
395
+
396
+ // 7. PROVE - One Command Reality Proof (orchestrates ctx → reality → ship → fix)
397
+ {
398
+ name: "vibecheck.prove",
399
+ description: "🔬 One Command Reality Proof — orchestrates ctx → reality → ship → fix loop until SHIP or stuck",
400
+ inputSchema: {
401
+ type: "object",
402
+ properties: {
403
+ projectPath: {
404
+ type: "string",
405
+ default: ".",
406
+ },
407
+ url: {
408
+ type: "string",
409
+ description: "Base URL for runtime testing",
410
+ },
411
+ auth: {
412
+ type: "string",
413
+ description: "Auth credentials (email:password)",
414
+ },
415
+ storageState: {
416
+ type: "string",
417
+ description: "Path to Playwright storageState.json",
418
+ },
419
+ maxFixRounds: {
420
+ type: "number",
421
+ description: "Max auto-fix attempts (default: 3)",
422
+ default: 3,
423
+ },
424
+ skipReality: {
425
+ type: "boolean",
426
+ description: "Skip runtime crawling (static only)",
427
+ default: false,
428
+ },
429
+ skipFix: {
430
+ type: "boolean",
431
+ description: "Don't auto-fix, just diagnose",
432
+ default: false,
433
+ },
434
+ headed: {
435
+ type: "boolean",
436
+ description: "Run browser in visible mode",
437
+ default: false,
438
+ },
439
+ danger: {
440
+ type: "boolean",
441
+ description: "Allow clicking risky buttons",
442
+ default: false,
443
+ },
444
+ },
445
+ },
446
+ },
447
+
448
+ // 8. CTX - Truth Pack Generator
449
+ {
450
+ name: "vibecheck.ctx",
451
+ description: "📦 Truth Pack — generate ground truth for AI agents (routes, env, auth, billing)",
452
+ inputSchema: {
453
+ type: "object",
454
+ properties: {
455
+ projectPath: {
456
+ type: "string",
457
+ default: ".",
458
+ },
459
+ snapshot: {
460
+ type: "boolean",
461
+ description: "Save timestamped snapshot",
462
+ default: false,
463
+ },
464
+ json: {
465
+ type: "boolean",
466
+ description: "Output raw JSON",
467
+ default: false,
468
+ },
469
+ },
470
+ },
471
+ },
472
+
473
+ // 9. PROOF - Premium verification
474
+ {
475
+ name: "vibecheck.proof",
476
+ description:
477
+ "🎬 Premium verification — mocks (static) or reality (runtime with Playwright)",
478
+ inputSchema: {
479
+ type: "object",
480
+ properties: {
481
+ projectPath: {
482
+ type: "string",
483
+ default: ".",
484
+ },
485
+ mode: {
486
+ type: "string",
487
+ enum: ["mocks", "reality"],
488
+ description:
489
+ "Proof mode: mocks (import graph + fake domains) or reality (Playwright runtime)",
490
+ },
491
+ url: {
492
+ type: "string",
493
+ description: "Base URL for reality mode",
494
+ default: "http://localhost:3000",
495
+ },
496
+ flow: {
497
+ type: "string",
498
+ enum: ["auth", "checkout", "dashboard"],
499
+ description: "Flow to test in reality mode",
500
+ default: "auth",
501
+ },
502
+ },
503
+ required: ["mode"],
504
+ },
505
+ },
506
+
507
+ // 5. REPORT - Access artifacts
508
+ {
509
+ name: "vibecheck.validate",
510
+ description:
511
+ "🤖 Validate AI-generated code. Checks for hallucinations, intent mismatch, and quality issues.",
512
+ inputSchema: {
513
+ type: "object",
514
+ properties: {
515
+ code: { type: "string", description: "The code content to validate" },
516
+ intent: {
517
+ type: "string",
518
+ description: "The user's original request/intent",
519
+ },
520
+ projectPath: { type: "string", default: "." },
521
+ },
522
+ required: ["code"],
523
+ },
524
+ },
525
+ // 10. REPORT - Access scan artifacts
526
+ {
527
+ name: "vibecheck.report",
528
+ description:
529
+ "📄 Access scan artifacts — summary, full report, SARIF export",
530
+ inputSchema: {
531
+ type: "object",
532
+ properties: {
533
+ projectPath: {
534
+ type: "string",
535
+ default: ".",
536
+ },
537
+ type: {
538
+ type: "string",
539
+ enum: ["summary", "full", "sarif", "html"],
540
+ description: "Report type to retrieve",
541
+ default: "summary",
542
+ },
543
+ runId: {
544
+ type: "string",
545
+ description: "Specific run ID (defaults to last run)",
546
+ },
547
+ },
548
+ },
549
+ },
550
+
551
+ // 11. STATUS - Health and config
552
+ {
553
+ name: "vibecheck.status",
554
+ description: "📊 Server status — health, versions, config, last run info",
555
+ inputSchema: {
556
+ type: "object",
557
+ properties: {
558
+ projectPath: {
559
+ type: "string",
560
+ default: ".",
561
+ },
562
+ },
563
+ },
564
+ },
565
+
566
+ // 12. AUTOPILOT - Continuous protection
567
+ {
568
+ name: "vibecheck.autopilot",
569
+ description:
570
+ "🤖 Autopilot — continuous protection with weekly reports, auto-PRs, deploy blocking",
571
+ inputSchema: {
572
+ type: "object",
573
+ properties: {
574
+ projectPath: {
575
+ type: "string",
576
+ default: ".",
577
+ },
578
+ action: {
579
+ type: "string",
580
+ enum: ["status", "enable", "disable", "digest"],
581
+ description: "Autopilot action",
582
+ default: "status",
583
+ },
584
+ slack: {
585
+ type: "string",
586
+ description: "Slack webhook URL for notifications",
587
+ },
588
+ email: {
589
+ type: "string",
590
+ description: "Email for weekly digest",
591
+ },
592
+ },
593
+ },
594
+ },
595
+
596
+ // 13. AUTOPILOT PLAN - Generate fix plan (Pro/Compliance)
597
+ {
598
+ name: "vibecheck.autopilot_plan",
599
+ description:
600
+ "🤖 Autopilot Plan — scan codebase, group issues into fix packs, estimate risk (Pro/Compliance)",
601
+ inputSchema: {
602
+ type: "object",
603
+ properties: {
604
+ projectPath: {
605
+ type: "string",
606
+ default: ".",
607
+ },
608
+ profile: {
609
+ type: "string",
610
+ enum: ["quick", "full", "ship", "ci"],
611
+ description: "Scan profile",
612
+ default: "ship",
613
+ },
614
+ maxFixes: {
615
+ type: "number",
616
+ description: "Max fixes per category",
617
+ default: 10,
618
+ },
619
+ },
620
+ },
621
+ },
622
+
623
+ // 14. AUTOPILOT APPLY - Apply fixes (Pro/Compliance)
624
+ {
625
+ name: "vibecheck.autopilot_apply",
626
+ description:
627
+ "🔧 Autopilot Apply — apply fix packs with verification, re-scan to confirm (Pro/Compliance)",
628
+ inputSchema: {
629
+ type: "object",
630
+ properties: {
631
+ projectPath: {
632
+ type: "string",
633
+ default: ".",
634
+ },
635
+ profile: {
636
+ type: "string",
637
+ enum: ["quick", "full", "ship", "ci"],
638
+ description: "Scan profile",
639
+ default: "ship",
640
+ },
641
+ maxFixes: {
642
+ type: "number",
643
+ description: "Max fixes per category",
644
+ default: 10,
645
+ },
646
+ verify: {
647
+ type: "boolean",
648
+ description: "Run verification after apply",
649
+ default: true,
650
+ },
651
+ dryRun: {
652
+ type: "boolean",
653
+ description: "Preview changes without applying",
654
+ default: false,
655
+ },
656
+ },
657
+ },
658
+ },
659
+
660
+ // 15. BADGE - Generate ship badge
661
+ {
662
+ name: "vibecheck.badge",
663
+ description:
664
+ "🏅 Ship Badge — generate a badge for README/PR showing scan status",
665
+ inputSchema: {
666
+ type: "object",
667
+ properties: {
668
+ projectPath: {
669
+ type: "string",
670
+ default: ".",
671
+ },
672
+ format: {
673
+ type: "string",
674
+ enum: ["svg", "md", "html"],
675
+ description: "Badge format",
676
+ default: "svg",
677
+ },
678
+ style: {
679
+ type: "string",
680
+ enum: ["flat", "flat-square"],
681
+ description: "Badge style",
682
+ default: "flat",
683
+ },
684
+ },
685
+ },
686
+ },
687
+
688
+ // 16. CONTEXT - AI Rules Generator
689
+ {
690
+ name: "vibecheck.context",
691
+ description:
692
+ "🧠 AI Context — generate rules files for Cursor, Windsurf, Copilot to understand your codebase",
693
+ inputSchema: {
694
+ type: "object",
695
+ properties: {
696
+ projectPath: {
697
+ type: "string",
698
+ description: "Path to project root",
699
+ default: ".",
700
+ },
701
+ platform: {
702
+ type: "string",
703
+ enum: ["all", "cursor", "windsurf", "copilot", "claude"],
704
+ description: "Target platform (default: all)",
705
+ default: "all",
706
+ },
707
+ },
708
+ },
709
+ },
710
+ ];
711
+
712
+ // ============================================================================
713
+ // SERVER IMPLEMENTATION
714
+ // ============================================================================
715
+
716
+ class VibecheckMCP {
717
+ constructor() {
718
+ this.server = new Server(
719
+ { name: "vibecheck", version: VERSION },
720
+ { capabilities: { tools: {}, resources: {} } },
721
+ );
722
+ this.setupHandlers();
723
+ }
724
+
725
+ setupHandlers() {
726
+ // List tools
727
+ this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
728
+ tools: TOOLS,
729
+ }));
730
+
731
+ // Call tool
732
+ this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
733
+ const { name, arguments: args } = request.params;
734
+ const projectPath = path.resolve(args?.projectPath || ".");
735
+ const startTime = Date.now();
736
+
737
+ // Emit audit event for tool invocation start
738
+ emitToolInvoke(name, args, "success", { projectPath });
739
+
740
+ try {
741
+ // Handle intelligence tools first
742
+ if (name.startsWith("vibecheck.intelligence.")) {
743
+ return await handleIntelligenceTool(name, args, __dirname);
744
+ }
745
+
746
+ // Handle AI vibecheck tools (verify, quality, smells, hallucination, breaking, mdc, coverage)
747
+ if (["vibecheck.verify", "vibecheck.quality", "vibecheck.smells",
748
+ "vibecheck.hallucination", "vibecheck.breaking", "vibecheck.mdc",
749
+ "vibecheck.coverage"].includes(name)) {
750
+ const result = await handleVibecheckTool(name, args);
751
+ return {
752
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
753
+ };
754
+ }
755
+
756
+ // Handle agent checkpoint tools
757
+ if (["vibecheck_checkpoint", "vibecheck_set_strictness", "vibecheck_checkpoint_status"].includes(name)) {
758
+ return await handleCheckpointTool(name, args);
759
+ }
760
+
761
+ // Handle architect tools
762
+ if (["vibecheck_architect_review", "vibecheck_architect_suggest",
763
+ "vibecheck_architect_patterns", "vibecheck_architect_set_strictness"].includes(name)) {
764
+ return await handleArchitectTool(name, args);
765
+ }
766
+
767
+ // Handle codebase architect tools
768
+ if (["vibecheck_architect_context", "vibecheck_architect_guide",
769
+ "vibecheck_architect_validate", "vibecheck_architect_patterns",
770
+ "vibecheck_architect_dependencies"].includes(name)) {
771
+ return await handleCodebaseArchitectTool(name, args);
772
+ }
773
+
774
+ // Handle vibecheck 2.0 tools
775
+ if (["checkpoint", "check", "ship", "fix", "status", "set_strictness"].includes(name)) {
776
+ return await handleVibecheck2Tool(name, args, __dirname);
777
+ }
778
+
779
+ // Handle intent drift tools
780
+ if (name.startsWith("vibecheck_intent_")) {
781
+ const tool = intentDriftTools.find(t => t.name === name);
782
+ if (tool && tool.handler) {
783
+ const result = await tool.handler(args);
784
+ return {
785
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
786
+ };
787
+ }
788
+ }
789
+
790
+ switch (name) {
791
+ case "vibecheck.ship":
792
+ return await this.handleShip(projectPath, args);
793
+ case "vibecheck.scan":
794
+ return await this.handleScan(projectPath, args);
795
+ case "vibecheck.verify":
796
+ return await this.handleVerify(projectPath, args);
797
+ case "vibecheck.reality":
798
+ return await this.handleReality(projectPath, args);
799
+ case "vibecheckai.dev-test":
800
+ return await this.handleAITest(projectPath, args);
801
+ case "vibecheck.gate":
802
+ return await this.handleGate(projectPath, args);
803
+ case "vibecheck.fix":
804
+ return await this.handleFix(projectPath, args);
805
+ case "vibecheck.share":
806
+ return await this.handleShare(projectPath, args);
807
+ case "vibecheck.ctx":
808
+ return await this.handleCtx(projectPath, args);
809
+ case "vibecheck.prove":
810
+ return await this.handleProve(projectPath, args);
811
+ case "vibecheck.proof":
812
+ return await this.handleProof(projectPath, args);
813
+ case "vibecheck.validate":
814
+ return await this.handleValidate(projectPath, args);
815
+ case "vibecheck.report":
816
+ return await this.handleReport(projectPath, args);
817
+ case "vibecheck.status":
818
+ return await this.handleStatus(projectPath, args);
819
+ case "vibecheck.autopilot":
820
+ return await this.handleAutopilot(projectPath, args);
821
+ case "vibecheck.autopilot_plan":
822
+ return await this.handleAutopilotPlan(projectPath, args);
823
+ case "vibecheck.autopilot_apply":
824
+ return await this.handleAutopilotApply(projectPath, args);
825
+ case "vibecheck.badge":
826
+ return await this.handleBadge(projectPath, args);
827
+ case "vibecheck.context":
828
+ return await this.handleContext(projectPath, args);
829
+ case "generate_mdc":
830
+ return await handleMDCGeneration(args);
831
+ // Truth Context tools (Evidence Pack / Truth Pack)
832
+ case "vibecheck.verify_claim":
833
+ case "vibecheck.evidence":
834
+ return await handleTruthContextTool(name, args);
835
+ // Truth Firewall tools (Hallucination Stopper)
836
+ case "vibecheck.get_truthpack":
837
+ case "vibecheck.validate_claim":
838
+ case "vibecheck.compile_context":
839
+ case "vibecheck.search_evidence":
840
+ case "vibecheck.find_counterexamples":
841
+ case "vibecheck.propose_patch":
842
+ case "vibecheck.check_invariants":
843
+ case "vibecheck.add_assumption":
844
+ return await handleTruthFirewallTool(name, args, projectPath);
845
+ default:
846
+ return this.error(`Unknown tool: ${name}`);
847
+ }
848
+ } catch (err) {
849
+ // Emit audit event for tool error
850
+ emitToolComplete(name, "error", {
851
+ errorMessage: err.message,
852
+ durationMs: Date.now() - startTime
853
+ });
854
+ return this.error(`${name} failed: ${err.message}`);
855
+ }
856
+ });
857
+
858
+ // Resources
859
+ this.server.setRequestHandler(ListResourcesRequestSchema, async () => ({
860
+ resources: [
861
+ {
862
+ uri: "vibecheck://config",
863
+ name: "vibecheck Config",
864
+ description: "Project vibecheck configuration",
865
+ mimeType: "application/json",
866
+ },
867
+ {
868
+ uri: "vibecheck://summary",
869
+ name: "Last Scan Summary",
870
+ description: "Most recent scan results and verdict",
871
+ mimeType: "application/json",
872
+ },
873
+ {
874
+ uri: "vibecheck://truthpack",
875
+ name: "Truth Pack",
876
+ description: "Ground truth: routes, env, auth, billing",
877
+ mimeType: "application/json",
878
+ },
879
+ {
880
+ uri: "vibecheck://missions",
881
+ name: "Fix Missions",
882
+ description: "Latest mission pack for AI-powered fixes",
883
+ mimeType: "application/json",
884
+ },
885
+ {
886
+ uri: "vibecheck://reality",
887
+ name: "Reality Results",
888
+ description: "Last runtime verification results",
889
+ mimeType: "application/json",
890
+ },
891
+ {
892
+ uri: "vibecheck://findings",
893
+ name: "All Findings",
894
+ description: "Complete list of detected issues",
895
+ mimeType: "application/json",
896
+ },
897
+ {
898
+ uri: "vibecheck://share",
899
+ name: "Share Pack",
900
+ description: "Latest fix missions share bundle for PR/review",
901
+ mimeType: "application/json",
902
+ },
903
+ {
904
+ uri: "vibecheck://prove",
905
+ name: "Prove Report",
906
+ description: "Last prove loop results (ctx → reality → ship → fix)",
907
+ mimeType: "application/json",
908
+ },
909
+ ],
910
+ }));
911
+
912
+ this.server.setRequestHandler(
913
+ ReadResourceRequestSchema,
914
+ async (request) => {
915
+ const { uri } = request.params;
916
+ const projectPath = process.cwd();
917
+
918
+ if (uri === "vibecheck://config") {
919
+ const configPath = path.join(projectPath, "vibecheck.config.json");
920
+ try {
921
+ const content = await fs.readFile(configPath, "utf-8");
922
+ return {
923
+ contents: [{ uri, mimeType: "application/json", text: content }],
924
+ };
925
+ } catch {
926
+ return {
927
+ contents: [{ uri, mimeType: "application/json", text: "{}" }],
928
+ };
929
+ }
930
+ }
931
+
932
+ if (uri === "vibecheck://summary") {
933
+ const summaryPath = path.join(
934
+ projectPath,
935
+ ".vibecheck",
936
+ "summary.json",
937
+ );
938
+ try {
939
+ const content = await fs.readFile(summaryPath, "utf-8");
940
+ return {
941
+ contents: [{ uri, mimeType: "application/json", text: content }],
942
+ };
943
+ } catch {
944
+ return {
945
+ contents: [
946
+ {
947
+ uri,
948
+ mimeType: "application/json",
949
+ text: '{"message": "No scan found. Run vibecheck.scan first."}',
950
+ },
951
+ ],
952
+ };
953
+ }
954
+ }
955
+
956
+ if (uri === "vibecheck://truthpack") {
957
+ const truthpackPath = path.join(projectPath, ".vibecheck", "truth", "truthpack.json");
958
+ try {
959
+ const content = await fs.readFile(truthpackPath, "utf-8");
960
+ return { contents: [{ uri, mimeType: "application/json", text: content }] };
961
+ } catch {
962
+ return { contents: [{ uri, mimeType: "application/json", text: '{"message": "No truthpack. Run vibecheck.ctx first."}' }] };
963
+ }
964
+ }
965
+
966
+ if (uri === "vibecheck://missions") {
967
+ const missionsDir = path.join(projectPath, ".vibecheck", "missions");
968
+ try {
969
+ const dirs = await fs.readdir(missionsDir);
970
+ const latest = dirs.sort().reverse()[0];
971
+ if (latest) {
972
+ const missionPath = path.join(missionsDir, latest, "missions.json");
973
+ const content = await fs.readFile(missionPath, "utf-8");
974
+ return { contents: [{ uri, mimeType: "application/json", text: content }] };
975
+ }
976
+ } catch {}
977
+ return { contents: [{ uri, mimeType: "application/json", text: '{"message": "No missions. Run vibecheck.fix first."}' }] };
978
+ }
979
+
980
+ if (uri === "vibecheck://reality") {
981
+ const realityPath = path.join(projectPath, ".vibecheck", "reality", "last_reality.json");
982
+ try {
983
+ const content = await fs.readFile(realityPath, "utf-8");
984
+ return { contents: [{ uri, mimeType: "application/json", text: content }] };
985
+ } catch {
986
+ return { contents: [{ uri, mimeType: "application/json", text: '{"message": "No reality results. Run vibecheck verify first."}' }] };
987
+ }
988
+ }
989
+
990
+ if (uri === "vibecheck://findings") {
991
+ const findingsPath = path.join(projectPath, ".vibecheck", "findings.json");
992
+ try {
993
+ const content = await fs.readFile(findingsPath, "utf-8");
994
+ return { contents: [{ uri, mimeType: "application/json", text: content }] };
995
+ } catch {
996
+ // Try summary.json as fallback
997
+ const summaryPath = path.join(projectPath, ".vibecheck", "summary.json");
998
+ try {
999
+ const summary = JSON.parse(await fs.readFile(summaryPath, "utf-8"));
1000
+ const findings = summary.findings || [];
1001
+ return { contents: [{ uri, mimeType: "application/json", text: JSON.stringify({ findings }, null, 2) }] };
1002
+ } catch {
1003
+ return { contents: [{ uri, mimeType: "application/json", text: '{"message": "No findings. Run vibecheck.scan first."}' }] };
1004
+ }
1005
+ }
1006
+ }
1007
+
1008
+ if (uri === "vibecheck://share") {
1009
+ const missionsDir = path.join(projectPath, ".vibecheck", "missions");
1010
+ try {
1011
+ const dirs = await fs.readdir(missionsDir);
1012
+ const latest = dirs.sort().reverse()[0];
1013
+ if (latest) {
1014
+ const sharePath = path.join(missionsDir, latest, "share", "share.json");
1015
+ try {
1016
+ const content = await fs.readFile(sharePath, "utf-8");
1017
+ return { contents: [{ uri, mimeType: "application/json", text: content }] };
1018
+ } catch {
1019
+ // Fallback to missions.json
1020
+ const missionPath = path.join(missionsDir, latest, "missions.json");
1021
+ const content = await fs.readFile(missionPath, "utf-8");
1022
+ return { contents: [{ uri, mimeType: "application/json", text: content }] };
1023
+ }
1024
+ }
1025
+ } catch {}
1026
+ return { contents: [{ uri, mimeType: "application/json", text: '{"message": "No share pack. Run vibecheck.fix --share first."}' }] };
1027
+ }
1028
+
1029
+ if (uri === "vibecheck://prove") {
1030
+ const provePath = path.join(projectPath, ".vibecheck", "prove", "last_prove.json");
1031
+ try {
1032
+ const content = await fs.readFile(provePath, "utf-8");
1033
+ return { contents: [{ uri, mimeType: "application/json", text: content }] };
1034
+ } catch {
1035
+ return { contents: [{ uri, mimeType: "application/json", text: '{"message": "No prove results. Run vibecheck.prove first."}' }] };
1036
+ }
1037
+ }
1038
+
1039
+ return { contents: [] };
1040
+ },
1041
+ );
1042
+ }
1043
+
1044
+ // Helpers
1045
+ success(text) {
1046
+ return { content: [{ type: "text", text }] };
1047
+ }
1048
+
1049
+ error(text) {
1050
+ return { content: [{ type: "text", text: `❌ ${text}` }], isError: true };
1051
+ }
1052
+
1053
+ // Validate project path exists and is accessible
1054
+ validateProjectPath(projectPath) {
1055
+ try {
1056
+ const stats = require("fs").statSync(projectPath);
1057
+ if (!stats.isDirectory()) {
1058
+ return { valid: false, error: `Path is not a directory: ${projectPath}` };
1059
+ }
1060
+ return { valid: true };
1061
+ } catch (e) {
1062
+ return { valid: false, error: `Cannot access path: ${projectPath} (${e.code || e.message})` };
1063
+ }
1064
+ }
1065
+
1066
+ // ============================================================================
1067
+ // SCAN
1068
+ // ============================================================================
1069
+ async handleScan(projectPath, args) {
1070
+ const profile = args?.profile || "quick";
1071
+ const format = args?.format || "text";
1072
+ const only = args?.only;
1073
+
1074
+ let output = "# 🔍 vibecheck Scan\n\n";
1075
+ output += `**Profile:** ${profile}\n`;
1076
+ output += `**Path:** ${projectPath}\n\n`;
1077
+
1078
+ try {
1079
+ // Build CLI command
1080
+ let cmd = `node "${path.join(__dirname, "..", "bin", "vibecheck.js")}" scan`;
1081
+ cmd += ` --profile=${profile}`;
1082
+ if (only?.length) cmd += ` --only=${only.join(",")}`;
1083
+ cmd += ` --json`;
1084
+
1085
+ const result = execSync(cmd, {
1086
+ cwd: projectPath,
1087
+ encoding: "utf8",
1088
+ maxBuffer: 10 * 1024 * 1024,
1089
+ });
1090
+
1091
+ // Read summary
1092
+ const summaryPath = path.join(projectPath, ".vibecheck", "summary.json");
1093
+ const summary = JSON.parse(await fs.readFile(summaryPath, "utf-8"));
1094
+
1095
+ output += `## Score: ${summary.score}/100 (${summary.grade})\n\n`;
1096
+ output += `**Verdict:** ${summary.canShip ? "✅ SHIP" : "🚫 NO-SHIP"}\n\n`;
1097
+
1098
+ if (summary.counts) {
1099
+ output += "### Checks\n\n";
1100
+ output += "| Category | Issues |\n|----------|--------|\n";
1101
+ for (const [key, count] of Object.entries(summary.counts)) {
1102
+ const icon = count === 0 ? "✅" : "⚠️";
1103
+ output += `| ${icon} ${key} | ${count} |\n`;
1104
+ }
1105
+ }
1106
+
1107
+ output += `\n📄 **Report:** .vibecheck/report.html\n`;
1108
+ } catch (err) {
1109
+ output += `\n⚠️ Scan error: ${err.message}\n`;
1110
+ }
1111
+
1112
+ output += "\n---\n_Context Enhanced by vibecheck AI_\n";
1113
+ return this.success(output);
1114
+ }
1115
+
1116
+ // ============================================================================
1117
+ // GATE
1118
+ // ============================================================================
1119
+ async handleGate(projectPath, args) {
1120
+ const policy = args?.policy || "strict";
1121
+
1122
+ let output = "# 🚦 vibecheck Gate\n\n";
1123
+ output += `**Policy:** ${policy}\n\n`;
1124
+
1125
+ try {
1126
+ let cmd = `node "${path.join(__dirname, "..", "bin", "vibecheck.js")}" gate`;
1127
+ cmd += ` --policy=${policy}`;
1128
+ if (args?.sarif) cmd += ` --sarif`;
1129
+
1130
+ execSync(cmd, {
1131
+ cwd: projectPath,
1132
+ encoding: "utf8",
1133
+ maxBuffer: 10 * 1024 * 1024,
1134
+ });
1135
+
1136
+ output += "## ✅ GATE PASSED\n\n";
1137
+ output += "All checks passed. Clear to merge.\n";
1138
+ } catch (err) {
1139
+ output += "## 🚫 GATE FAILED\n\n";
1140
+ output += "Build blocked. Fix the issues and re-run.\n\n";
1141
+ output += `Run \`vibecheck fix --plan\` to see recommended fixes.\n`;
1142
+ }
1143
+
1144
+ return this.success(output);
1145
+ }
1146
+
1147
+ // ============================================================================
1148
+ // FIX MISSIONS v1
1149
+ // ============================================================================
1150
+ async handleFix(projectPath, args) {
1151
+ const mode = args?.autopilot ? "Autopilot" :
1152
+ args?.apply ? "Apply" :
1153
+ args?.promptOnly ? "Prompt Only" : "Plan";
1154
+
1155
+ let output = "# 🛠 vibecheck Fix Missions v1\n\n";
1156
+ output += `**Mode:** ${mode}\n`;
1157
+ output += `**Max Missions:** ${args?.maxMissions || 8}\n`;
1158
+ if (args?.autopilot) output += `**Max Steps:** ${args?.maxSteps || 10}\n`;
1159
+ output += "\n";
1160
+
1161
+ try {
1162
+ let cmd = `node "${path.join(__dirname, "..", "bin", "vibecheck.js")}" fix`;
1163
+ if (args?.promptOnly) cmd += ` --prompt-only`;
1164
+ if (args?.apply) cmd += ` --apply`;
1165
+ if (args?.autopilot) cmd += ` --autopilot`;
1166
+ if (args?.share) cmd += ` --share`;
1167
+ if (args?.maxMissions) cmd += ` --max-missions ${args.maxMissions}`;
1168
+ if (args?.maxSteps) cmd += ` --max-steps ${args.maxSteps}`;
1169
+
1170
+ const result = execSync(cmd, {
1171
+ cwd: projectPath,
1172
+ encoding: "utf8",
1173
+ maxBuffer: 10 * 1024 * 1024,
1174
+ timeout: 300000, // 5 min timeout for autopilot
1175
+ env: { ...process.env, VIBECHECK_SKIP_AUTH: "1" },
1176
+ });
1177
+
1178
+ output += result.replace(/\x1b\[[0-9;]*m/g, ""); // Strip ANSI codes
1179
+
1180
+ // Read mission pack if available
1181
+ const missionsDir = path.join(projectPath, ".vibecheck", "missions");
1182
+ try {
1183
+ const dirs = await fs.readdir(missionsDir);
1184
+ const latest = dirs.sort().reverse()[0];
1185
+ if (latest) {
1186
+ const missionPath = path.join(missionsDir, latest, "missions.json");
1187
+ const missions = JSON.parse(await fs.readFile(missionPath, "utf-8"));
1188
+
1189
+ output += "\n## Planned Missions\n\n";
1190
+ output += "| # | Mission | Target Findings |\n|---|---------|----------------|\n";
1191
+ for (let i = 0; i < missions.missions.length; i++) {
1192
+ const m = missions.missions[i];
1193
+ output += `| ${i + 1} | ${m.title} | ${m.targetFindingIds.length} |\n`;
1194
+ }
1195
+ output += `\n📁 **Mission Pack:** .vibecheck/missions/${latest}\n`;
1196
+ }
1197
+ } catch {}
1198
+ } catch (err) {
1199
+ output += `\n⚠️ Fix error: ${err.message}\n`;
1200
+ if (err.stdout) output += `\n${err.stdout.replace(/\x1b\[[0-9;]*m/g, "")}\n`;
1201
+ }
1202
+
1203
+ output += "\n---\n_Fix Missions v1 — Reality Firewall Protected_\n";
1204
+ return this.success(output);
1205
+ }
1206
+
1207
+ // ============================================================================
1208
+ // SHARE - Generate share bundle from fix missions
1209
+ // ============================================================================
1210
+ async handleShare(projectPath, args) {
1211
+ let output = "# 📦 vibecheck Share Bundle\n\n";
1212
+
1213
+ try {
1214
+ let cmd = `node "${path.join(__dirname, "..", "bin", "vibecheck.js")}" share`;
1215
+ if (args?.prComment) cmd += ` --pr-comment`;
1216
+ if (args?.out) cmd += ` --out "${args.out}"`;
1217
+
1218
+ const result = execSync(cmd, {
1219
+ cwd: projectPath,
1220
+ encoding: "utf8",
1221
+ maxBuffer: 10 * 1024 * 1024,
1222
+ env: { ...process.env, VIBECHECK_SKIP_AUTH: "1" },
1223
+ });
1224
+
1225
+ output += result.replace(/\x1b\[[0-9;]*m/g, "");
1226
+
1227
+ // Read share pack if available
1228
+ const missionsDir = path.join(projectPath, ".vibecheck", "missions");
1229
+ try {
1230
+ const dirs = await fs.readdir(missionsDir);
1231
+ const latest = dirs.sort().reverse()[0];
1232
+ if (latest) {
1233
+ const sharePath = path.join(missionsDir, latest, "share", "share.json");
1234
+ try {
1235
+ const share = JSON.parse(await fs.readFile(sharePath, "utf-8"));
1236
+
1237
+ output += "\n## Share Pack Summary\n\n";
1238
+ output += `| Metric | Value |\n|--------|-------|\n`;
1239
+ output += `| Mission Pack | ${latest} |\n`;
1240
+ output += `| Total Steps | ${share.timeline?.length || 0} |\n`;
1241
+ output += `| Final Verdict | ${share.finalVerdict || "Unknown"} |\n`;
1242
+
1243
+ if (share.timeline?.length > 0) {
1244
+ output += "\n## Timeline\n\n";
1245
+ output += "| Step | Mission | Before | After |\n|------|---------|--------|-------|\n";
1246
+ for (const t of share.timeline.slice(0, 10)) {
1247
+ output += `| ${t.step} | ${t.missionTitle || t.missionId} | ${t.beforeVerdict} | ${t.afterVerdict} |\n`;
1248
+ }
1249
+ }
1250
+
1251
+ output += `\n📁 **Share Pack:** .vibecheck/missions/${latest}/share/\n`;
1252
+ output += `📄 **PR Comment:** .vibecheck/missions/${latest}/share/pr_comment.md\n`;
1253
+ } catch {}
1254
+ }
1255
+ } catch {}
1256
+ } catch (err) {
1257
+ output += `\n⚠️ Share error: ${err.message}\n`;
1258
+ if (err.stdout) output += `\n${err.stdout.replace(/\x1b\[[0-9;]*m/g, "")}\n`;
1259
+ }
1260
+
1261
+ output += "\n---\n_Fix Missions Share Bundle_\n";
1262
+ return this.success(output);
1263
+ }
1264
+
1265
+ // ============================================================================
1266
+ // CTX - Truth Pack Generator
1267
+ // ============================================================================
1268
+ async handleCtx(projectPath, args) {
1269
+ let output = "# 📦 vibecheck Truth Pack\n\n";
1270
+ output += `**Path:** ${projectPath}\n\n`;
1271
+
1272
+ try {
1273
+ let cmd = `node "${path.join(__dirname, "..", "bin", "vibecheck.js")}" ctx`;
1274
+ if (args?.snapshot) cmd += ` --snapshot`;
1275
+ if (args?.json) cmd += ` --json`;
1276
+
1277
+ const result = execSync(cmd, {
1278
+ cwd: projectPath,
1279
+ encoding: "utf8",
1280
+ maxBuffer: 10 * 1024 * 1024,
1281
+ env: { ...process.env, VIBECHECK_SKIP_AUTH: "1" },
1282
+ });
1283
+
1284
+ if (args?.json) {
1285
+ // Return raw JSON
1286
+ output = result;
1287
+ return this.success(output);
1288
+ }
1289
+
1290
+ output += result.replace(/\x1b\[[0-9;]*m/g, ""); // Strip ANSI codes
1291
+
1292
+ // Read truthpack summary
1293
+ const truthpackPath = path.join(projectPath, ".vibecheck", "truth", "truthpack.json");
1294
+ try {
1295
+ const truthpack = JSON.parse(await fs.readFile(truthpackPath, "utf-8"));
1296
+
1297
+ output += "\n## Truth Pack Contents\n\n";
1298
+ output += `| Category | Count |\n|----------|-------|\n`;
1299
+ output += `| Server Routes | ${truthpack.routes?.server?.length || 0} |\n`;
1300
+ output += `| Client Refs | ${truthpack.routes?.clientRefs?.length || 0} |\n`;
1301
+ output += `| Route Gaps | ${truthpack.routes?.gaps?.length || 0} |\n`;
1302
+ output += `| Env Used | ${truthpack.env?.vars?.length || 0} |\n`;
1303
+ output += `| Env Declared | ${truthpack.env?.declared?.length || 0} |\n`;
1304
+ output += `| Auth Middleware | ${truthpack.auth?.nextMiddleware?.length || 0} |\n`;
1305
+ output += `| Stripe Detected | ${truthpack.billing?.hasStripe ? "✅" : "❌"} |\n`;
1306
+ output += `| Webhooks | ${truthpack.billing?.summary?.webhookHandlersFound || 0} |\n`;
1307
+
1308
+ output += `\n📁 **Saved:** .vibecheck/truth/truthpack.json\n`;
1309
+ } catch {}
1310
+ } catch (err) {
1311
+ output += `\n⚠️ Truth pack error: ${err.message}\n`;
1312
+ }
1313
+
1314
+ output += "\n---\n_Ground Truth for AI Agents_\n";
1315
+ return this.success(output);
1316
+ }
1317
+
1318
+ // ============================================================================
1319
+ // PROVE - One Command Reality Proof
1320
+ // ============================================================================
1321
+ async handleProve(projectPath, args) {
1322
+ let output = "# 🔬 vibecheck prove\n\n";
1323
+ output += `**URL:** ${args?.url || "(static only)"}\n`;
1324
+ output += `**Max Fix Rounds:** ${args?.maxFixRounds || 3}\n\n`;
1325
+
1326
+ try {
1327
+ let cmd = `node "${path.join(__dirname, "..", "bin", "vibecheck.js")}" prove`;
1328
+ if (args?.url) cmd += ` --url ${args.url}`;
1329
+ if (args?.auth) cmd += ` --auth ${args.auth}`;
1330
+ if (args?.storageState) cmd += ` --storage-state ${args.storageState}`;
1331
+ if (args?.skipReality) cmd += ` --skip-reality`;
1332
+ if (args?.skipFix) cmd += ` --skip-fix`;
1333
+ if (args?.maxFixRounds) cmd += ` --max-fix-rounds ${args.maxFixRounds}`;
1334
+
1335
+ const result = execSync(cmd, {
1336
+ cwd: projectPath,
1337
+ encoding: "utf8",
1338
+ maxBuffer: 10 * 1024 * 1024,
1339
+ timeout: 600000, // 10 min timeout for full prove loop
1340
+ env: { ...process.env, VIBECHECK_SKIP_AUTH: "1" },
1341
+ });
1342
+
1343
+ output += result.replace(/\x1b\[[0-9;]*m/g, "");
1344
+
1345
+ // Read prove report
1346
+ const provePath = path.join(projectPath, ".vibecheck", "prove", "last_prove.json");
1347
+ try {
1348
+ const report = JSON.parse(await fs.readFile(provePath, "utf-8"));
1349
+
1350
+ output += "\n## Timeline\n\n";
1351
+ output += "| Step | Action | Status |\n|------|--------|--------|\n";
1352
+ for (const t of report.timeline || []) {
1353
+ output += `| ${t.step} | ${t.action} | ${t.status || t.verdict || "ok"} |\n`;
1354
+ }
1355
+
1356
+ output += `\n**Final Verdict:** ${report.finalVerdict}\n`;
1357
+ output += `**Duration:** ${report.meta?.durationSeconds}s\n`;
1358
+ } catch {}
1359
+ } catch (err) {
1360
+ output += `\n⚠️ Prove error: ${err.message}\n`;
1361
+ if (err.stdout) output += `\n${err.stdout.replace(/\x1b\[[0-9;]*m/g, "")}\n`;
1362
+ }
1363
+
1364
+ output += "\n---\n_One command to make it real_\n";
1365
+ return this.success(output);
1366
+ }
1367
+
1368
+ // ============================================================================
1369
+ // PROOF
1370
+ // ============================================================================
1371
+ async handleProof(projectPath, args) {
1372
+ const mode = args?.mode;
1373
+
1374
+ if (!mode) {
1375
+ return this.error("Mode required: 'mocks' or 'reality'");
1376
+ }
1377
+
1378
+ let output = `# 🎬 vibecheck Proof: ${mode.toUpperCase()}\n\n`;
1379
+
1380
+ try {
1381
+ let cmd = `node "${path.join(__dirname, "..", "bin", "vibecheck.js")}" proof ${mode}`;
1382
+ if (mode === "reality" && args?.url) cmd += ` --url=${args.url}`;
1383
+ if (mode === "reality" && args?.flow) cmd += ` --flow=${args.flow}`;
1384
+
1385
+ const result = execSync(cmd, {
1386
+ cwd: projectPath,
1387
+ encoding: "utf8",
1388
+ maxBuffer: 10 * 1024 * 1024,
1389
+ timeout: 120000, // 2 min timeout for reality mode
1390
+ });
1391
+
1392
+ output += result;
1393
+ } catch (err) {
1394
+ if (mode === "mocks") {
1395
+ output += "## 🚫 MOCKPROOF: FAIL\n\n";
1396
+ output += "Mock/demo code detected in production paths.\n";
1397
+ } else {
1398
+ output += "## 🚫 REALITY MODE: FAIL\n\n";
1399
+ output += "Fake data or mock services detected at runtime.\n";
1400
+ }
1401
+ output += `\n${err.stdout || err.message}\n`;
1402
+ }
1403
+
1404
+ return this.success(output);
1405
+ }
1406
+
1407
+ // ============================================================================
1408
+ // VALIDATE
1409
+ // ============================================================================
1410
+ async handleValidate(projectPath, args) {
1411
+ const { code, intent } = args;
1412
+ if (!code) return this.error("Code is required");
1413
+
1414
+ let output = "# 🤖 AI Code Validation\n\n";
1415
+ if (intent) output += `**Intent:** ${intent}\n\n`;
1416
+
1417
+ try {
1418
+ const {
1419
+ runHallucinationCheck,
1420
+ validateIntent,
1421
+ validateQuality,
1422
+ } = require(
1423
+ path.join(__dirname, "..", "bin", "runners", "lib", "ai-bridge.js"),
1424
+ );
1425
+
1426
+ // 1. Hallucinations (checking against project deps + internal logic)
1427
+ // Note: In MCP context, we might want to check the provided code specifically for imports.
1428
+ // The bridge's runHallucinationCheck mostly checks package.json.
1429
+ // But we can check imports in the 'code' snippet if we extract them.
1430
+ // The bridge handles extractImports internally but runHallucinationCheck doesn't expose it directly for a string input.
1431
+ // We will rely on package.json sanity check for now + static analysis of the snippet.
1432
+
1433
+ const hallResult = await runHallucinationCheck(projectPath);
1434
+
1435
+ // 2. Intent
1436
+ let intentResult = { score: 100, issues: [] };
1437
+ if (intent) {
1438
+ intentResult = validateIntent(code, intent);
1439
+ }
1440
+
1441
+ // 3. Quality
1442
+ const qualityResult = validateQuality(code);
1443
+
1444
+ const allIssues = [
1445
+ ...hallResult.issues,
1446
+ ...intentResult.issues,
1447
+ ...qualityResult.issues,
1448
+ ];
1449
+
1450
+ const score = Math.round(
1451
+ (hallResult.score + intentResult.score + qualityResult.score) / 3,
1452
+ );
1453
+ const status = score >= 80 ? "✅ PASSED" : "⚠️ ISSUES FOUND";
1454
+
1455
+ output += `**Status:** ${status} (${score}/100)\n\n`;
1456
+
1457
+ if (allIssues.length > 0) {
1458
+ output += "### Issues\n";
1459
+ for (const issue of allIssues) {
1460
+ const icon =
1461
+ issue.severity === "critical"
1462
+ ? "🔴"
1463
+ : issue.severity === "high"
1464
+ ? "🟠"
1465
+ : "🟡";
1466
+ output += `- ${icon} **[${issue.type}]** ${issue.message}\n`;
1467
+ }
1468
+ } else {
1469
+ output += "✨ Code looks valid and safe.\n";
1470
+ }
1471
+
1472
+ return this.success(output);
1473
+ } catch (err) {
1474
+ return this.error(`Validation failed: ${err.message}`);
1475
+ }
1476
+ }
1477
+
1478
+ // ============================================================================
1479
+ // REPORT
1480
+ // ============================================================================
1481
+ async handleReport(projectPath, args) {
1482
+ const type = args?.type || "summary";
1483
+ const outputDir = path.join(projectPath, ".vibecheck");
1484
+
1485
+ let output = "# 📄 vibecheck Report\n\n";
1486
+
1487
+ try {
1488
+ if (type === "summary") {
1489
+ const summaryPath = path.join(outputDir, "summary.json");
1490
+ const summary = JSON.parse(await fs.readFile(summaryPath, "utf-8"));
1491
+
1492
+ output += `**Score:** ${summary.score}/100 (${summary.grade})\n`;
1493
+ output += `**Verdict:** ${summary.canShip ? "✅ SHIP" : "🚫 NO-SHIP"}\n`;
1494
+ output += `**Generated:** ${summary.timestamp}\n`;
1495
+ } else if (type === "full") {
1496
+ const reportPath = path.join(outputDir, "summary.md");
1497
+ output += await fs.readFile(reportPath, "utf-8");
1498
+ } else if (type === "sarif") {
1499
+ const sarifPath = path.join(outputDir, "results.sarif");
1500
+ const sarif = await fs.readFile(sarifPath, "utf-8");
1501
+ output += "```json\n" + sarif.substring(0, 2000) + "\n```\n";
1502
+ output += `\n📄 **Full SARIF:** ${sarifPath}\n`;
1503
+ } else if (type === "html") {
1504
+ output += `📄 **HTML Report:** ${path.join(outputDir, "report.html")}\n`;
1505
+ output += "Open in browser to view the full report.\n";
1506
+ }
1507
+ } catch (err) {
1508
+ output += `⚠️ No ${type} report found. Run \`vibecheck.scan\` first.\n`;
1509
+ }
1510
+
1511
+ return this.success(output);
1512
+ }
1513
+
1514
+ // ============================================================================
1515
+ // SHIP - Quick health check
1516
+ // ============================================================================
1517
+ async handleShip(projectPath, args) {
1518
+ let output = "# 🚀 vibecheck Ship\n\n";
1519
+ output += `**Path:** ${projectPath}\n\n`;
1520
+
1521
+ try {
1522
+ let cmd = `node "${path.join(__dirname, "..", "bin", "vibecheck.js")}" ship`;
1523
+ if (args?.fix) cmd += ` --fix`;
1524
+
1525
+ const result = execSync(cmd, {
1526
+ cwd: projectPath,
1527
+ encoding: "utf8",
1528
+ maxBuffer: 10 * 1024 * 1024,
1529
+ env: { ...process.env, VIBECHECK_SKIP_AUTH: "1" },
1530
+ });
1531
+
1532
+ // Parse the output for key information
1533
+ output += result.replace(/\x1b\[[0-9;]*m/g, ""); // Strip ANSI codes
1534
+ } catch (err) {
1535
+ output += `\n⚠️ Ship check failed: ${err.message}\n`;
1536
+ }
1537
+
1538
+ output += "\n---\n_Context Enhanced by vibecheck AI_\n";
1539
+ return this.success(output);
1540
+ }
1541
+
1542
+ // ============================================================================
1543
+ // VERIFY - Runtime browser testing
1544
+ // ============================================================================
1545
+ async handleVerify(projectPath, args) {
1546
+ const url = args?.url;
1547
+ if (!url) return this.error("URL is required");
1548
+
1549
+ let output = "# 🧪 vibecheck Verify\n\n";
1550
+ output += `**URL:** ${url}\n`;
1551
+ if (args?.flows?.length) output += `**Flows:** ${args.flows.join(", ")}\n`;
1552
+ if (args?.headed) output += `**Mode:** Headed (visible browser)\n`;
1553
+ if (args?.record) output += `**Recording:** Enabled\n`;
1554
+ output += "\n";
1555
+
1556
+ try {
1557
+ let cmd = `node "${path.join(__dirname, "..", "bin", "vibecheck.js")}" verify --url "${url}"`;
1558
+ if (args?.auth) cmd += ` --auth "${args.auth}"`;
1559
+ if (args?.flows?.length) cmd += ` --flows ${args.flows.join(",")}`;
1560
+ if (args?.headed) cmd += ` --headed`;
1561
+ if (args?.record) cmd += ` --record`;
1562
+
1563
+ const result = execSync(cmd, {
1564
+ cwd: projectPath,
1565
+ encoding: "utf8",
1566
+ maxBuffer: 10 * 1024 * 1024,
1567
+ timeout: 180000, // 3 min timeout
1568
+ env: { ...process.env, VIBECHECK_SKIP_AUTH: "1" },
1569
+ });
1570
+
1571
+ output += result.replace(/\x1b\[[0-9;]*m/g, "");
1572
+
1573
+ // Try to read reality results
1574
+ const realityPath = path.join(projectPath, ".vibecheck", "reality", "last_reality.json");
1575
+ try {
1576
+ const reality = JSON.parse(await fs.readFile(realityPath, "utf-8"));
1577
+
1578
+ output += "\n## Verification Summary\n\n";
1579
+ output += `| Metric | Value |\n|--------|-------|\n`;
1580
+ output += `| Pages Visited | ${reality.meta?.pagesVisited || 0} |\n`;
1581
+ output += `| Buttons Clicked | ${reality.meta?.buttonsClicked || 0} |\n`;
1582
+ output += `| Forms Tested | ${reality.meta?.formsTested || 0} |\n`;
1583
+ output += `| Dead UI Found | ${reality.findings?.length || 0} |\n`;
1584
+ output += `| Console Errors | ${reality.consoleErrors?.length || 0} |\n`;
1585
+ output += `| Duration | ${reality.meta?.durationMs || 0}ms |\n`;
1586
+
1587
+ if (reality.findings?.length > 0) {
1588
+ output += "\n## Dead UI Detected\n\n";
1589
+ for (const f of reality.findings.slice(0, 5)) {
1590
+ output += `- **${f.title}** — ${f.reason}\n`;
1591
+ }
1592
+ if (reality.findings.length > 5) {
1593
+ output += `\n_...and ${reality.findings.length - 5} more_\n`;
1594
+ }
1595
+ }
1596
+
1597
+ output += `\n📁 **Full Report:** .vibecheck/reality/last_reality.json\n`;
1598
+ } catch {}
1599
+ } catch (err) {
1600
+ output += `\n⚠️ Verify failed: ${err.message}\n`;
1601
+ if (err.stdout) output += `\n${err.stdout.replace(/\x1b\[[0-9;]*m/g, "")}\n`;
1602
+ }
1603
+
1604
+ output += "\n---\n_Runtime Verification by vibecheck_\n";
1605
+ return this.success(output);
1606
+ }
1607
+
1608
+ // ============================================================================
1609
+ // REALITY v2 - Two-Pass Auth Verification + Dead UI Crawler
1610
+ // ============================================================================
1611
+ async handleReality(projectPath, args) {
1612
+ const url = args?.url;
1613
+ if (!url) return this.error("URL is required");
1614
+
1615
+ let output = "# 🧪 vibecheck Reality v2\n\n";
1616
+ output += `**URL:** ${url}\n`;
1617
+ output += `**Two-Pass Auth:** ${args?.verifyAuth ? "Yes" : "No"}\n`;
1618
+ if (args?.auth) output += `**Auth:** ${args.auth.split(":")[0]}:***\n`;
1619
+ if (args?.storageState) output += `**Storage State:** ${args.storageState}\n`;
1620
+ if (args?.headed) output += `**Mode:** Headed (visible browser)\n`;
1621
+ if (args?.danger) output += `**Danger Mode:** Enabled (risky clicks allowed)\n`;
1622
+ output += "\n";
1623
+
1624
+ try {
1625
+ let cmd = `node "${path.join(__dirname, "..", "bin", "vibecheck.js")}" reality --url "${url}"`;
1626
+ if (args?.auth) cmd += ` --auth "${args.auth}"`;
1627
+ if (args?.verifyAuth) cmd += ` --verify-auth`;
1628
+ if (args?.storageState) cmd += ` --storage-state "${args.storageState}"`;
1629
+ if (args?.saveStorageState) cmd += ` --save-storage-state "${args.saveStorageState}"`;
1630
+ if (args?.truthpack) cmd += ` --truthpack "${args.truthpack}"`;
1631
+ if (args?.headed) cmd += ` --headed`;
1632
+ if (args?.maxPages) cmd += ` --max-pages ${args.maxPages}`;
1633
+ if (args?.maxDepth) cmd += ` --max-depth ${args.maxDepth}`;
1634
+ if (args?.danger) cmd += ` --danger`;
1635
+
1636
+ const result = execSync(cmd, {
1637
+ cwd: projectPath,
1638
+ encoding: "utf8",
1639
+ maxBuffer: 10 * 1024 * 1024,
1640
+ timeout: 300000, // 5 min timeout for two-pass
1641
+ env: { ...process.env, VIBECHECK_SKIP_AUTH: "1" },
1642
+ });
1643
+
1644
+ output += result.replace(/\x1b\[[0-9;]*m/g, "");
1645
+
1646
+ // Read reality results
1647
+ const realityPath = path.join(projectPath, ".vibecheck", "reality", "last_reality.json");
1648
+ try {
1649
+ const reality = JSON.parse(await fs.readFile(realityPath, "utf-8"));
1650
+
1651
+ output += "\n## Reality Report\n\n";
1652
+ output += `| Metric | Value |\n|--------|-------|\n`;
1653
+ output += `| Anon Pages | ${reality.passes?.anon?.pagesVisited?.length || 0} |\n`;
1654
+ if (reality.passes?.auth) {
1655
+ output += `| Auth Pages | ${reality.passes?.auth?.pagesVisited?.length || 0} |\n`;
1656
+ }
1657
+ output += `| Findings | ${reality.findings?.length || 0} |\n`;
1658
+ output += `| Console Errors | ${reality.consoleErrors?.length || 0} |\n`;
1659
+ output += `| Network Errors | ${reality.networkErrors?.length || 0} |\n`;
1660
+
1661
+ if (reality.coverage) {
1662
+ output += `| UI Coverage | ${reality.coverage.percent}% (${reality.coverage.hit}/${reality.coverage.total}) |\n`;
1663
+ }
1664
+
1665
+ if (reality.meta?.protectedMatcherCount > 0) {
1666
+ output += `| Auth Matchers | ${reality.meta.protectedMatcherCount} |\n`;
1667
+ }
1668
+
1669
+ // Show findings by category
1670
+ const blocks = reality.findings?.filter(f => f.severity === "BLOCK") || [];
1671
+ const warns = reality.findings?.filter(f => f.severity === "WARN") || [];
1672
+
1673
+ if (blocks.length > 0) {
1674
+ output += "\n### 🛑 BLOCK Findings\n\n";
1675
+ for (const f of blocks.slice(0, 5)) {
1676
+ output += `- **${f.title}**\n - ${f.reason}\n`;
1677
+ if (f.screenshot) output += ` - Screenshot: ${f.screenshot}\n`;
1678
+ }
1679
+ if (blocks.length > 5) {
1680
+ output += `\n_...and ${blocks.length - 5} more BLOCKs_\n`;
1681
+ }
1682
+ }
1683
+
1684
+ if (warns.length > 0) {
1685
+ output += "\n### ⚠️ WARN Findings\n\n";
1686
+ for (const f of warns.slice(0, 3)) {
1687
+ output += `- **${f.title}** — ${f.reason}\n`;
1688
+ }
1689
+ if (warns.length > 3) {
1690
+ output += `\n_...and ${warns.length - 3} more WARNs_\n`;
1691
+ }
1692
+ }
1693
+
1694
+ // Verdict
1695
+ const verdict = blocks.length > 0 ? "🛑 BLOCK" : warns.length > 0 ? "⚠️ WARN" : "✅ CLEAN";
1696
+ output += `\n## Verdict: ${verdict}\n`;
1697
+
1698
+ output += `\n📁 **Full Report:** .vibecheck/reality/last_reality.json\n`;
1699
+
1700
+ if (reality.meta?.savedStorageState) {
1701
+ output += `📁 **Saved Auth State:** ${reality.meta.savedStorageState}\n`;
1702
+ }
1703
+ } catch {}
1704
+ } catch (err) {
1705
+ output += `\n⚠️ Reality failed: ${err.message}\n`;
1706
+ if (err.stdout) output += `\n${err.stdout.replace(/\x1b\[[0-9;]*m/g, "")}\n`;
1707
+ }
1708
+
1709
+ output += "\n---\n_Reality Mode v2 — Two-Pass Auth Verification_\n";
1710
+ return this.success(output);
1711
+ }
1712
+
1713
+ // ============================================================================
1714
+ // AI-TEST - AI Agent testing
1715
+ // ============================================================================
1716
+ async handleAITest(projectPath, args) {
1717
+ const url = args?.url;
1718
+ if (!url) return this.error("URL is required");
1719
+
1720
+ let output = "# 🤖 vibecheck AI Agent\n\n";
1721
+ output += `**URL:** ${url}\n`;
1722
+ output += `**Goal:** ${args?.goal || "Test all features"}\n\n`;
1723
+
1724
+ try {
1725
+ let cmd = `node "${path.join(__dirname, "..", "bin", "vibecheck.js")}" ai-test --url "${url}"`;
1726
+ if (args?.goal) cmd += ` --goal "${args.goal}"`;
1727
+ if (args?.headed) cmd += ` --headed`;
1728
+
1729
+ const result = execSync(cmd, {
1730
+ cwd: projectPath,
1731
+ encoding: "utf8",
1732
+ maxBuffer: 10 * 1024 * 1024,
1733
+ timeout: 180000,
1734
+ env: { ...process.env, VIBECHECK_SKIP_AUTH: "1" },
1735
+ });
1736
+
1737
+ output += result.replace(/\x1b\[[0-9;]*m/g, "");
1738
+
1739
+ // Try to read fix prompts
1740
+ const promptPath = path.join(
1741
+ projectPath,
1742
+ ".vibecheck",
1743
+ "ai-agent",
1744
+ "fix-prompt.md",
1745
+ );
1746
+ try {
1747
+ const prompts = await fs.readFile(promptPath, "utf-8");
1748
+ output += "\n## Fix Prompts Generated\n\n";
1749
+ output += prompts.substring(0, 2000);
1750
+ if (prompts.length > 2000) output += "\n\n... (truncated)";
1751
+ } catch {}
1752
+ } catch (err) {
1753
+ output += `\n⚠️ AI Agent failed: ${err.message}\n`;
1754
+ }
1755
+
1756
+ output += "\n---\n_Context Enhanced by vibecheck AI_\n";
1757
+ return this.success(output);
1758
+ }
1759
+
1760
+ // ============================================================================
1761
+ // AUTOPILOT - Continuous protection
1762
+ // ============================================================================
1763
+ async handleAutopilot(projectPath, args) {
1764
+ const action = args?.action || "status";
1765
+
1766
+ let output = "# 🤖 vibecheck Autopilot\n\n";
1767
+ output += `**Action:** ${action}\n\n`;
1768
+
1769
+ try {
1770
+ let cmd = `node "${path.join(__dirname, "..", "bin", "vibecheck.js")}" autopilot ${action}`;
1771
+ if (args?.slack) cmd += ` --slack="${args.slack}"`;
1772
+ if (args?.email) cmd += ` --email="${args.email}"`;
1773
+
1774
+ const result = execSync(cmd, {
1775
+ cwd: projectPath,
1776
+ encoding: "utf8",
1777
+ maxBuffer: 10 * 1024 * 1024,
1778
+ env: { ...process.env, VIBECHECK_SKIP_AUTH: "1" },
1779
+ });
1780
+
1781
+ output += result.replace(/\x1b\[[0-9;]*m/g, "");
1782
+ } catch (err) {
1783
+ output += `\n⚠️ Autopilot failed: ${err.message}\n`;
1784
+ }
1785
+
1786
+ return this.success(output);
1787
+ }
1788
+
1789
+ // ============================================================================
1790
+ // AUTOPILOT PLAN - Generate fix plan (Pro/Compliance)
1791
+ // ============================================================================
1792
+ async handleAutopilotPlan(projectPath, args) {
1793
+ let output = "# 🤖 vibecheck Autopilot Plan\n\n";
1794
+ output += `**Path:** ${projectPath}\n`;
1795
+ output += `**Profile:** ${args?.profile || "ship"}\n\n`;
1796
+
1797
+ try {
1798
+ // Use the core autopilot runner directly
1799
+ const corePath = path.join(__dirname, "..", "packages", "core", "dist", "index.js");
1800
+ let runAutopilot;
1801
+
1802
+ try {
1803
+ const core = await import(corePath);
1804
+ runAutopilot = core.runAutopilot;
1805
+ } catch {
1806
+ // Fallback to CLI
1807
+ let cmd = `node "${path.join(__dirname, "..", "bin", "vibecheck.js")}" autopilot plan`;
1808
+ cmd += ` --profile ${args?.profile || "ship"}`;
1809
+ cmd += ` --max-fixes ${args?.maxFixes || 10}`;
1810
+ cmd += ` --json`;
1811
+
1812
+ const result = execSync(cmd, {
1813
+ cwd: projectPath,
1814
+ encoding: "utf8",
1815
+ maxBuffer: 10 * 1024 * 1024,
1816
+ env: { ...process.env, VIBECHECK_SKIP_AUTH: "1" },
1817
+ });
1818
+
1819
+ const jsonResult = JSON.parse(result);
1820
+ output += `## Scan Results\n\n`;
1821
+ output += `- **Total findings:** ${jsonResult.totalFindings}\n`;
1822
+ output += `- **Fixable:** ${jsonResult.fixableFindings}\n`;
1823
+ output += `- **Estimated time:** ${jsonResult.estimatedDuration}\n\n`;
1824
+
1825
+ output += `## Fix Packs\n\n`;
1826
+ for (const pack of jsonResult.packs || []) {
1827
+ const risk = pack.estimatedRisk === "high" ? "🔴" : pack.estimatedRisk === "medium" ? "🟡" : "🟢";
1828
+ output += `### ${risk} ${pack.name}\n`;
1829
+ output += `- Issues: ${pack.findings.length}\n`;
1830
+ output += `- Files: ${pack.impactedFiles.join(", ")}\n\n`;
1831
+ }
1832
+
1833
+ output += `\n💡 Run \`vibecheck.autopilot_apply\` to apply these fixes.\n`;
1834
+ return this.success(output);
1835
+ }
1836
+
1837
+ if (runAutopilot) {
1838
+ const result = await runAutopilot({
1839
+ projectPath,
1840
+ mode: "plan",
1841
+ profile: args?.profile || "ship",
1842
+ maxFixes: args?.maxFixes || 10,
1843
+ });
1844
+
1845
+ output += `## Scan Results\n\n`;
1846
+ output += `- **Total findings:** ${result.totalFindings}\n`;
1847
+ output += `- **Fixable:** ${result.fixableFindings}\n`;
1848
+ output += `- **Estimated time:** ${result.estimatedDuration}\n\n`;
1849
+
1850
+ output += `## Fix Packs\n\n`;
1851
+ for (const pack of result.packs) {
1852
+ const risk = pack.estimatedRisk === "high" ? "🔴" : pack.estimatedRisk === "medium" ? "🟡" : "🟢";
1853
+ output += `### ${risk} ${pack.name}\n`;
1854
+ output += `- Issues: ${pack.findings.length}\n`;
1855
+ output += `- Files: ${pack.impactedFiles.join(", ")}\n\n`;
1856
+ }
1857
+
1858
+ output += `\n💡 Run \`vibecheck.autopilot_apply\` to apply these fixes.\n`;
1859
+ }
1860
+ } catch (err) {
1861
+ output += `\n❌ Error: ${err.message}\n`;
1862
+ }
1863
+
1864
+ return this.success(output);
1865
+ }
1866
+
1867
+ // ============================================================================
1868
+ // AUTOPILOT APPLY - Apply fixes (Pro/Compliance)
1869
+ // ============================================================================
1870
+ async handleAutopilotApply(projectPath, args) {
1871
+ let output = "# 🔧 vibecheck Autopilot Apply\n\n";
1872
+ output += `**Path:** ${projectPath}\n`;
1873
+ output += `**Profile:** ${args?.profile || "ship"}\n`;
1874
+ output += `**Dry Run:** ${args?.dryRun ? "Yes" : "No"}\n\n`;
1875
+
1876
+ try {
1877
+ // Fallback to CLI
1878
+ let cmd = `node "${path.join(__dirname, "..", "bin", "vibecheck.js")}" autopilot apply`;
1879
+ cmd += ` --profile ${args?.profile || "ship"}`;
1880
+ cmd += ` --max-fixes ${args?.maxFixes || 10}`;
1881
+ if (args?.verify === false) cmd += ` --no-verify`;
1882
+ if (args?.dryRun) cmd += ` --dry-run`;
1883
+ cmd += ` --json`;
1884
+
1885
+ const result = execSync(cmd, {
1886
+ cwd: projectPath,
1887
+ encoding: "utf8",
1888
+ maxBuffer: 10 * 1024 * 1024,
1889
+ timeout: 300000, // 5 min timeout
1890
+ env: { ...process.env, VIBECHECK_SKIP_AUTH: "1" },
1891
+ });
1892
+
1893
+ const jsonResult = JSON.parse(result);
1894
+
1895
+ output += `## Results\n\n`;
1896
+ output += `- **Packs attempted:** ${jsonResult.packsAttempted}\n`;
1897
+ output += `- **Packs succeeded:** ${jsonResult.packsSucceeded}\n`;
1898
+ output += `- **Packs failed:** ${jsonResult.packsFailed}\n`;
1899
+ output += `- **Fixes applied:** ${jsonResult.appliedFixes?.filter(f => f.success).length || 0}\n`;
1900
+ output += `- **Duration:** ${jsonResult.duration}ms\n\n`;
1901
+
1902
+ if (jsonResult.verification) {
1903
+ output += `## Verification\n\n`;
1904
+ output += `- TypeScript: ${jsonResult.verification.typecheck?.passed ? "✅" : "❌"}\n`;
1905
+ output += `- Build: ${jsonResult.verification.build?.passed ? "✅" : "⏭️"}\n`;
1906
+ output += `- Overall: ${jsonResult.verification.passed ? "✅ PASSED" : "❌ FAILED"}\n\n`;
1907
+ }
1908
+
1909
+ output += `**Remaining findings:** ${jsonResult.remainingFindings}\n`;
1910
+ output += `**New scan verdict:** ${jsonResult.newScanVerdict}\n`;
1911
+ } catch (err) {
1912
+ output += `\n❌ Error: ${err.message}\n`;
1913
+ }
1914
+
1915
+ return this.success(output);
1916
+ }
1917
+
1918
+ // ============================================================================
1919
+ // BADGE - Generate ship badge
1920
+ // ============================================================================
1921
+ async handleBadge(projectPath, args) {
1922
+ const format = args?.format || "svg";
1923
+
1924
+ let output = "# 🏅 vibecheck Badge\n\n";
1925
+
1926
+ try {
1927
+ let cmd = `node "${path.join(__dirname, "..", "bin", "vibecheck.js")}" badge --format ${format}`;
1928
+ if (args?.style) cmd += ` --style ${args.style}`;
1929
+
1930
+ const result = execSync(cmd, {
1931
+ cwd: projectPath,
1932
+ encoding: "utf8",
1933
+ maxBuffer: 10 * 1024 * 1024,
1934
+ env: { ...process.env, VIBECHECK_SKIP_AUTH: "1" },
1935
+ });
1936
+
1937
+ output += result.replace(/\x1b\[[0-9;]*m/g, "");
1938
+
1939
+ // Read the badge file
1940
+ const badgePath = path.join(
1941
+ projectPath,
1942
+ ".vibecheck",
1943
+ "badges",
1944
+ `badge.${format}`,
1945
+ );
1946
+ try {
1947
+ const badge = await fs.readFile(badgePath, "utf-8");
1948
+ if (format === "md") {
1949
+ output += "\n**Markdown:**\n```\n" + badge + "\n```\n";
1950
+ } else if (format === "html") {
1951
+ output += "\n**HTML:**\n```html\n" + badge + "\n```\n";
1952
+ } else {
1953
+ output += `\n**Badge saved to:** ${badgePath}\n`;
1954
+ }
1955
+ } catch {}
1956
+ } catch (err) {
1957
+ output += `\n⚠️ Badge generation failed: ${err.message}\n`;
1958
+ }
1959
+
1960
+ return this.success(output);
1961
+ }
1962
+
1963
+ // ============================================================================
1964
+ // CONTEXT - AI Rules Generator
1965
+ // ============================================================================
1966
+ async handleContext(projectPath, args) {
1967
+ const platform = args?.platform || "all";
1968
+
1969
+ let output = "# 🧠 vibecheck Context Generator\n\n";
1970
+ output += `**Project:** ${path.basename(projectPath)}\n`;
1971
+ output += `**Platform:** ${platform}\n\n`;
1972
+
1973
+ try {
1974
+ let cmd = `node "${path.join(__dirname, "..", "bin", "vibecheck.js")}" context`;
1975
+ if (platform !== "all") cmd += ` --platform=${platform}`;
1976
+
1977
+ execSync(cmd, {
1978
+ cwd: projectPath,
1979
+ encoding: "utf8",
1980
+ maxBuffer: 10 * 1024 * 1024,
1981
+ });
1982
+
1983
+ output += "## ✅ Context Generated\n\n";
1984
+ output +=
1985
+ "Your AI coding assistants now have full project awareness.\n\n";
1986
+
1987
+ output += "### Generated Files\n\n";
1988
+
1989
+ if (platform === "all" || platform === "cursor") {
1990
+ output += "**Cursor:**\n";
1991
+ output += "- `.cursorrules` - Main rules file\n";
1992
+ output += "- `.cursor/rules/*.mdc` - Modular rules\n\n";
1993
+ }
1994
+
1995
+ if (platform === "all" || platform === "windsurf") {
1996
+ output += "**Windsurf:**\n";
1997
+ output += "- `.windsurf/rules/*.md` - Cascade rules\n\n";
1998
+ }
1999
+
2000
+ if (platform === "all" || platform === "copilot") {
2001
+ output += "**GitHub Copilot:**\n";
2002
+ output += "- `.github/copilot-instructions.md`\n\n";
2003
+ }
2004
+
2005
+ output += "**Universal (MCP):**\n";
2006
+ output += "- `.vibecheck/context.json` - Full context\n";
2007
+ output += "- `.vibecheck/project-map.json` - Project analysis\n\n";
2008
+
2009
+ output += "### What Your AI Now Knows\n\n";
2010
+ output += "- Project architecture and structure\n";
2011
+ output += "- API routes and endpoints\n";
2012
+ output += "- Components and data models\n";
2013
+ output += "- Coding conventions and patterns\n";
2014
+ output += "- Dependencies and tech stack\n\n";
2015
+
2016
+ output +=
2017
+ "> **Tip:** Regenerate after major codebase changes with `vibecheck context`\n";
2018
+ } catch (err) {
2019
+ output += `\n⚠️ Context generation failed: ${err.message}\n`;
2020
+ }
2021
+
2022
+ output += "\n---\n_Context Enhanced by vibecheck AI_\n";
2023
+ return this.success(output);
2024
+ }
2025
+
2026
+ // ============================================================================
2027
+ // STATUS
2028
+ // ============================================================================
2029
+ async handleStatus(projectPath, args) {
2030
+ let output = "# 📊 vibecheck Status\n\n";
2031
+
2032
+ output += "## Server\n\n";
2033
+ output += `- **Version:** ${VERSION}\n`;
2034
+ output += `- **Node:** ${process.version}\n`;
2035
+ output += `- **Platform:** ${process.platform}\n\n`;
2036
+
2037
+ output += "## Project\n\n";
2038
+ output += `- **Path:** ${projectPath}\n`;
2039
+
2040
+ // Config
2041
+ const configPaths = [
2042
+ path.join(projectPath, "vibecheck.config.json"),
2043
+ path.join(projectPath, ".vibecheckrc"),
2044
+ ];
2045
+ let hasConfig = false;
2046
+ for (const p of configPaths) {
2047
+ try {
2048
+ await fs.access(p);
2049
+ hasConfig = true;
2050
+ output += `- **Config:** ✅ Found (${path.basename(p)})\n`;
2051
+ break;
2052
+ } catch {}
2053
+ }
2054
+ if (!hasConfig) {
2055
+ output += "- **Config:** ⚠️ Not found (run `vibecheck init`)\n";
2056
+ }
2057
+
2058
+ // Last scan
2059
+ const summaryPath = path.join(projectPath, ".vibecheck", "summary.json");
2060
+ try {
2061
+ const summary = JSON.parse(await fs.readFile(summaryPath, "utf-8"));
2062
+ output += `- **Last Scan:** ${summary.timestamp}\n`;
2063
+ output += `- **Last Score:** ${summary.score}/100 (${summary.grade})\n`;
2064
+ output += `- **Last Verdict:** ${summary.canShip ? "✅ SHIP" : "🚫 NO-SHIP"}\n`;
2065
+ } catch {
2066
+ output += "- **Last Scan:** None\n";
2067
+ }
2068
+
2069
+ output += "\n## Available Tools\n\n";
2070
+ output += "| Tool | Description |\n|------|-------------|\n";
2071
+ for (const tool of TOOLS) {
2072
+ output += `| \`${tool.name}\` | ${tool.description.split("—")[0].trim()} |\n`;
2073
+ }
2074
+
2075
+ output += "\n---\n_Vibecheck v" + VERSION + " — https://vibecheckai.dev_\n";
2076
+
2077
+ return this.success(output);
2078
+ }
2079
+
2080
+ // ============================================================================
2081
+ // RUN
2082
+ // ============================================================================
2083
+ async run() {
2084
+ const transport = new StdioServerTransport();
2085
+ await this.server.connect(transport);
2086
+ console.error("vibecheck MCP Server v2.0 running on stdio");
2087
+ }
2088
+ }
2089
+
2090
+ // Main
2091
+ const server = new VibecheckMCP();
2092
+ server.run().catch(console.error);