@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,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: vibecheck pr (generate comment)
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 ${failOnWarn ? "--fail-on-warn" : ""}
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 body = fs.readFileSync('.vibecheck/pr_comment.md', 'utf8');
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) { core.warning('No pull_request in context; skipping comment'); return; }
63
- await github.rest.issues.createComment({
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=$code"
128
+ echo "vibecheck verdict code: $code"
74
129
  if [ "$code" = "0" ]; then
75
- echo "SHIP/WARN allowed."
130
+ echo "SHIP - All checks passed"
76
131
  exit 0
77
132
  fi
78
133
  if [ "$code" = "1" ]; then
79
- echo "WARN failing (policy)."
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 "BLOCK failing."
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 };