@vibecheckai/cli 3.0.4 → 3.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/dev/run-v2-torture.js +30 -0
- package/bin/runners/context/index.js +1 -1
- package/bin/runners/lib/analyzers.js +38 -0
- package/bin/runners/lib/assets/vibecheck-logo.png +0 -0
- package/bin/runners/lib/contracts/auth-contract.js +8 -0
- package/bin/runners/lib/contracts/env-contract.js +3 -0
- package/bin/runners/lib/contracts/external-contract.js +10 -2
- package/bin/runners/lib/contracts/route-contract.js +7 -0
- package/bin/runners/lib/contracts.js +804 -0
- package/bin/runners/lib/detectors-v2.js +703 -0
- package/bin/runners/lib/drift.js +425 -0
- package/bin/runners/lib/entitlements-v2.js +3 -1
- package/bin/runners/lib/entitlements.js +11 -3
- package/bin/runners/lib/env-resolver.js +417 -0
- package/bin/runners/lib/extractors/client-calls.js +990 -0
- package/bin/runners/lib/extractors/fastify-route-dump.js +573 -0
- package/bin/runners/lib/extractors/fastify-routes.js +426 -0
- package/bin/runners/lib/extractors/index.js +363 -0
- package/bin/runners/lib/extractors/next-routes.js +524 -0
- package/bin/runners/lib/extractors/proof-graph.js +431 -0
- package/bin/runners/lib/extractors/route-matcher.js +451 -0
- package/bin/runners/lib/extractors/truthpack-v2.js +377 -0
- package/bin/runners/lib/extractors/ui-bindings.js +547 -0
- package/bin/runners/lib/findings-schema.js +281 -0
- package/bin/runners/lib/html-report.js +650 -0
- package/bin/runners/lib/missions/templates.js +45 -0
- package/bin/runners/lib/policy.js +295 -0
- package/bin/runners/lib/reality/correlation-detectors.js +359 -0
- package/bin/runners/lib/reality/index.js +318 -0
- package/bin/runners/lib/reality/request-hashing.js +416 -0
- package/bin/runners/lib/reality/request-mapper.js +453 -0
- package/bin/runners/lib/reality/safety-rails.js +463 -0
- package/bin/runners/lib/reality/semantic-snapshot.js +408 -0
- package/bin/runners/lib/reality/toast-detector.js +393 -0
- package/bin/runners/lib/report-html.js +5 -0
- package/bin/runners/lib/report-templates.js +5 -0
- package/bin/runners/lib/report.js +135 -0
- package/bin/runners/lib/route-truth.js +10 -10
- package/bin/runners/lib/schema-validator.js +350 -0
- package/bin/runners/lib/schemas/contracts.schema.json +160 -0
- package/bin/runners/lib/schemas/finding.schema.json +100 -0
- package/bin/runners/lib/schemas/mission-pack.schema.json +206 -0
- package/bin/runners/lib/schemas/proof-graph.schema.json +176 -0
- package/bin/runners/lib/schemas/reality-report.schema.json +162 -0
- package/bin/runners/lib/schemas/share-pack.schema.json +180 -0
- package/bin/runners/lib/schemas/ship-report.schema.json +117 -0
- package/bin/runners/lib/schemas/truthpack-v2.schema.json +303 -0
- package/bin/runners/lib/schemas/validator.js +438 -0
- package/bin/runners/lib/ui.js +562 -0
- package/bin/runners/lib/verdict-engine.js +628 -0
- package/bin/runners/runAIAgent.js +228 -1
- package/bin/runners/runBadge.js +181 -1
- package/bin/runners/runCtx.js +7 -2
- package/bin/runners/runCtxDiff.js +301 -0
- package/bin/runners/runGuard.js +168 -0
- package/bin/runners/runInitGha.js +78 -15
- package/bin/runners/runLabs.js +341 -0
- package/bin/runners/runLaunch.js +180 -1
- package/bin/runners/runMdc.js +203 -1
- package/bin/runners/runProof.zip +0 -0
- package/bin/runners/runProve.js +23 -0
- package/bin/runners/runReplay.js +114 -84
- package/bin/runners/runScan.js +111 -32
- package/bin/runners/runShip.js +23 -2
- package/bin/runners/runTruthpack.js +9 -7
- package/bin/runners/runValidate.js +161 -1
- package/bin/vibecheck.js +416 -770
- package/mcp-server/.guardrail/audit/audit.log.jsonl +2 -0
- package/mcp-server/.specs/architecture.mdc +90 -0
- package/mcp-server/.specs/security.mdc +30 -0
- package/mcp-server/README.md +252 -0
- package/mcp-server/agent-checkpoint.js +364 -0
- package/mcp-server/architect-tools.js +707 -0
- package/mcp-server/audit-mcp.js +206 -0
- package/mcp-server/codebase-architect-tools.js +838 -0
- package/mcp-server/consolidated-tools.js +804 -0
- package/mcp-server/hygiene-tools.js +428 -0
- package/mcp-server/index-v1.js +698 -0
- package/mcp-server/index.js +2092 -0
- package/mcp-server/index.old.js +4137 -0
- package/mcp-server/intelligence-tools.js +664 -0
- package/mcp-server/intent-drift-tools.js +873 -0
- package/mcp-server/mdc-generator.js +298 -0
- package/mcp-server/package-lock.json +165 -0
- package/mcp-server/package.json +47 -0
- package/mcp-server/premium-tools.js +1275 -0
- package/mcp-server/test-mcp.js +108 -0
- package/mcp-server/test-tools.js +36 -0
- package/mcp-server/tier-auth.js +147 -0
- package/mcp-server/tools/index.js +72 -0
- package/mcp-server/tools-reorganized.ts +244 -0
- package/mcp-server/truth-context.js +581 -0
- package/mcp-server/truth-firewall-tools.js +1500 -0
- package/mcp-server/vibecheck-2.0-tools.js +748 -0
- package/mcp-server/vibecheck-tools.js +1075 -0
- package/package.json +10 -8
- package/bin/guardrail.js +0 -834
- package/bin/runners/runAudit.js +0 -2
- package/bin/runners/runAutopilot.js +0 -2
- package/bin/runners/runCertify.js +0 -2
- package/bin/runners/runDashboard.js +0 -10
- package/bin/runners/runEnhancedShip.js +0 -2
- package/bin/runners/runFixPacks.js +0 -2
- package/bin/runners/runNaturalLanguage.js +0 -3
- package/bin/runners/runProof.js +0 -2
- package/bin/runners/runRealitySniff.js +0 -2
- package/bin/runners/runUpgrade.js +0 -2
- package/bin/runners/runVerifyAgentOutput.js +0 -2
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* vibecheck ctx diff - Contract Diff
|
|
3
|
+
*
|
|
4
|
+
* Shows detailed changes between current code and contracts.
|
|
5
|
+
* Useful for reviewing what would change before running ctx sync.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
"use strict";
|
|
9
|
+
|
|
10
|
+
const path = require("path");
|
|
11
|
+
const fs = require("fs");
|
|
12
|
+
const { buildTruthpack, loadTruthpack } = require("./lib/truth");
|
|
13
|
+
const {
|
|
14
|
+
buildAllContracts,
|
|
15
|
+
loadContracts,
|
|
16
|
+
diffAllContracts,
|
|
17
|
+
hasContractChanges
|
|
18
|
+
} = require("./lib/contracts");
|
|
19
|
+
|
|
20
|
+
const c = {
|
|
21
|
+
reset: '\x1b[0m',
|
|
22
|
+
bold: '\x1b[1m',
|
|
23
|
+
dim: '\x1b[2m',
|
|
24
|
+
green: '\x1b[32m',
|
|
25
|
+
yellow: '\x1b[33m',
|
|
26
|
+
cyan: '\x1b[36m',
|
|
27
|
+
red: '\x1b[31m',
|
|
28
|
+
blue: '\x1b[34m',
|
|
29
|
+
magenta: '\x1b[35m',
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
async function runCtxDiff(opts = {}) {
|
|
33
|
+
const root = path.resolve(opts.repoRoot || process.cwd());
|
|
34
|
+
|
|
35
|
+
console.log(`\n${c.cyan}${c.bold}📊 vibecheck ctx diff${c.reset}`);
|
|
36
|
+
console.log(`${c.dim}Comparing code against contracts...${c.reset}\n`);
|
|
37
|
+
|
|
38
|
+
// Load existing contracts
|
|
39
|
+
const existingContracts = loadContracts(root);
|
|
40
|
+
|
|
41
|
+
if (Object.keys(existingContracts).length === 0) {
|
|
42
|
+
console.log(`${c.yellow}⚠️ No contracts found${c.reset}`);
|
|
43
|
+
console.log(`${c.dim}Run 'vibecheck ctx sync' first to generate contracts${c.reset}\n`);
|
|
44
|
+
return 0;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Build truthpack
|
|
48
|
+
let truthpack = loadTruthpack(root);
|
|
49
|
+
if (!truthpack) {
|
|
50
|
+
console.log(`${c.dim}Building truthpack...${c.reset}`);
|
|
51
|
+
truthpack = await buildTruthpack({ repoRoot: root, fastifyEntry: opts.fastifyEntry });
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Build new contracts from current code
|
|
55
|
+
const newContracts = buildAllContracts(truthpack);
|
|
56
|
+
|
|
57
|
+
// Calculate diff
|
|
58
|
+
const diff = diffAllContracts(existingContracts, newContracts);
|
|
59
|
+
|
|
60
|
+
// Check if there are changes
|
|
61
|
+
if (!hasContractChanges(diff)) {
|
|
62
|
+
console.log(`${c.green}✓${c.reset} No drift detected - contracts match current code\n`);
|
|
63
|
+
return 0;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Print route diff
|
|
67
|
+
if (diff.routes) {
|
|
68
|
+
printRouteDiff(diff.routes, opts.verbose);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Print env diff
|
|
72
|
+
if (diff.env) {
|
|
73
|
+
printEnvDiff(diff.env, opts.verbose);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Print auth diff
|
|
77
|
+
if (diff.auth) {
|
|
78
|
+
printAuthDiff(diff.auth, opts.verbose);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Print external diff
|
|
82
|
+
if (diff.external) {
|
|
83
|
+
printExternalDiff(diff.external, opts.verbose);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Summary
|
|
87
|
+
const totalAdded = (diff.routes?.added?.length || 0) +
|
|
88
|
+
(diff.env?.added?.length || 0) +
|
|
89
|
+
(diff.auth?.protectedAdded?.length || 0) +
|
|
90
|
+
(diff.external?.added?.length || 0);
|
|
91
|
+
const totalRemoved = (diff.routes?.removed?.length || 0) +
|
|
92
|
+
(diff.env?.removed?.length || 0) +
|
|
93
|
+
(diff.auth?.protectedRemoved?.length || 0) +
|
|
94
|
+
(diff.external?.removed?.length || 0);
|
|
95
|
+
const totalChanged = (diff.routes?.changed?.length || 0) +
|
|
96
|
+
(diff.env?.changed?.length || 0);
|
|
97
|
+
|
|
98
|
+
console.log(`\n${c.bold}Summary${c.reset}`);
|
|
99
|
+
console.log(` ${c.green}+${totalAdded} added${c.reset} ${c.red}-${totalRemoved} removed${c.reset} ${c.yellow}~${totalChanged} changed${c.reset}`);
|
|
100
|
+
|
|
101
|
+
if (totalAdded > 0 || totalRemoved > 0 || totalChanged > 0) {
|
|
102
|
+
console.log(`\n${c.dim}Run 'vibecheck ctx sync' to update contracts${c.reset}`);
|
|
103
|
+
console.log(`${c.dim}Run 'vibecheck ship' to see if drift causes BLOCK${c.reset}\n`);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// JSON output
|
|
107
|
+
if (opts.json) {
|
|
108
|
+
const jsonPath = path.join(root, ".vibecheck", "diff-result.json");
|
|
109
|
+
fs.mkdirSync(path.dirname(jsonPath), { recursive: true });
|
|
110
|
+
fs.writeFileSync(jsonPath, JSON.stringify({
|
|
111
|
+
hasDrift: true,
|
|
112
|
+
diff,
|
|
113
|
+
summary: { added: totalAdded, removed: totalRemoved, changed: totalChanged }
|
|
114
|
+
}, null, 2), "utf8");
|
|
115
|
+
console.log(`${c.dim}JSON output: .vibecheck/diff-result.json${c.reset}\n`);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return totalRemoved > 0 || totalChanged > 0 ? 1 : 0;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function printRouteDiff(diff, verbose) {
|
|
122
|
+
const hasChanges = diff.added?.length || diff.removed?.length || diff.changed?.length;
|
|
123
|
+
if (!hasChanges) return;
|
|
124
|
+
|
|
125
|
+
console.log(`${c.bold}Routes${c.reset}`);
|
|
126
|
+
|
|
127
|
+
if (diff.added?.length) {
|
|
128
|
+
console.log(` ${c.green}+ ${diff.added.length} new routes${c.reset}`);
|
|
129
|
+
if (verbose) {
|
|
130
|
+
for (const r of diff.added.slice(0, 10)) {
|
|
131
|
+
console.log(` ${c.green}+${c.reset} ${r.method} ${r.path}`);
|
|
132
|
+
}
|
|
133
|
+
if (diff.added.length > 10) {
|
|
134
|
+
console.log(` ${c.dim}...and ${diff.added.length - 10} more${c.reset}`);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (diff.removed?.length) {
|
|
140
|
+
console.log(` ${c.red}- ${diff.removed.length} removed routes${c.reset}`);
|
|
141
|
+
if (verbose) {
|
|
142
|
+
for (const r of diff.removed.slice(0, 10)) {
|
|
143
|
+
console.log(` ${c.red}-${c.reset} ${r.method} ${r.path}`);
|
|
144
|
+
}
|
|
145
|
+
if (diff.removed.length > 10) {
|
|
146
|
+
console.log(` ${c.dim}...and ${diff.removed.length - 10} more${c.reset}`);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (diff.changed?.length) {
|
|
152
|
+
console.log(` ${c.yellow}~ ${diff.changed.length} changed routes${c.reset}`);
|
|
153
|
+
if (verbose) {
|
|
154
|
+
for (const { before, after } of diff.changed.slice(0, 5)) {
|
|
155
|
+
console.log(` ${c.yellow}~${c.reset} ${before.path}: auth ${before.auth} → ${after.auth}`);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function printEnvDiff(diff, verbose) {
|
|
162
|
+
const hasChanges = diff.added?.length || diff.removed?.length || diff.changed?.length;
|
|
163
|
+
if (!hasChanges) return;
|
|
164
|
+
|
|
165
|
+
console.log(`\n${c.bold}Environment Variables${c.reset}`);
|
|
166
|
+
|
|
167
|
+
if (diff.added?.length) {
|
|
168
|
+
console.log(` ${c.green}+ ${diff.added.length} new vars${c.reset}`);
|
|
169
|
+
if (verbose) {
|
|
170
|
+
for (const v of diff.added.slice(0, 10)) {
|
|
171
|
+
console.log(` ${c.green}+${c.reset} ${v.name}${v.required ? ' (required)' : ''}`);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (diff.removed?.length) {
|
|
177
|
+
console.log(` ${c.red}- ${diff.removed.length} removed vars${c.reset}`);
|
|
178
|
+
if (verbose) {
|
|
179
|
+
for (const v of diff.removed.slice(0, 10)) {
|
|
180
|
+
console.log(` ${c.red}-${c.reset} ${v.name}`);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (diff.changed?.length) {
|
|
186
|
+
console.log(` ${c.yellow}~ ${diff.changed.length} changed vars${c.reset}`);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function printAuthDiff(diff, verbose) {
|
|
191
|
+
const hasChanges = diff.protectedAdded?.length || diff.protectedRemoved?.length;
|
|
192
|
+
if (!hasChanges) return;
|
|
193
|
+
|
|
194
|
+
console.log(`\n${c.bold}Auth Patterns${c.reset}`);
|
|
195
|
+
|
|
196
|
+
if (diff.protectedAdded?.length) {
|
|
197
|
+
console.log(` ${c.green}+ ${diff.protectedAdded.length} new protected patterns${c.reset}`);
|
|
198
|
+
if (verbose) {
|
|
199
|
+
for (const p of diff.protectedAdded.slice(0, 5)) {
|
|
200
|
+
console.log(` ${c.green}+${c.reset} ${p}`);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if (diff.protectedRemoved?.length) {
|
|
206
|
+
console.log(` ${c.red}- ${diff.protectedRemoved.length} removed patterns (security!)${c.reset}`);
|
|
207
|
+
if (verbose) {
|
|
208
|
+
for (const p of diff.protectedRemoved.slice(0, 5)) {
|
|
209
|
+
console.log(` ${c.red}-${c.reset} ${p}`);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function printExternalDiff(diff, verbose) {
|
|
216
|
+
const hasChanges = diff.added?.length || diff.removed?.length;
|
|
217
|
+
if (!hasChanges) return;
|
|
218
|
+
|
|
219
|
+
console.log(`\n${c.bold}External Services${c.reset}`);
|
|
220
|
+
|
|
221
|
+
if (diff.added?.length) {
|
|
222
|
+
console.log(` ${c.green}+ ${diff.added.length} new services${c.reset}`);
|
|
223
|
+
if (verbose) {
|
|
224
|
+
for (const s of diff.added) {
|
|
225
|
+
console.log(` ${c.green}+${c.reset} ${s.name}`);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
if (diff.removed?.length) {
|
|
231
|
+
console.log(` ${c.red}- ${diff.removed.length} removed services${c.reset}`);
|
|
232
|
+
if (verbose) {
|
|
233
|
+
for (const s of diff.removed) {
|
|
234
|
+
console.log(` ${c.red}-${c.reset} ${s.name}`);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function parseArgs(args) {
|
|
241
|
+
const opts = {
|
|
242
|
+
repoRoot: process.cwd(),
|
|
243
|
+
fastifyEntry: null,
|
|
244
|
+
json: false,
|
|
245
|
+
verbose: false,
|
|
246
|
+
help: false
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
for (let i = 0; i < args.length; i++) {
|
|
250
|
+
const arg = args[i];
|
|
251
|
+
if (arg === "--json") opts.json = true;
|
|
252
|
+
else if (arg === "--verbose" || arg === "-v") opts.verbose = true;
|
|
253
|
+
else if (arg === "--fastify-entry") opts.fastifyEntry = args[++i];
|
|
254
|
+
else if (arg === "--path" || arg === "-p") opts.repoRoot = args[++i];
|
|
255
|
+
else if (arg === "--help" || arg === "-h") opts.help = true;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
return opts;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
function printHelp() {
|
|
262
|
+
console.log(`
|
|
263
|
+
${c.cyan}${c.bold}📊 vibecheck ctx diff${c.reset} - Contract Diff
|
|
264
|
+
|
|
265
|
+
Shows what would change if you run 'vibecheck ctx sync'.
|
|
266
|
+
Useful for reviewing drift before committing.
|
|
267
|
+
|
|
268
|
+
${c.bold}USAGE${c.reset}
|
|
269
|
+
vibecheck ctx diff ${c.dim}# Show drift summary${c.reset}
|
|
270
|
+
vibecheck ctx diff --verbose ${c.dim}# Show detailed changes${c.reset}
|
|
271
|
+
vibecheck ctx diff --json ${c.dim}# Output JSON${c.reset}
|
|
272
|
+
|
|
273
|
+
${c.bold}OPTIONS${c.reset}
|
|
274
|
+
--verbose, -v Show detailed changes
|
|
275
|
+
--json Output JSON to .vibecheck/diff-result.json
|
|
276
|
+
--fastify-entry Fastify entry file
|
|
277
|
+
--path, -p Project path (default: current directory)
|
|
278
|
+
--help, -h Show this help
|
|
279
|
+
|
|
280
|
+
${c.bold}EXIT CODES${c.reset}
|
|
281
|
+
0 = No drift (or only additions)
|
|
282
|
+
1 = Drift detected (removals or changes)
|
|
283
|
+
|
|
284
|
+
${c.bold}EXAMPLES${c.reset}
|
|
285
|
+
vibecheck ctx diff ${c.dim}# Quick check${c.reset}
|
|
286
|
+
vibecheck ctx diff -v ${c.dim}# Detailed view${c.reset}
|
|
287
|
+
`);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
async function main(args) {
|
|
291
|
+
const opts = parseArgs(args);
|
|
292
|
+
|
|
293
|
+
if (opts.help) {
|
|
294
|
+
printHelp();
|
|
295
|
+
return 0;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
return runCtxDiff(opts);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
module.exports = { runCtxDiff, main };
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* vibecheck guard - Unified trust boundary enforcement
|
|
3
|
+
*
|
|
4
|
+
* Combines: validate + claim-verifier + prompt-firewall
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* vibecheck guard # Run all checks
|
|
8
|
+
* vibecheck guard --claims # Verify AI claims against truthpack
|
|
9
|
+
* vibecheck guard --prompts # Check for prompt injection
|
|
10
|
+
* vibecheck guard --hallucinations # Detect AI hallucination patterns
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const path = require("path");
|
|
14
|
+
const fs = require("fs");
|
|
15
|
+
|
|
16
|
+
// Import underlying implementations
|
|
17
|
+
const { runValidate } = require("./runValidate");
|
|
18
|
+
const { runPromptFirewall } = require("./runPromptFirewall");
|
|
19
|
+
|
|
20
|
+
// ANSI colors
|
|
21
|
+
const c = {
|
|
22
|
+
reset: "\x1b[0m",
|
|
23
|
+
dim: "\x1b[2m",
|
|
24
|
+
bold: "\x1b[1m",
|
|
25
|
+
cyan: "\x1b[36m",
|
|
26
|
+
green: "\x1b[32m",
|
|
27
|
+
yellow: "\x1b[33m",
|
|
28
|
+
red: "\x1b[31m",
|
|
29
|
+
magenta: "\x1b[35m",
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
function printHelp() {
|
|
33
|
+
console.log(`
|
|
34
|
+
${c.cyan}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${c.reset}
|
|
35
|
+
${c.bold}vibecheck guard${c.reset} - Trust boundary enforcement for AI outputs
|
|
36
|
+
${c.cyan}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${c.reset}
|
|
37
|
+
|
|
38
|
+
${c.green}USAGE${c.reset}
|
|
39
|
+
vibecheck guard [options]
|
|
40
|
+
|
|
41
|
+
${c.yellow}OPTIONS${c.reset}
|
|
42
|
+
--claims Verify AI claims against truthpack (route_exists, auth_enforced, etc.)
|
|
43
|
+
--prompts Check code for prompt injection vulnerabilities
|
|
44
|
+
--hallucinations Detect AI hallucination patterns in generated code
|
|
45
|
+
--file <path> Check specific file(s)
|
|
46
|
+
--json Output JSON for CI integration
|
|
47
|
+
--strict Fail on warnings (default: fail on errors only)
|
|
48
|
+
|
|
49
|
+
${c.magenta}EXAMPLES${c.reset}
|
|
50
|
+
vibecheck guard # Run all checks
|
|
51
|
+
vibecheck guard --claims --file api.ts # Verify claims in specific file
|
|
52
|
+
vibecheck guard --prompts # Prompt injection scan
|
|
53
|
+
vibecheck guard --json # CI-friendly output
|
|
54
|
+
|
|
55
|
+
${c.dim}This command unifies trust boundary checks for AI-generated code.${c.reset}
|
|
56
|
+
`);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
async function runGuard(args = []) {
|
|
60
|
+
// Parse arguments
|
|
61
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
62
|
+
printHelp();
|
|
63
|
+
return 0;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const runClaims = args.includes("--claims") || (!args.includes("--prompts") && !args.includes("--hallucinations"));
|
|
67
|
+
const runPrompts = args.includes("--prompts") || (!args.includes("--claims") && !args.includes("--hallucinations"));
|
|
68
|
+
const runHallucinations = args.includes("--hallucinations") || (!args.includes("--claims") && !args.includes("--prompts"));
|
|
69
|
+
const jsonOutput = args.includes("--json");
|
|
70
|
+
const strict = args.includes("--strict");
|
|
71
|
+
|
|
72
|
+
const results = {
|
|
73
|
+
claims: null,
|
|
74
|
+
prompts: null,
|
|
75
|
+
hallucinations: null,
|
|
76
|
+
verdict: "PASS",
|
|
77
|
+
errors: 0,
|
|
78
|
+
warnings: 0,
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
console.log(`
|
|
82
|
+
${c.cyan}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${c.reset}
|
|
83
|
+
${c.bold}🛡️ VIBECHECK GUARD${c.reset} - Trust Boundary Enforcement
|
|
84
|
+
${c.cyan}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${c.reset}
|
|
85
|
+
`);
|
|
86
|
+
|
|
87
|
+
// Run claims verification (validates AI claims against truthpack)
|
|
88
|
+
if (runClaims) {
|
|
89
|
+
console.log(`${c.dim}▸ Verifying AI claims against truthpack...${c.reset}`);
|
|
90
|
+
try {
|
|
91
|
+
const validateArgs = args.filter(a => !["--claims", "--prompts", "--hallucinations"].includes(a));
|
|
92
|
+
const exitCode = await runValidate(validateArgs);
|
|
93
|
+
results.claims = { exitCode, status: exitCode === 0 ? "pass" : "fail" };
|
|
94
|
+
if (exitCode !== 0) {
|
|
95
|
+
results.errors++;
|
|
96
|
+
results.verdict = "FAIL";
|
|
97
|
+
}
|
|
98
|
+
console.log(exitCode === 0
|
|
99
|
+
? ` ${c.green}✓${c.reset} Claims verified`
|
|
100
|
+
: ` ${c.red}✗${c.reset} Claim verification failed`);
|
|
101
|
+
} catch (e) {
|
|
102
|
+
results.claims = { error: e.message };
|
|
103
|
+
console.log(` ${c.yellow}⚠${c.reset} Claims check skipped: ${e.message}`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Run prompt injection detection
|
|
108
|
+
if (runPrompts) {
|
|
109
|
+
console.log(`${c.dim}▸ Scanning for prompt injection vulnerabilities...${c.reset}`);
|
|
110
|
+
try {
|
|
111
|
+
const firewallArgs = args.filter(a => !["--claims", "--prompts", "--hallucinations"].includes(a));
|
|
112
|
+
const exitCode = await runPromptFirewall(firewallArgs);
|
|
113
|
+
results.prompts = { exitCode, status: exitCode === 0 ? "pass" : "fail" };
|
|
114
|
+
if (exitCode !== 0) {
|
|
115
|
+
results.warnings++;
|
|
116
|
+
if (strict) results.verdict = "FAIL";
|
|
117
|
+
}
|
|
118
|
+
console.log(exitCode === 0
|
|
119
|
+
? ` ${c.green}✓${c.reset} No prompt injection risks`
|
|
120
|
+
: ` ${c.yellow}⚠${c.reset} Prompt injection risks detected`);
|
|
121
|
+
} catch (e) {
|
|
122
|
+
results.prompts = { error: e.message };
|
|
123
|
+
console.log(` ${c.yellow}⚠${c.reset} Prompt check skipped: ${e.message}`);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Run hallucination detection
|
|
128
|
+
if (runHallucinations) {
|
|
129
|
+
console.log(`${c.dim}▸ Detecting hallucination patterns...${c.reset}`);
|
|
130
|
+
// Use validate with hallucination focus
|
|
131
|
+
try {
|
|
132
|
+
const validateArgs = ["--hallucinations", ...args.filter(a => !["--claims", "--prompts", "--hallucinations"].includes(a))];
|
|
133
|
+
const exitCode = await runValidate(validateArgs);
|
|
134
|
+
results.hallucinations = { exitCode, status: exitCode === 0 ? "pass" : "fail" };
|
|
135
|
+
if (exitCode !== 0) {
|
|
136
|
+
results.warnings++;
|
|
137
|
+
if (strict) results.verdict = "FAIL";
|
|
138
|
+
}
|
|
139
|
+
console.log(exitCode === 0
|
|
140
|
+
? ` ${c.green}✓${c.reset} No hallucination patterns`
|
|
141
|
+
: ` ${c.yellow}⚠${c.reset} Potential hallucinations detected`);
|
|
142
|
+
} catch (e) {
|
|
143
|
+
results.hallucinations = { error: e.message };
|
|
144
|
+
console.log(` ${c.yellow}⚠${c.reset} Hallucination check skipped: ${e.message}`);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Summary
|
|
149
|
+
console.log(`
|
|
150
|
+
${c.cyan}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${c.reset}`);
|
|
151
|
+
|
|
152
|
+
if (results.verdict === "PASS") {
|
|
153
|
+
console.log(` ${c.green}${c.bold}✓ GUARD PASS${c.reset} - All trust boundaries intact`);
|
|
154
|
+
} else {
|
|
155
|
+
console.log(` ${c.red}${c.bold}✗ GUARD FAIL${c.reset} - Trust boundary violations detected`);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
console.log(`${c.cyan}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${c.reset}
|
|
159
|
+
`);
|
|
160
|
+
|
|
161
|
+
if (jsonOutput) {
|
|
162
|
+
console.log(JSON.stringify(results, null, 2));
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return results.verdict === "PASS" ? 0 : (results.errors > 0 ? 2 : 1);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
module.exports = { runGuard };
|
|
@@ -6,16 +6,23 @@ function ensureDir(p) {
|
|
|
6
6
|
fs.mkdirSync(p, { recursive: true });
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
-
function workflowYaml({ packageRunner = "npx vibecheck", failOnWarn = false } = {}) {
|
|
9
|
+
function workflowYaml({ packageRunner = "npx vibecheck", failOnWarn = false, policy = "ci" } = {}) {
|
|
10
|
+
const policyFlag = policy !== "ci" ? `--policy ${policy}` : "--policy ci";
|
|
11
|
+
const failFlag = failOnWarn ? "--fail-on-warn" : "";
|
|
12
|
+
|
|
10
13
|
return `name: vibecheck
|
|
11
14
|
|
|
12
15
|
on:
|
|
13
16
|
pull_request:
|
|
17
|
+
types: [opened, synchronize, reopened]
|
|
14
18
|
|
|
15
19
|
permissions:
|
|
16
20
|
contents: read
|
|
17
21
|
pull-requests: write
|
|
18
22
|
|
|
23
|
+
env:
|
|
24
|
+
VIBECHECK_CI: "true"
|
|
25
|
+
|
|
19
26
|
jobs:
|
|
20
27
|
vibecheck:
|
|
21
28
|
runs-on: ubuntu-latest
|
|
@@ -26,7 +33,7 @@ jobs:
|
|
|
26
33
|
with:
|
|
27
34
|
node-version: 20
|
|
28
35
|
|
|
29
|
-
- name: Install deps (auto)
|
|
36
|
+
- name: Install deps (auto-detect package manager)
|
|
30
37
|
run: |
|
|
31
38
|
set -e
|
|
32
39
|
if [ -f pnpm-lock.yaml ]; then
|
|
@@ -41,61 +48,117 @@ jobs:
|
|
|
41
48
|
npm install
|
|
42
49
|
fi
|
|
43
50
|
|
|
44
|
-
- name:
|
|
51
|
+
- name: Vibecheck - Build truthpack
|
|
52
|
+
run: ${packageRunner} ctx ${policyFlag}
|
|
53
|
+
|
|
54
|
+
- name: Vibecheck - Generate PR report
|
|
45
55
|
id: vc
|
|
46
56
|
run: |
|
|
47
57
|
set +e
|
|
48
58
|
mkdir -p .vibecheck
|
|
49
|
-
${packageRunner} pr --out .vibecheck/pr_comment.md ${
|
|
59
|
+
${packageRunner} pr --out .vibecheck/pr_comment.md ${policyFlag} ${failFlag}
|
|
50
60
|
code=$?
|
|
51
61
|
echo "code=$code" >> $GITHUB_OUTPUT
|
|
62
|
+
echo "verdict=$code" >> $GITHUB_ENV
|
|
52
63
|
echo "---- vibecheck exit code: $code ----"
|
|
64
|
+
# Generate HTML report
|
|
65
|
+
${packageRunner} ship --html || true
|
|
53
66
|
exit 0
|
|
54
67
|
|
|
55
68
|
- name: Post PR comment
|
|
69
|
+
if: github.event_name == 'pull_request'
|
|
56
70
|
uses: actions/github-script@v7
|
|
57
71
|
with:
|
|
58
72
|
script: |
|
|
59
73
|
const fs = require('fs');
|
|
60
|
-
const
|
|
74
|
+
const commentPath = '.vibecheck/pr_comment.md';
|
|
75
|
+
if (!fs.existsSync(commentPath)) {
|
|
76
|
+
core.warning('No PR comment generated');
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
const body = fs.readFileSync(commentPath, 'utf8');
|
|
61
80
|
const pr = context.payload.pull_request;
|
|
62
|
-
if (!pr) {
|
|
63
|
-
|
|
81
|
+
if (!pr) {
|
|
82
|
+
core.warning('No pull_request in context; skipping comment');
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Find existing vibecheck comment to update
|
|
87
|
+
const { data: comments } = await github.rest.issues.listComments({
|
|
64
88
|
owner: context.repo.owner,
|
|
65
89
|
repo: context.repo.repo,
|
|
66
90
|
issue_number: pr.number,
|
|
67
|
-
body
|
|
68
91
|
});
|
|
92
|
+
const existingComment = comments.find(c =>
|
|
93
|
+
c.body.includes('Vibecheck:') && c.user.type === 'Bot'
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
if (existingComment) {
|
|
97
|
+
await github.rest.issues.updateComment({
|
|
98
|
+
owner: context.repo.owner,
|
|
99
|
+
repo: context.repo.repo,
|
|
100
|
+
comment_id: existingComment.id,
|
|
101
|
+
body
|
|
102
|
+
});
|
|
103
|
+
} else {
|
|
104
|
+
await github.rest.issues.createComment({
|
|
105
|
+
owner: context.repo.owner,
|
|
106
|
+
repo: context.repo.repo,
|
|
107
|
+
issue_number: pr.number,
|
|
108
|
+
body
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
- name: Upload artifacts
|
|
113
|
+
if: always()
|
|
114
|
+
uses: actions/upload-artifact@v4
|
|
115
|
+
with:
|
|
116
|
+
name: vibecheck-report
|
|
117
|
+
path: |
|
|
118
|
+
.vibecheck/last_ship.json
|
|
119
|
+
.vibecheck/last_ship.html
|
|
120
|
+
.vibecheck/pr_comment.md
|
|
121
|
+
.vibecheck/contracts_diff.json
|
|
122
|
+
retention-days: 30
|
|
123
|
+
if-no-files-found: ignore
|
|
69
124
|
|
|
70
125
|
- name: Enforce verdict
|
|
71
126
|
run: |
|
|
72
127
|
code="\${{ steps.vc.outputs.code }}"
|
|
73
|
-
echo "vibecheck code
|
|
128
|
+
echo "vibecheck verdict code: $code"
|
|
74
129
|
if [ "$code" = "0" ]; then
|
|
75
|
-
echo "SHIP
|
|
130
|
+
echo "✅ SHIP - All checks passed"
|
|
76
131
|
exit 0
|
|
77
132
|
fi
|
|
78
133
|
if [ "$code" = "1" ]; then
|
|
79
|
-
echo "WARN failing
|
|
134
|
+
echo "⚠️ WARN - Warnings found (failing per policy)"
|
|
135
|
+
exit 1
|
|
136
|
+
fi
|
|
137
|
+
if [ "$code" = "2" ]; then
|
|
138
|
+
echo "🚫 BLOCK - Blocking issues found"
|
|
80
139
|
exit 1
|
|
81
140
|
fi
|
|
82
|
-
echo "
|
|
141
|
+
echo "❓ Unknown exit code"
|
|
83
142
|
exit 1
|
|
84
143
|
`;
|
|
85
144
|
}
|
|
86
145
|
|
|
87
|
-
async function runInitGha({ repoRoot, failOnWarn = false } = {}) {
|
|
146
|
+
async function runInitGha({ repoRoot, failOnWarn = false, policy = "ci" } = {}) {
|
|
88
147
|
const root = repoRoot || process.cwd();
|
|
89
148
|
const wfDir = path.join(root, ".github", "workflows");
|
|
90
149
|
ensureDir(wfDir);
|
|
91
150
|
|
|
92
151
|
const wfPath = path.join(wfDir, "vibecheck.yml");
|
|
93
|
-
fs.writeFileSync(wfPath, workflowYaml({ failOnWarn }), "utf8");
|
|
152
|
+
fs.writeFileSync(wfPath, workflowYaml({ failOnWarn, policy }), "utf8");
|
|
94
153
|
|
|
95
154
|
console.log(`✅ Wrote: ${path.relative(root, wfPath).replace(/\\/g, "/")}`);
|
|
155
|
+
console.log(`\nPolicy: ${policy}`);
|
|
156
|
+
console.log(`Fail on warn: ${failOnWarn}`);
|
|
96
157
|
console.log(`\nNotes:`);
|
|
97
158
|
console.log(`- If your CLI isn't called via "npx vibecheck", edit the workflow line.`);
|
|
98
159
|
console.log(`- Set VIBECHECK_API_KEY as a GitHub Actions secret for cloud features.`);
|
|
160
|
+
console.log(`- Artifacts (JSON + HTML reports) uploaded automatically.`);
|
|
161
|
+
console.log(`- PR comments auto-updated on each push.`);
|
|
99
162
|
}
|
|
100
163
|
|
|
101
|
-
module.exports = { runInitGha };
|
|
164
|
+
module.exports = { runInitGha, workflowYaml };
|