@vibecheckai/cli 3.1.6 → 3.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/README.md +27 -32
  2. package/bin/registry.js +208 -343
  3. package/bin/runners/context/generators/mcp.js +18 -0
  4. package/bin/runners/context/index.js +72 -4
  5. package/bin/runners/context/proof-context.js +293 -1
  6. package/bin/runners/context/security-scanner.js +311 -73
  7. package/bin/runners/lib/analyzers.js +607 -20
  8. package/bin/runners/lib/detectors-v2.js +172 -15
  9. package/bin/runners/lib/entitlements-v2.js +48 -1
  10. package/bin/runners/lib/evidence-pack.js +678 -0
  11. package/bin/runners/lib/html-proof-report.js +913 -0
  12. package/bin/runners/lib/missions/plan.js +231 -41
  13. package/bin/runners/lib/missions/templates.js +125 -0
  14. package/bin/runners/lib/scan-output.js +492 -253
  15. package/bin/runners/lib/ship-output.js +901 -641
  16. package/bin/runners/runCheckpoint.js +44 -3
  17. package/bin/runners/runContext.d.ts +4 -0
  18. package/bin/runners/runContext.js +2 -3
  19. package/bin/runners/runDoctor.js +11 -4
  20. package/bin/runners/runFix.js +51 -341
  21. package/bin/runners/runInit.js +37 -20
  22. package/bin/runners/runPolish.d.ts +4 -0
  23. package/bin/runners/runPolish.js +608 -29
  24. package/bin/runners/runProve.js +210 -25
  25. package/bin/runners/runReality.js +861 -107
  26. package/bin/runners/runScan.js +238 -4
  27. package/bin/runners/runShip.js +19 -3
  28. package/bin/runners/runWatch.js +25 -5
  29. package/bin/vibecheck.js +35 -47
  30. package/mcp-server/consolidated-tools.js +408 -42
  31. package/mcp-server/index.js +152 -15
  32. package/mcp-server/package.json +1 -1
  33. package/mcp-server/proof-tools.js +571 -0
  34. package/mcp-server/tier-auth.js +22 -19
  35. package/mcp-server/tools-v3.js +744 -0
  36. package/mcp-server/truth-firewall-tools.js +190 -4
  37. package/package.json +3 -1
  38. package/bin/runners/runBadge.js +0 -916
  39. package/bin/runners/runContracts.js +0 -105
  40. package/bin/runners/runCtx.js +0 -680
  41. package/bin/runners/runCtxDiff.js +0 -301
  42. package/bin/runners/runCtxGuard.js +0 -176
  43. package/bin/runners/runCtxSync.js +0 -116
  44. package/bin/runners/runExport.js +0 -93
  45. package/bin/runners/runGraph.js +0 -454
  46. package/bin/runners/runInstall.js +0 -273
  47. package/bin/runners/runLabs.js +0 -341
  48. package/bin/runners/runLaunch.js +0 -181
  49. package/bin/runners/runPR.js +0 -255
  50. package/bin/runners/runPermissions.js +0 -310
  51. package/bin/runners/runPreflight.js +0 -580
  52. package/bin/runners/runReplay.js +0 -499
  53. package/bin/runners/runSecurity.js +0 -92
  54. package/bin/runners/runShare.js +0 -212
  55. package/bin/runners/runStatus.js +0 -102
  56. package/bin/runners/runVerify.js +0 -272
@@ -1,8 +1,15 @@
1
1
  /**
2
2
  * MCP Context JSON Generator
3
3
  * Generates .vibecheck/context.json for MCP servers
4
+ *
5
+ * Uses the unified EVIDENCE_SCHEMA and TRUTH_CONTRACT from proof-context.js
4
6
  */
5
7
 
8
+ const { EVIDENCE_SCHEMA, TRUTH_CONTRACT } = require("../proof-context.js");
9
+
10
+ // Context attribution message shown when AI uses vibecheck data
11
+ const CONTEXT_ATTRIBUTION = "🧠 Context enhanced by vibecheck";
12
+
6
13
  /**
7
14
  * Generate universal context JSON for MCP
8
15
  */
