@vibecheckai/cli 3.1.8 → 3.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/registry.js +106 -116
- package/bin/runners/context/generators/mcp.js +18 -0
- package/bin/runners/context/index.js +72 -4
- package/bin/runners/context/proof-context.js +293 -1
- package/bin/runners/context/security-scanner.js +311 -73
- package/bin/runners/lib/analyzers.js +607 -20
- package/bin/runners/lib/detectors-v2.js +172 -15
- package/bin/runners/lib/entitlements-v2.js +48 -1
- package/bin/runners/lib/evidence-pack.js +678 -0
- package/bin/runners/lib/html-proof-report.js +913 -0
- package/bin/runners/lib/missions/plan.js +231 -41
- package/bin/runners/lib/missions/templates.js +125 -0
- package/bin/runners/lib/scan-output.js +492 -253
- package/bin/runners/lib/ship-output.js +901 -641
- package/bin/runners/runCheckpoint.js +44 -3
- package/bin/runners/runContext.d.ts +4 -0
- package/bin/runners/runDoctor.js +10 -2
- package/bin/runners/runFix.js +51 -341
- package/bin/runners/runInit.js +11 -0
- package/bin/runners/runPolish.d.ts +4 -0
- package/bin/runners/runPolish.js +608 -29
- package/bin/runners/runProve.js +210 -25
- package/bin/runners/runReality.js +846 -101
- package/bin/runners/runScan.js +238 -4
- package/bin/runners/runShip.js +19 -3
- package/bin/runners/runWatch.js +14 -1
- package/bin/vibecheck.js +32 -2
- package/mcp-server/consolidated-tools.js +408 -42
- package/mcp-server/index.js +152 -15
- package/mcp-server/proof-tools.js +571 -0
- package/mcp-server/tier-auth.js +22 -19
- package/mcp-server/tools-v3.js +744 -0
- package/mcp-server/truth-firewall-tools.js +190 -4
- package/package.json +3 -1
- package/bin/runners/runInstall.js +0 -281
- package/bin/runners/runLabs.js +0 -341
|
@@ -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
|
};
|