@vibecheckai/cli 3.2.0 → 3.2.2
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/runners/lib/agent-firewall/change-packet/builder.js +214 -0
- package/bin/runners/lib/agent-firewall/change-packet/schema.json +228 -0
- package/bin/runners/lib/agent-firewall/change-packet/store.js +200 -0
- package/bin/runners/lib/agent-firewall/claims/claim-types.js +21 -0
- package/bin/runners/lib/agent-firewall/claims/extractor.js +214 -0
- package/bin/runners/lib/agent-firewall/claims/patterns.js +24 -0
- package/bin/runners/lib/agent-firewall/evidence/auth-evidence.js +88 -0
- package/bin/runners/lib/agent-firewall/evidence/contract-evidence.js +75 -0
- package/bin/runners/lib/agent-firewall/evidence/env-evidence.js +118 -0
- package/bin/runners/lib/agent-firewall/evidence/resolver.js +102 -0
- package/bin/runners/lib/agent-firewall/evidence/route-evidence.js +142 -0
- package/bin/runners/lib/agent-firewall/evidence/side-effect-evidence.js +145 -0
- package/bin/runners/lib/agent-firewall/fs-hook/daemon.js +19 -0
- package/bin/runners/lib/agent-firewall/fs-hook/installer.js +87 -0
- package/bin/runners/lib/agent-firewall/fs-hook/watcher.js +184 -0
- package/bin/runners/lib/agent-firewall/git-hook/pre-commit.js +163 -0
- package/bin/runners/lib/agent-firewall/ide-extension/cursor.js +107 -0
- package/bin/runners/lib/agent-firewall/ide-extension/vscode.js +68 -0
- package/bin/runners/lib/agent-firewall/ide-extension/windsurf.js +66 -0
- package/bin/runners/lib/agent-firewall/interceptor/base.js +304 -0
- package/bin/runners/lib/agent-firewall/interceptor/cursor.js +35 -0
- package/bin/runners/lib/agent-firewall/interceptor/vscode.js +35 -0
- package/bin/runners/lib/agent-firewall/interceptor/windsurf.js +34 -0
- package/bin/runners/lib/agent-firewall/policy/default-policy.json +84 -0
- package/bin/runners/lib/agent-firewall/policy/engine.js +72 -0
- package/bin/runners/lib/agent-firewall/policy/loader.js +143 -0
- package/bin/runners/lib/agent-firewall/policy/rules/auth-drift.js +50 -0
- package/bin/runners/lib/agent-firewall/policy/rules/contract-drift.js +50 -0
- package/bin/runners/lib/agent-firewall/policy/rules/fake-success.js +61 -0
- package/bin/runners/lib/agent-firewall/policy/rules/ghost-env.js +50 -0
- package/bin/runners/lib/agent-firewall/policy/rules/ghost-route.js +50 -0
- package/bin/runners/lib/agent-firewall/policy/rules/scope.js +93 -0
- package/bin/runners/lib/agent-firewall/policy/rules/unsafe-side-effect.js +57 -0
- package/bin/runners/lib/agent-firewall/policy/schema.json +183 -0
- package/bin/runners/lib/agent-firewall/policy/verdict.js +54 -0
- package/bin/runners/lib/agent-firewall/truthpack/index.js +67 -0
- package/bin/runners/lib/agent-firewall/truthpack/loader.js +116 -0
- package/bin/runners/lib/agent-firewall/unblock/planner.js +337 -0
- package/bin/runners/lib/analysis-core.js +198 -180
- package/bin/runners/lib/analyzers.js +1119 -536
- package/bin/runners/lib/cli-output.js +236 -210
- package/bin/runners/lib/detectors-v2.js +547 -785
- package/bin/runners/lib/fingerprint.js +377 -0
- package/bin/runners/lib/route-truth.js +1167 -322
- package/bin/runners/lib/scan-output.js +144 -738
- package/bin/runners/lib/ship-output-enterprise.js +239 -0
- package/bin/runners/lib/terminal-ui.js +188 -770
- package/bin/runners/lib/truth.js +1004 -321
- package/bin/runners/lib/unified-output.js +162 -158
- package/bin/runners/runAgent.js +161 -0
- package/bin/runners/runFirewall.js +134 -0
- package/bin/runners/runFirewallHook.js +56 -0
- package/bin/runners/runScan.js +113 -10
- package/bin/runners/runShip.js +7 -8
- package/bin/runners/runTruth.js +89 -0
- package/mcp-server/agent-firewall-interceptor.js +164 -0
- package/mcp-server/index.js +347 -313
- package/mcp-server/truth-context.js +131 -90
- package/mcp-server/truth-firewall-tools.js +1412 -1045
- package/package.json +1 -1
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-Unblock Planner
|
|
3
|
+
*
|
|
4
|
+
* Generates exact missing proofs and minimal patch plans when blocked.
|
|
5
|
+
* No open-ended advice - specific file/line instructions.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
"use strict";
|
|
9
|
+
|
|
10
|
+
const path = require("path");
|
|
11
|
+
const fs = require("fs");
|
|
12
|
+
const { CLAIM_TYPES } = require("../claims/claim-types");
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Generate unblock plan from violations
|
|
16
|
+
* @param {object} params
|
|
17
|
+
* @param {array} params.violations - Rule violations
|
|
18
|
+
* @param {array} params.claims - Extracted claims
|
|
19
|
+
* @param {string} params.projectRoot - Project root directory
|
|
20
|
+
* @returns {object} Unblock plan with specific steps
|
|
21
|
+
*/
|
|
22
|
+
function generateUnblockPlan({ violations, claims, projectRoot }) {
|
|
23
|
+
const steps = [];
|
|
24
|
+
|
|
25
|
+
for (const violation of violations) {
|
|
26
|
+
if (violation.severity !== "block") {
|
|
27
|
+
continue; // Only generate plans for blocking violations
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const claim = violation.claim || claims.find(c =>
|
|
31
|
+
violation.claimId && c.type === violation.claim?.type
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
switch (violation.rule) {
|
|
35
|
+
case "ghost_route":
|
|
36
|
+
steps.push(...planGhostRoute(claim, projectRoot));
|
|
37
|
+
break;
|
|
38
|
+
|
|
39
|
+
case "ghost_env":
|
|
40
|
+
steps.push(...planGhostEnv(claim, projectRoot));
|
|
41
|
+
break;
|
|
42
|
+
|
|
43
|
+
case "auth_drift":
|
|
44
|
+
steps.push(...planAuthDrift(claim, projectRoot));
|
|
45
|
+
break;
|
|
46
|
+
|
|
47
|
+
case "contract_drift":
|
|
48
|
+
steps.push(...planContractDrift(claim, projectRoot));
|
|
49
|
+
break;
|
|
50
|
+
|
|
51
|
+
case "scope_explosion":
|
|
52
|
+
steps.push(...planScopeExplosion(violation));
|
|
53
|
+
break;
|
|
54
|
+
|
|
55
|
+
case "unsafe_side_effect":
|
|
56
|
+
steps.push(...planUnsafeSideEffect(claim, projectRoot));
|
|
57
|
+
break;
|
|
58
|
+
|
|
59
|
+
case "fake_success_ui":
|
|
60
|
+
steps.push(...planFakeSuccess(claim, projectRoot));
|
|
61
|
+
break;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return {
|
|
66
|
+
steps: deduplicateSteps(steps)
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Plan for ghost route violation
|
|
72
|
+
*/
|
|
73
|
+
function planGhostRoute(claim, projectRoot) {
|
|
74
|
+
if (!claim || claim.type !== CLAIM_TYPES.ROUTE) {
|
|
75
|
+
return [];
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const routePath = claim.value;
|
|
79
|
+
const steps = [];
|
|
80
|
+
|
|
81
|
+
// Determine framework and suggest route file location
|
|
82
|
+
const isApiRoute = routePath.startsWith("/api/");
|
|
83
|
+
|
|
84
|
+
if (isApiRoute) {
|
|
85
|
+
// Next.js App Router
|
|
86
|
+
const routePathSegments = routePath.replace("/api/", "").split("/");
|
|
87
|
+
const routeFile = path.join("app", "api", ...routePathSegments, "route.ts");
|
|
88
|
+
const routeFileAbs = path.join(projectRoot, routeFile);
|
|
89
|
+
|
|
90
|
+
steps.push({
|
|
91
|
+
action: "create",
|
|
92
|
+
file: routeFile,
|
|
93
|
+
line: 1,
|
|
94
|
+
description: `Create route handler at ${routeFile} for ${routePath}`
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
// Also suggest adding to truthpack
|
|
98
|
+
steps.push({
|
|
99
|
+
action: "modify",
|
|
100
|
+
file: ".vibecheck/truthpack/routes.json",
|
|
101
|
+
line: null,
|
|
102
|
+
description: `Run 'vibecheck truth' to regenerate truthpack with new route`
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return steps;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Plan for ghost env violation
|
|
111
|
+
*/
|
|
112
|
+
function planGhostEnv(claim, projectRoot) {
|
|
113
|
+
if (!claim || claim.type !== CLAIM_TYPES.ENV) {
|
|
114
|
+
return [];
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const envVarName = claim.value;
|
|
118
|
+
const steps = [];
|
|
119
|
+
|
|
120
|
+
// Find env schema file
|
|
121
|
+
const envSchemaFiles = [
|
|
122
|
+
"apps/api/src/config/env.schema.ts",
|
|
123
|
+
"apps/api/src/env.schema.ts",
|
|
124
|
+
"src/config/env.schema.ts",
|
|
125
|
+
"src/env.schema.ts"
|
|
126
|
+
];
|
|
127
|
+
|
|
128
|
+
let envSchemaFile = null;
|
|
129
|
+
for (const candidate of envSchemaFiles) {
|
|
130
|
+
const fullPath = path.join(projectRoot, candidate);
|
|
131
|
+
if (fs.existsSync(fullPath)) {
|
|
132
|
+
envSchemaFile = candidate;
|
|
133
|
+
break;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (!envSchemaFile) {
|
|
138
|
+
// Create new schema file
|
|
139
|
+
envSchemaFile = "apps/api/src/config/env.schema.ts";
|
|
140
|
+
steps.push({
|
|
141
|
+
action: "create",
|
|
142
|
+
file: envSchemaFile,
|
|
143
|
+
line: 1,
|
|
144
|
+
description: `Create env schema file and add ${envVarName}`
|
|
145
|
+
});
|
|
146
|
+
} else {
|
|
147
|
+
// Add to existing schema
|
|
148
|
+
steps.push({
|
|
149
|
+
action: "modify",
|
|
150
|
+
file: envSchemaFile,
|
|
151
|
+
line: null, // Will need to find insertion point
|
|
152
|
+
description: `Add ${envVarName} to env schema`
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Also add to .env.example
|
|
157
|
+
steps.push({
|
|
158
|
+
action: "modify",
|
|
159
|
+
file: ".env.example",
|
|
160
|
+
line: null,
|
|
161
|
+
description: `Add ${envVarName}=<value> to .env.example`
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
return steps;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Plan for auth drift violation
|
|
169
|
+
*/
|
|
170
|
+
function planAuthDrift(claim, projectRoot) {
|
|
171
|
+
if (!claim || claim.type !== CLAIM_TYPES.AUTH) {
|
|
172
|
+
return [];
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const steps = [];
|
|
176
|
+
|
|
177
|
+
// Find middleware file
|
|
178
|
+
const middlewareFiles = [
|
|
179
|
+
"middleware.ts",
|
|
180
|
+
"src/middleware.ts",
|
|
181
|
+
"apps/api/src/middleware.ts"
|
|
182
|
+
];
|
|
183
|
+
|
|
184
|
+
let middlewareFile = null;
|
|
185
|
+
for (const candidate of middlewareFiles) {
|
|
186
|
+
const fullPath = path.join(projectRoot, candidate);
|
|
187
|
+
if (fs.existsSync(fullPath)) {
|
|
188
|
+
middlewareFile = candidate;
|
|
189
|
+
break;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (middlewareFile) {
|
|
194
|
+
steps.push({
|
|
195
|
+
action: "modify",
|
|
196
|
+
file: middlewareFile,
|
|
197
|
+
line: null,
|
|
198
|
+
description: `Add protected route pattern to middleware matcher for ${claim.file || "affected route"}`
|
|
199
|
+
});
|
|
200
|
+
} else {
|
|
201
|
+
steps.push({
|
|
202
|
+
action: "create",
|
|
203
|
+
file: "middleware.ts",
|
|
204
|
+
line: 1,
|
|
205
|
+
description: `Create middleware.ts and add auth protection for ${claim.file || "affected route"}`
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
return steps;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Plan for contract drift violation
|
|
214
|
+
*/
|
|
215
|
+
function planContractDrift(claim, projectRoot) {
|
|
216
|
+
if (!claim || claim.type !== CLAIM_TYPES.CONTRACT) {
|
|
217
|
+
return [];
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const steps = [];
|
|
221
|
+
|
|
222
|
+
steps.push({
|
|
223
|
+
action: "modify",
|
|
224
|
+
file: ".vibecheck/truthpack/contracts.json",
|
|
225
|
+
line: null,
|
|
226
|
+
description: `Update contract definition for ${claim.value} or run 'vibecheck truth' to regenerate`
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
return steps;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Plan for scope explosion violation
|
|
234
|
+
*/
|
|
235
|
+
function planScopeExplosion(violation) {
|
|
236
|
+
const steps = [];
|
|
237
|
+
|
|
238
|
+
const metadata = violation.metadata || {};
|
|
239
|
+
|
|
240
|
+
if (metadata.totalFiles) {
|
|
241
|
+
steps.push({
|
|
242
|
+
action: "modify",
|
|
243
|
+
file: null,
|
|
244
|
+
line: null,
|
|
245
|
+
description: `Reduce scope: ${metadata.totalFiles} files touched (max: ${metadata.maxFiles}). Split into smaller changes or provide explicit intent message.`
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
if (metadata.totalLines) {
|
|
250
|
+
steps.push({
|
|
251
|
+
action: "modify",
|
|
252
|
+
file: null,
|
|
253
|
+
line: null,
|
|
254
|
+
description: `Reduce scope: ${metadata.totalLines} lines changed (max: ${metadata.maxLines}). Split into smaller changes or provide explicit intent message.`
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
return steps;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Plan for unsafe side effect violation
|
|
263
|
+
*/
|
|
264
|
+
function planUnsafeSideEffect(claim, projectRoot) {
|
|
265
|
+
if (!claim || claim.type !== CLAIM_TYPES.SIDE_EFFECT) {
|
|
266
|
+
return [];
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
const steps = [];
|
|
270
|
+
|
|
271
|
+
// Suggest creating test file
|
|
272
|
+
const claimFile = claim.file || "";
|
|
273
|
+
if (claimFile) {
|
|
274
|
+
const baseName = path.basename(claimFile, path.extname(claimFile));
|
|
275
|
+
const dir = path.dirname(claimFile);
|
|
276
|
+
const testFile = path.join(dir, `${baseName}.test.ts`);
|
|
277
|
+
|
|
278
|
+
steps.push({
|
|
279
|
+
action: "create",
|
|
280
|
+
file: testFile,
|
|
281
|
+
line: 1,
|
|
282
|
+
description: `Create test file to verify side effect: ${claim.value}`
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// Also suggest reality proof
|
|
287
|
+
steps.push({
|
|
288
|
+
action: "modify",
|
|
289
|
+
file: null,
|
|
290
|
+
line: null,
|
|
291
|
+
description: `Run 'vibecheck reality' to generate proof for side effect: ${claim.value}`
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
return steps;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Plan for fake success UI violation
|
|
299
|
+
*/
|
|
300
|
+
function planFakeSuccess(claim, projectRoot) {
|
|
301
|
+
if (!claim || claim.type !== CLAIM_TYPES.UI_SUCCESS) {
|
|
302
|
+
return [];
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
const steps = [];
|
|
306
|
+
|
|
307
|
+
steps.push({
|
|
308
|
+
action: "modify",
|
|
309
|
+
file: claim.file || "unknown",
|
|
310
|
+
line: null,
|
|
311
|
+
description: `Add HTTP call or side effect before showing success message: ${claim.value}`
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
return steps;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Deduplicate steps by file+action
|
|
319
|
+
*/
|
|
320
|
+
function deduplicateSteps(steps) {
|
|
321
|
+
const seen = new Set();
|
|
322
|
+
const unique = [];
|
|
323
|
+
|
|
324
|
+
for (const step of steps) {
|
|
325
|
+
const key = `${step.action}|${step.file || "null"}`;
|
|
326
|
+
if (!seen.has(key)) {
|
|
327
|
+
seen.add(key);
|
|
328
|
+
unique.push(step);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
return unique;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
module.exports = {
|
|
336
|
+
generateUnblockPlan
|
|
337
|
+
};
|