@@ -14,6 +21,9 @@ function generateContextJson(analysis, projectPath) {
14
21
  version: "3.0.0",
15
22
  generatedAt: new Date().toISOString(),
16
23
  generator: "vibecheck-context",
24
+ // Reference the unified schema from proof-context.js
25
+ evidenceSchema: EVIDENCE_SCHEMA,
26
+ truthContract: TRUTH_CONTRACT,
17
27
 
18
28
  project: {
19
29
  name: analysis.name,
@@ -92,6 +102,9 @@ function generateContextJson(analysis, projectPath) {
92
102
  byExtension: analysis.stats?.byExtension || {},
93
103
  largestFiles: analysis.stats?.largestFiles || [],
94
104
  },
105
+ contextValidation: analysis.contextValidation || null,
106
+ contextDrift: analysis.contextDrift || null,
107
+ contextSummary: analysis.contextSummary || null,
95
108
 
96
109
  rules: {
97
110
  critical: [
@@ -143,9 +156,14 @@ function generateContextJson(analysis, projectPath) {
143
156
  },
144
157
  ],
145
158
  },
159
+
160
+ // Attribution message for AI responses
161
+ _attribution: CONTEXT_ATTRIBUTION,
162
+ _attributionNote: "Include this attribution when referencing vibecheck context in responses",
146
163
  }, null, 2);
147
164
  }
148
165
 
149
166
  module.exports = {
150
167
  generateContextJson,
168
+ CONTEXT_ATTRIBUTION,
151
169
  };
@@ -59,6 +59,67 @@ const { generateCopilotInstructions } = require("./generators/copilot");
59
59
  const { generateClaudeConfig } = require("./generators/claude");
60
60
  const { generateCodexConfig } = require("./generators/codex");
61
61
  const { generateContextJson } = require("./generators/mcp");
62
+ let enhancedContextEngine = null;
63
+ const enhancedCandidates = [
64
+ path.join(__dirname, "..", "..", "..", "src", "lib", "context", "enhanced-context-engine.js"),
65
+ path.join(__dirname, "..", "..", "..", "..", "..", "src", "lib", "context", "enhanced-context-engine.js"),
66
+ ];
67
+ for (const candidate of enhancedCandidates) {
68
+ try {
69
+ if (fs.existsSync(candidate)) {
70
+ ({ enhancedContextEngine } = require(candidate));
71
+ break;
72
+ }
73
+ } catch {}
74
+ }
75
+
76
+ async function emitGuardrailMetric(projectPath, metric) {
77
+ try {
78
+ const auditDir = path.join(projectPath, ".vibecheck", "audit");
79
+ fs.mkdirSync(auditDir, { recursive: true });
80
+ const record = JSON.stringify({ ...metric, timestamp: new Date().toISOString() });
81
+ fs.appendFileSync(path.join(auditDir, "guardrail-metrics.jsonl"), `${record}\n`);
82
+ } catch {
83
+ // ignore metrics write failures
84
+ }
85
+ }
86
+
87
+ async function applyContextValidation(projectPath, analysis, opts) {
88
+ if (!enhancedContextEngine) return analysis;
89
+
90
+ const { context, validation, drift } = await enhancedContextEngine.getValidatedContext(projectPath, {
91
+ file: opts.currentFile || undefined,
92
+ purpose: opts.task || "general",
93
+ checkDrift: true,
94
+ });
95
+
96
+ analysis.contextValidation = validation;
97
+ analysis.contextDrift = drift;
98
+ analysis.contextSummary = {
99
+ confidence: validation?.confidence ?? context?.confidence ?? 0,
100
+ freshness: context?.freshness ?? null,
101
+ issues: validation?.issues?.length || 0,
102
+ driftScore: drift?.score ?? 0,
103
+ blocked: validation?.valid === false,
104
+ };
105
+
106
+ await emitGuardrailMetric(projectPath, {
107
+ event: "context_validation",
108
+ confidence: analysis.contextSummary.confidence,
109
+ freshness: analysis.contextSummary.freshness,
110
+ issues: analysis.contextSummary.issues,
111
+ driftScore: analysis.contextSummary.driftScore,
112
+ });
113
+
114
+ const vibecheckDir = path.join(projectPath, ".vibecheck");
115
+ fs.mkdirSync(vibecheckDir, { recursive: true });
116
+ fs.writeFileSync(
117
+ path.join(vibecheckDir, "context-validation.json"),
118
+ JSON.stringify({ validation, drift }, null, 2),
119
+ );
120
+
121
+ return analysis;
122
+ }
62
123
 
