@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.
- package/README.md +27 -32
- package/bin/registry.js +208 -343
- 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/runContext.js +2 -3
- package/bin/runners/runDoctor.js +11 -4
- package/bin/runners/runFix.js +51 -341
- package/bin/runners/runInit.js +37 -20
- 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 +861 -107
- package/bin/runners/runScan.js +238 -4
- package/bin/runners/runShip.js +19 -3
- package/bin/runners/runWatch.js +25 -5
- package/bin/vibecheck.js +35 -47
- package/mcp-server/consolidated-tools.js +408 -42
- package/mcp-server/index.js +152 -15
- package/mcp-server/package.json +1 -1
- 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/runBadge.js +0 -916
- package/bin/runners/runContracts.js +0 -105
- package/bin/runners/runCtx.js +0 -680
- package/bin/runners/runCtxDiff.js +0 -301
- package/bin/runners/runCtxGuard.js +0 -176
- package/bin/runners/runCtxSync.js +0 -116
- package/bin/runners/runExport.js +0 -93
- package/bin/runners/runGraph.js +0 -454
- package/bin/runners/runInstall.js +0 -273
- package/bin/runners/runLabs.js +0 -341
- package/bin/runners/runLaunch.js +0 -181
- package/bin/runners/runPR.js +0 -255
- package/bin/runners/runPermissions.js +0 -310
- package/bin/runners/runPreflight.js +0 -580
- package/bin/runners/runReplay.js +0 -499
- package/bin/runners/runSecurity.js +0 -92
- package/bin/runners/runShare.js +0 -212
- package/bin/runners/runStatus.js +0 -102
- 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
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
};
|