63
124
  /**
64
125
  * Parse command line arguments
@@ -357,10 +418,14 @@ function startWatchMode(projectPath, platform, verbose) {
357
418
  let debounceTimer = null;
358
419
  const watchDirs = ["src", "app", "pages", "components", "lib", "server"];
359
420
 
360
- const regenerate = () => {
421
+ const regenerate = async () => {
361
422
  console.log(`\n${c.yellow}⟳ Change detected, regenerating...${c.reset}\n`);
362
423
  try {
363
- const analysis = analyzeProject(projectPath);
424
+ let analysis = analyzeProject(projectPath);
425
+ analysis = await applyContextValidation(projectPath, analysis, {
426
+ currentFile: "",
427
+ task: "general",
428
+ });
364
429
  writeFiles(projectPath, platform, analysis, verbose);
365
430
  console.log(`${c.green}✓ Rules regenerated${c.reset} at ${new Date().toLocaleTimeString()}\n`);
366
431
  } catch (err) {
@@ -375,7 +440,9 @@ function startWatchMode(projectPath, platform, verbose) {
375
440
  fs.watch(watchPath, { recursive: true }, (eventType, filename) => {
376
441
  if (filename && (filename.endsWith(".ts") || filename.endsWith(".tsx") || filename.endsWith(".js") || filename.endsWith(".jsx"))) {
377
442
  if (debounceTimer) clearTimeout(debounceTimer);
378
- debounceTimer = setTimeout(regenerate, 500);
443
+ debounceTimer = setTimeout(() => {
444
+ void regenerate();
445
+ }, 500);
379
446
  }
380
447
  });
381
448
  console.log(`${c.dim} Watching: ${dir}/${c.reset}`);
@@ -839,7 +906,8 @@ ${c.cyan}━━━━━━━━━━━━━━━━━━━━━━━
839
906
  `);
840
907
 
841
908
  // Analyze project
842
- const analysis = analyzeProject(projectPath);
909
+ let analysis = analyzeProject(projectPath);
910
+ analysis = await applyContextValidation(projectPath, analysis, opts);
843
911
  const p = analysis.patterns || {};
844
912
 
845
913
  // Display analysis - Basic info
@@ -1,11 +1,211 @@
1
1
  /**
2
2
  * Proof-Carrying Context System
3
3
  * Every claim must have file:line evidence or it gets flagged as hypothesis
4
+ *
5
+ * This module exports the SINGLE TRUTH CONTRACT used across CLI, MCP, and extension.
4
6
  */
5
7
 
6
8
  const fs = require("fs");
7
9
  const path = require("path");
8
10
 
11
+ const DEFAULT_EVIDENCE_CONFIDENCE = 0.9;
12
+ const MAX_EVIDENCE_SNIPPET = 200;
13
+
14
+ // Context attribution message shown when AI uses vibecheck data
15
+ const CONTEXT_ATTRIBUTION = "🧠 Context enhanced by vibecheck";
16
+
17
+ // =============================================================================
18
+ // UNIFIED TRUTH CONTRACT SCHEMA v1.0
19
+ // =============================================================================
20
+
21
+ /**
22
+ * The canonical evidence schema used across CLI, MCP, and VS Code extension.
23
+ * All tools emitting claims MUST include this minimal required payload.
24
+ */
25
+ const EVIDENCE_SCHEMA = {
26
+ version: "1.0.0",
27
+ required: ["file", "line", "snippet", "confidence"],
28
+ maxSnippet: MAX_EVIDENCE_SNIPPET,
29
+ confidenceThresholds: {
30
+ strict: 0.8, // HIGH confidence required for strict mode
31
+ balanced: 0.6, // MEDIUM confidence for balanced mode
32
+ permissive: 0.4, // LOW confidence allowed in permissive mode
33
+ },
34
+ claimTypes: [
35
+ "route",
36
+ "schema_table",
37
+ "schema_column",
38
+ "export",
39
+ "default_export",
40
+ "middleware",
41
+ "env_var",
42
+ "auth_guard",
43
+ "billing_gate",
44
+ "hypothesis", // Claims without proof get flagged here
45
+ ],
46
+ };
47
+
48
+ /**
49
+ * The truth contract defines rules that AI must satisfy.
50
+ * Claims without evidence are automatically flagged as "unknown" and block actions.
51
+ */
52
+ const TRUTH_CONTRACT = {
53
+ version: "1.0.0",
54
+ claimsRequireEvidence: true,
55
+ confidenceThresholds: EVIDENCE_SCHEMA.confidenceThresholds,
56
+ policies: {
57
+ strict: {
58
+ minConfidence: 0.8,
59
+ allowUnknown: false,
60
+ requireValidation: true,
61
+ blockOnDrift: true,
62
+ },
63
+ balanced: {
64
+ minConfidence: 0.6,
65
+ allowUnknown: false,
66
+ requireValidation: true,
67
+ blockOnDrift: false,
68
+ },
69
+ permissive: {
70
+ minConfidence: 0.4,
71
+ allowUnknown: true,
72
+ requireValidation: false,
73
+ blockOnDrift: false,
74
+ },
75
+ },
76
+ invariants: [
77
+ "No paid feature without server-side enforcement",
78
+ "No success UI without confirmed success",
79
+ "No route reference without matching route map entry",
80
+ "No silent catch in auth/billing flows",
81
+ "No hardcoded secrets in production code",
82
+ ],
83
+ };
84
+
85
+ /**
86
+ * Normalize an evidence item to the canonical schema.
87
+ * @param {object} item - Raw evidence item
88
+ * @param {string} projectPath - Project root path
89
+ * @param {object} fallback - Fallback values
90
+ * @returns {object} Normalized evidence conforming to EVIDENCE_SCHEMA
91
+ */
92
+ function normalizeToEvidenceSchema(item, projectPath, fallback = {}) {
93
+ const file = item?.file || fallback.file || "";
94
+ const line = Number(item?.line || item?.lines || fallback.line || 1);
95
+ let snippet = item?.snippet || item?.evidence || fallback.evidence || "";
96
+
97
+ if (!snippet && file && projectPath) {
98
+ try {
99
+ const content = fs.readFileSync(path.join(projectPath, file), "utf-8");
100
+ const lines = content.split("\n");
101
+ const idx = Math.max(0, Math.min(lines.length - 1, line - 1));
102
+ snippet = lines[idx] || "";
103
+ } catch {
104
+ // File not readable
105
+ }
106
+ }
107
+
108
+ return {
109
+ file,
110
+ line,
111
+ snippet: String(snippet || "").slice(0, EVIDENCE_SCHEMA.maxSnippet),
112
+ confidence: item?.confidence ?? fallback.confidence ?? DEFAULT_EVIDENCE_CONFIDENCE,
113
+ };
114
+ }
115
+
116
+ /**
117
+ * Validate that evidence conforms to the schema.
118
+ * @param {object} evidence - Evidence object to validate
119
+ * @param {string} policy - Policy mode: 'strict' | 'balanced' | 'permissive'
120
+ * @returns {{ valid: boolean, errors: string[], confidence: number }}
121
+ */
122
+ function validateEvidence(evidence, policy = "balanced") {
123
+ const errors = [];
124
+ const thresholds = EVIDENCE_SCHEMA.confidenceThresholds;
125
+ const minConfidence = thresholds[policy] || thresholds.balanced;
126
+
127
+ // Check required fields
128
+ for (const field of EVIDENCE_SCHEMA.required) {
129
+ if (evidence[field] === undefined || evidence[field] === null || evidence[field] === "") {
130
+ errors.push(`Missing required field: ${field}`);
131
+ }
132
+ }
133
+
134
+ // Check confidence threshold
135
+ const confidence = evidence.confidence ?? 0;
136
+ if (confidence < minConfidence) {
137
+ errors.push(`Confidence ${confidence} below threshold ${minConfidence} for ${policy} policy`);
138
+ }
139
+
140
+ // Check snippet length
141
+ if (evidence.snippet && evidence.snippet.length > EVIDENCE_SCHEMA.maxSnippet) {
142
+ errors.push(`Snippet exceeds max length of ${EVIDENCE_SCHEMA.maxSnippet}`);
143
+ }
144
+
145
+ return {
146
+ valid: errors.length === 0,
147
+ errors,
148
+ confidence,
149
+ };
150
+ }
151
+
152
+ /**
153
+ * Create a claim with proper evidence attachment.
154
+ * @param {string} type - Claim type from EVIDENCE_SCHEMA.claimTypes
155
+ * @param {string} claim - Human-readable claim text
156
+ * @param {object} evidence - Evidence object
157
+ * @param {object} metadata - Additional metadata
158
+ * @returns {object} Properly formatted claim
159
+ */
160
+ function createClaim(type, claim, evidence, metadata = {}) {
161
+ const isHypothesis = !evidence || !evidence.file;
162
+
163
+ return {
164
+ id: `claim_${hashString(claim + (evidence?.file || ""))}`,
165
+ type: isHypothesis ? "hypothesis" : type,
166
+ claim,
167
+ evidence: evidence ? [evidence] : [],
168
+ metadata,
169
+ isVerified: !isHypothesis,
170
+ timestamp: new Date().toISOString(),
171
+ };
172
+ }
173
+
174
+ function normalizeEvidenceItem(projectPath, fact, item) {
175
+ const file = item?.file || fact.file || "";
176
+ const line = Number(item?.line || item?.lines || fact.line || 1);
177
+ let snippet = item?.snippet || item?.evidence || fact.evidence || "";
178
+
179
+ if (!snippet && file) {
180
+ try {
181
+ const content = fs.readFileSync(path.join(projectPath, file), "utf-8");
182
+ const lines = content.split("\n");
183
+ const idx = Math.max(0, Math.min(lines.length - 1, line - 1));
184
+ snippet = lines[idx] || "";
185
+ } catch {}
186
+ }
187
+
188
+ return {
189
+ file,
190
+ line,
191
+ snippet: String(snippet || "").slice(0, MAX_EVIDENCE_SNIPPET),
192
+ confidence: item?.confidence ?? fact.confidence ?? DEFAULT_EVIDENCE_CONFIDENCE,
193
+ };
194
+ }
195
+
196
+ function normalizeFactEvidence(projectPath, fact) {
197
+ const raw = Array.isArray(fact.evidence) ? fact.evidence : [fact.evidence];
198
+ const evidence = raw
199
+ .filter(Boolean)
200
+ .map((item) => normalizeEvidenceItem(projectPath, fact, item));
201
+
202
+ return {
203
+ ...fact,
204
+ confidence: fact.confidence ?? DEFAULT_EVIDENCE_CONFIDENCE,
205
+ evidence,
206
+ };
207
+ }
208
+
9
209
  /**
10
210
  * Extract proof-carrying facts with exact file:line references
11
211
  */
@@ -32,6 +232,13 @@ function extractProofCarryingFacts(projectPath) {
32
232
  const middlewareFacts = extractVerifiedMiddleware(projectPath);
33
233
  facts.verified.push(...middlewareFacts);
34
234
 
235
+ facts.verified = facts.verified.map((fact) =>
236
+ normalizeFactEvidence(projectPath, fact),
237
+ );
238
+ facts.hypotheses = facts.hypotheses.map((fact) =>
239
+ normalizeFactEvidence(projectPath, fact),
240
+ );
241
+
35
242
  // Build proof map
36
243
  facts.verified.forEach(f => {
37
244
  facts.proofMap[f.claim] = {
@@ -609,6 +816,72 @@ function hashString(str) {
609
816
  return Math.abs(hash).toString(16);
610
817
  }
611
818
 
819
+ /**
820
+ * Emit guardrail metrics to .vibecheck/audit/guardrail-metrics.jsonl
821
+ * KPIs tracked: false_positive_rate, unknown_rate, drift_score
822
+ */
823
+ async function emitGuardrailMetric(projectPath, metric) {
824
+ const auditDir = path.join(projectPath, ".vibecheck", "audit");
825
+ try {
826
+ fs.mkdirSync(auditDir, { recursive: true });
827
+ const record = JSON.stringify({
828
+ ...metric,
829
+ timestamp: new Date().toISOString(),
830
+ });
831
+ fs.appendFileSync(
832
+ path.join(auditDir, "guardrail-metrics.jsonl"),
833
+ `${record}\n`
834
+ );
835
+ } catch {
836
+ // Ignore write failures in metrics
837
+ }
838
+ }
839
+
840
+ /**
841
+ * Aggregate guardrail KPIs from audit logs
842
+ * Returns: { falsePositiveRate, unknownRate, avgDriftScore, totalValidations }
843
+ */
844
+ function aggregateGuardrailKPIs(projectPath) {
845
+ const auditLogPath = path.join(projectPath, ".vibecheck", "audit", "guardrail-metrics.jsonl");
846
+
847
+ try {
848
+ const content = fs.readFileSync(auditLogPath, "utf-8");
849
+ const lines = content.trim().split("\n").filter(Boolean).map(l => JSON.parse(l));
850
+
851
+ // Filter claim validations
852
+ const validations = lines.filter(l => l.event === "claim_validation");
853
+ const unknowns = validations.filter(l => l.result === "unknown");
854
+ const falsePositives = validations.filter(l => l.falsePositive === true);
855
+
856
+ // Filter drift events
857
+ const drifts = lines.filter(l => l.event === "drift_detected");
858
+ const avgDriftScore = drifts.length > 0
859
+ ? drifts.reduce((sum, d) => sum + (d.score || 0), 0) / drifts.length
860
+ : 0;
861
+
862
+ // Filter firewall blocks
863
+ const blocks = lines.filter(l => l.event === "truth_firewall_block");
864
+
865
+ return {
866
+ totalValidations: validations.length,
867
+ unknownRate: validations.length > 0 ? unknowns.length / validations.length : 0,
868
+ falsePositiveRate: validations.length > 0 ? falsePositives.length / validations.length : 0,
869
+ avgDriftScore,
870
+ totalBlocks: blocks.length,
871
+ lastUpdated: new Date().toISOString(),
872
+ };
873
+ } catch {
874
+ return {
875
+ totalValidations: 0,
876
+ unknownRate: 0,
877
+ falsePositiveRate: 0,
878
+ avgDriftScore: 0,
879
+ totalBlocks: 0,
880
+ lastUpdated: new Date().toISOString(),
881
+ };
882
+ }
883
+ }
884
+
612
885
  /**
613
886
  * Golden Path Replay Templates - recorded successful change patterns
614
887
  */
@@ -951,13 +1224,25 @@ function generateTruthPack(projectPath, analysis) {
951
1224
  framework: analysis.framework,
952
1225
  routes: analysis.proofCarryingFacts?.verified?.length,
953
1226
  packages: analysis.symbolReality?.installedPackages?.size
954
- }))
1227
+ })),
1228
+
1229
+ // Attribution message for AI responses
1230
+ _attribution: CONTEXT_ATTRIBUTION,
1231
+ _attributionNote: "Include this note when referencing vibecheck data: " + CONTEXT_ATTRIBUTION,
955
1232
  };
956
1233
 
957
1234
  return pack;
958
1235
  }
959
1236
 
960
1237
  module.exports = {
1238
+ // Unified Truth Contract Schema (v1.0)
1239
+ EVIDENCE_SCHEMA,
1240
+ TRUTH_CONTRACT,
1241
+ normalizeToEvidenceSchema,
1242
+ validateEvidence,
1243
+ createClaim,
1244
+
1245
+ // Proof-Carrying Context Functions
961
1246
  extractProofCarryingFacts,
962
1247
  symbolVibecheck,
963
1248
  computeFileImportanceScore,
@@ -969,4 +1254,11 @@ module.exports = {
969
1254
  detectDrift,
970
1255
  enforceOneFileRule,
971
1256
  generateTruthPack,
1257
+
1258
+ // Audit Metrics and KPIs
1259
+ emitGuardrailMetric,
1260
+ aggregateGuardrailKPIs,
1261
+
1262
+ // Context Attribution
1263
+ CONTEXT_ATTRIBUTION,
972
1264
  };