@vibecheckai/cli 3.0.2 → 3.0.3
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/package.json +9 -1
- package/bin/cli-hygiene.js +0 -241
- package/bin/guardrail.js +0 -834
- package/bin/runners/cli-utils.js +0 -1070
- package/bin/runners/context/ai-task-decomposer.js +0 -337
- package/bin/runners/context/analyzer.js +0 -462
- package/bin/runners/context/api-contracts.js +0 -427
- package/bin/runners/context/context-diff.js +0 -342
- package/bin/runners/context/context-pruner.js +0 -291
- package/bin/runners/context/dependency-graph.js +0 -414
- package/bin/runners/context/generators/claude.js +0 -107
- package/bin/runners/context/generators/codex.js +0 -108
- package/bin/runners/context/generators/copilot.js +0 -119
- package/bin/runners/context/generators/cursor.js +0 -514
- package/bin/runners/context/generators/mcp.js +0 -151
- package/bin/runners/context/generators/windsurf.js +0 -180
- package/bin/runners/context/git-context.js +0 -302
- package/bin/runners/context/index.js +0 -1042
- package/bin/runners/context/insights.js +0 -173
- package/bin/runners/context/mcp-server/generate-rules.js +0 -337
- package/bin/runners/context/mcp-server/index.js +0 -1176
- package/bin/runners/context/mcp-server/package.json +0 -24
- package/bin/runners/context/memory.js +0 -200
- package/bin/runners/context/monorepo.js +0 -215
- package/bin/runners/context/multi-repo-federation.js +0 -404
- package/bin/runners/context/patterns.js +0 -253
- package/bin/runners/context/proof-context.js +0 -972
- package/bin/runners/context/security-scanner.js +0 -303
- package/bin/runners/context/semantic-search.js +0 -350
- package/bin/runners/context/shared.js +0 -264
- package/bin/runners/context/team-conventions.js +0 -310
- package/bin/runners/lib/ai-bridge.js +0 -416
- package/bin/runners/lib/analysis-core.js +0 -271
- package/bin/runners/lib/analyzers.js +0 -541
- package/bin/runners/lib/audit-bridge.js +0 -391
- package/bin/runners/lib/auth-truth.js +0 -193
- package/bin/runners/lib/auth.js +0 -215
- package/bin/runners/lib/backup.js +0 -62
- package/bin/runners/lib/billing.js +0 -107
- package/bin/runners/lib/claims.js +0 -118
- package/bin/runners/lib/cli-ui.js +0 -540
- package/bin/runners/lib/compliance-bridge-new.js +0 -0
- package/bin/runners/lib/compliance-bridge.js +0 -165
- package/bin/runners/lib/contracts/auth-contract.js +0 -194
- package/bin/runners/lib/contracts/env-contract.js +0 -178
- package/bin/runners/lib/contracts/external-contract.js +0 -198
- package/bin/runners/lib/contracts/guard.js +0 -168
- package/bin/runners/lib/contracts/index.js +0 -89
- package/bin/runners/lib/contracts/plan-validator.js +0 -311
- package/bin/runners/lib/contracts/route-contract.js +0 -192
- package/bin/runners/lib/detect.js +0 -89
- package/bin/runners/lib/doctor/autofix.js +0 -254
- package/bin/runners/lib/doctor/index.js +0 -37
- package/bin/runners/lib/doctor/modules/dependencies.js +0 -325
- package/bin/runners/lib/doctor/modules/index.js +0 -46
- package/bin/runners/lib/doctor/modules/network.js +0 -250
- package/bin/runners/lib/doctor/modules/project.js +0 -312
- package/bin/runners/lib/doctor/modules/runtime.js +0 -224
- package/bin/runners/lib/doctor/modules/security.js +0 -348
- package/bin/runners/lib/doctor/modules/system.js +0 -213
- package/bin/runners/lib/doctor/modules/vibecheck.js +0 -394
- package/bin/runners/lib/doctor/reporter.js +0 -262
- package/bin/runners/lib/doctor/service.js +0 -262
- package/bin/runners/lib/doctor/types.js +0 -113
- package/bin/runners/lib/doctor/ui.js +0 -263
- package/bin/runners/lib/doctor-enhanced.js +0 -233
- package/bin/runners/lib/doctor-v2.js +0 -608
- package/bin/runners/lib/enforcement.js +0 -72
|
@@ -1,168 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Contract Guard
|
|
3
|
-
* Validates code against contracts - CI gate functionality
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
"use strict";
|
|
7
|
-
|
|
8
|
-
const fs = require("fs");
|
|
9
|
-
const path = require("path");
|
|
10
|
-
const { validateAgainstRouteContract } = require("./route-contract");
|
|
11
|
-
const { validateAgainstEnvContract } = require("./env-contract");
|
|
12
|
-
const { validateAuthContract } = require("./auth-contract");
|
|
13
|
-
const { validateExternalContract } = require("./external-contract");
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Load contracts from .vibecheck/contracts/
|
|
17
|
-
*/
|
|
18
|
-
function loadContracts(repoRoot) {
|
|
19
|
-
const contractDir = path.join(repoRoot, ".vibecheck", "contracts");
|
|
20
|
-
const contracts = {};
|
|
21
|
-
|
|
22
|
-
const files = {
|
|
23
|
-
routes: "routes.json",
|
|
24
|
-
env: "env.json",
|
|
25
|
-
auth: "auth.json",
|
|
26
|
-
external: "external.json"
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
for (const [key, file] of Object.entries(files)) {
|
|
30
|
-
const filePath = path.join(contractDir, file);
|
|
31
|
-
if (fs.existsSync(filePath)) {
|
|
32
|
-
try {
|
|
33
|
-
contracts[key] = JSON.parse(fs.readFileSync(filePath, "utf8"));
|
|
34
|
-
} catch (e) {
|
|
35
|
-
console.warn(`Warning: Could not parse ${file}: ${e.message}`);
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
return contracts;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Save contracts to .vibecheck/contracts/
|
|
45
|
-
*/
|
|
46
|
-
function saveContracts(repoRoot, contracts) {
|
|
47
|
-
const contractDir = path.join(repoRoot, ".vibecheck", "contracts");
|
|
48
|
-
fs.mkdirSync(contractDir, { recursive: true });
|
|
49
|
-
|
|
50
|
-
const files = {
|
|
51
|
-
routes: "routes.json",
|
|
52
|
-
env: "env.json",
|
|
53
|
-
auth: "auth.json",
|
|
54
|
-
external: "external.json"
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
for (const [key, file] of Object.entries(files)) {
|
|
58
|
-
if (contracts[key]) {
|
|
59
|
-
const filePath = path.join(contractDir, file);
|
|
60
|
-
fs.writeFileSync(filePath, JSON.stringify(contracts[key], null, 2), "utf8");
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* Guard: Validate current code against contracts
|
|
67
|
-
*/
|
|
68
|
-
function guardContracts(contracts, truthpack, options = {}) {
|
|
69
|
-
const violations = [];
|
|
70
|
-
|
|
71
|
-
// Route contract validation
|
|
72
|
-
if (contracts.routes && truthpack?.routes?.clientRefs) {
|
|
73
|
-
const routeViolations = validateAgainstRouteContract(
|
|
74
|
-
contracts.routes,
|
|
75
|
-
truthpack.routes.clientRefs
|
|
76
|
-
);
|
|
77
|
-
violations.push(...routeViolations);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
// Env contract validation
|
|
81
|
-
if (contracts.env && truthpack?.env?.vars) {
|
|
82
|
-
const envViolations = validateAgainstEnvContract(
|
|
83
|
-
contracts.env,
|
|
84
|
-
truthpack.env.vars
|
|
85
|
-
);
|
|
86
|
-
violations.push(...envViolations);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
// Auth contract validation
|
|
90
|
-
if (contracts.auth && truthpack?.routes?.server) {
|
|
91
|
-
const authViolations = validateAuthContract(
|
|
92
|
-
contracts.auth,
|
|
93
|
-
truthpack.routes.server,
|
|
94
|
-
options.realityResults
|
|
95
|
-
);
|
|
96
|
-
violations.push(...authViolations);
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// External contract validation
|
|
100
|
-
if (contracts.external) {
|
|
101
|
-
const externalViolations = validateExternalContract(contracts.external);
|
|
102
|
-
violations.push(...externalViolations);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
return {
|
|
106
|
-
valid: violations.filter(v => v.severity === "BLOCK").length === 0,
|
|
107
|
-
violations,
|
|
108
|
-
blocks: violations.filter(v => v.severity === "BLOCK"),
|
|
109
|
-
warns: violations.filter(v => v.severity === "WARN")
|
|
110
|
-
};
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
* Get guard summary for CI output
|
|
115
|
-
*/
|
|
116
|
-
function getGuardSummary(guardResult) {
|
|
117
|
-
const { valid, blocks, warns } = guardResult;
|
|
118
|
-
|
|
119
|
-
return {
|
|
120
|
-
verdict: valid ? (warns.length ? "WARN" : "PASS") : "BLOCK",
|
|
121
|
-
exitCode: valid ? (warns.length ? 1 : 0) : 2,
|
|
122
|
-
summary: {
|
|
123
|
-
blocks: blocks.length,
|
|
124
|
-
warns: warns.length,
|
|
125
|
-
total: blocks.length + warns.length
|
|
126
|
-
},
|
|
127
|
-
violations: guardResult.violations
|
|
128
|
-
};
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
/**
|
|
132
|
-
* Format guard result for console output
|
|
133
|
-
*/
|
|
134
|
-
function formatGuardResult(guardResult) {
|
|
135
|
-
const lines = [];
|
|
136
|
-
const { violations, blocks, warns } = guardResult;
|
|
137
|
-
|
|
138
|
-
if (violations.length === 0) {
|
|
139
|
-
lines.push("✓ All contracts satisfied");
|
|
140
|
-
return lines.join("\n");
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
if (blocks.length > 0) {
|
|
144
|
-
lines.push(`\n🛑 BLOCKS (${blocks.length}):`);
|
|
145
|
-
for (const v of blocks) {
|
|
146
|
-
lines.push(` ✗ [${v.type}] ${v.message}`);
|
|
147
|
-
if (v.route) lines.push(` Route: ${v.route}`);
|
|
148
|
-
if (v.name) lines.push(` Var: ${v.name}`);
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
if (warns.length > 0) {
|
|
153
|
-
lines.push(`\n⚠️ WARNINGS (${warns.length}):`);
|
|
154
|
-
for (const v of warns) {
|
|
155
|
-
lines.push(` ⚠ [${v.type}] ${v.message}`);
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
return lines.join("\n");
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
module.exports = {
|
|
163
|
-
loadContracts,
|
|
164
|
-
saveContracts,
|
|
165
|
-
guardContracts,
|
|
166
|
-
getGuardSummary,
|
|
167
|
-
formatGuardResult
|
|
168
|
-
};
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Context Contracts - Module Index
|
|
3
|
-
*
|
|
4
|
-
* Exports all contract functionality for CLI and MCP use
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
"use strict";
|
|
8
|
-
|
|
9
|
-
const { buildRouteContract, validateAgainstRouteContract, diffRouteContracts } = require("./route-contract");
|
|
10
|
-
const { buildEnvContract, validateAgainstEnvContract, diffEnvContracts } = require("./env-contract");
|
|
11
|
-
const { buildAuthContract, validateAuthContract, diffAuthContracts } = require("./auth-contract");
|
|
12
|
-
const { buildExternalContract, validateExternalContract, diffExternalContracts } = require("./external-contract");
|
|
13
|
-
const { loadContracts, saveContracts, guardContracts, getGuardSummary, formatGuardResult } = require("./guard");
|
|
14
|
-
const { validatePlan, parsePlanActions, formatValidationResult } = require("./plan-validator");
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Build all contracts from truthpack
|
|
18
|
-
*/
|
|
19
|
-
function buildAllContracts(truthpack) {
|
|
20
|
-
return {
|
|
21
|
-
routes: buildRouteContract(truthpack),
|
|
22
|
-
env: buildEnvContract(truthpack),
|
|
23
|
-
auth: buildAuthContract(truthpack),
|
|
24
|
-
external: buildExternalContract(truthpack)
|
|
25
|
-
};
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Diff all contracts
|
|
30
|
-
*/
|
|
31
|
-
function diffAllContracts(before, after) {
|
|
32
|
-
return {
|
|
33
|
-
routes: before.routes && after.routes ? diffRouteContracts(before.routes, after.routes) : null,
|
|
34
|
-
env: before.env && after.env ? diffEnvContracts(before.env, after.env) : null,
|
|
35
|
-
auth: before.auth && after.auth ? diffAuthContracts(before.auth, after.auth) : null,
|
|
36
|
-
external: before.external && after.external ? diffExternalContracts(before.external, after.external) : null
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Check if contracts have meaningful changes
|
|
42
|
-
*/
|
|
43
|
-
function hasContractChanges(diff) {
|
|
44
|
-
if (!diff) return false;
|
|
45
|
-
|
|
46
|
-
const checkDiff = (d) => {
|
|
47
|
-
if (!d) return false;
|
|
48
|
-
return (d.added?.length > 0) || (d.removed?.length > 0) || (d.changed?.length > 0) ||
|
|
49
|
-
(d.protectedAdded?.length > 0) || (d.protectedRemoved?.length > 0);
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
return checkDiff(diff.routes) || checkDiff(diff.env) ||
|
|
53
|
-
checkDiff(diff.auth) || checkDiff(diff.external);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
module.exports = {
|
|
57
|
-
// Contract builders
|
|
58
|
-
buildRouteContract,
|
|
59
|
-
buildEnvContract,
|
|
60
|
-
buildAuthContract,
|
|
61
|
-
buildExternalContract,
|
|
62
|
-
buildAllContracts,
|
|
63
|
-
|
|
64
|
-
// Contract validation
|
|
65
|
-
validateAgainstRouteContract,
|
|
66
|
-
validateAgainstEnvContract,
|
|
67
|
-
validateAuthContract,
|
|
68
|
-
validateExternalContract,
|
|
69
|
-
|
|
70
|
-
// Contract diffing
|
|
71
|
-
diffRouteContracts,
|
|
72
|
-
diffEnvContracts,
|
|
73
|
-
diffAuthContracts,
|
|
74
|
-
diffExternalContracts,
|
|
75
|
-
diffAllContracts,
|
|
76
|
-
hasContractChanges,
|
|
77
|
-
|
|
78
|
-
// Guard (CI gate)
|
|
79
|
-
loadContracts,
|
|
80
|
-
saveContracts,
|
|
81
|
-
guardContracts,
|
|
82
|
-
getGuardSummary,
|
|
83
|
-
formatGuardResult,
|
|
84
|
-
|
|
85
|
-
// Plan validation (hallucination stopper)
|
|
86
|
-
validatePlan,
|
|
87
|
-
parsePlanActions,
|
|
88
|
-
formatValidationResult
|
|
89
|
-
};
|
|
@@ -1,311 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Plan Validator
|
|
3
|
-
* Validates AI agent plans against contracts
|
|
4
|
-
* This is the core of the "hallucination stopper"
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
"use strict";
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Validate an AI-generated plan against contracts
|
|
11
|
-
* Returns validation result with specific violations
|
|
12
|
-
*/
|
|
13
|
-
function validatePlan(plan, contracts) {
|
|
14
|
-
const result = {
|
|
15
|
-
valid: true,
|
|
16
|
-
violations: [],
|
|
17
|
-
warnings: [],
|
|
18
|
-
suggestions: []
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
// Parse plan to extract intended actions
|
|
22
|
-
const actions = parsePlanActions(plan);
|
|
23
|
-
|
|
24
|
-
// Validate route references
|
|
25
|
-
if (contracts.routes) {
|
|
26
|
-
const routeResult = validateRouteActions(actions, contracts.routes);
|
|
27
|
-
result.violations.push(...routeResult.violations);
|
|
28
|
-
result.warnings.push(...routeResult.warnings);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
// Validate env references
|
|
32
|
-
if (contracts.env) {
|
|
33
|
-
const envResult = validateEnvActions(actions, contracts.env);
|
|
34
|
-
result.violations.push(...envResult.violations);
|
|
35
|
-
result.warnings.push(...envResult.warnings);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
// Validate auth assumptions
|
|
39
|
-
if (contracts.auth) {
|
|
40
|
-
const authResult = validateAuthActions(actions, contracts.auth);
|
|
41
|
-
result.violations.push(...authResult.violations);
|
|
42
|
-
result.warnings.push(...authResult.warnings);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
// Validate external service usage
|
|
46
|
-
if (contracts.external) {
|
|
47
|
-
const externalResult = validateExternalActions(actions, contracts.external);
|
|
48
|
-
result.violations.push(...externalResult.violations);
|
|
49
|
-
result.warnings.push(...externalResult.warnings);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
result.valid = result.violations.length === 0;
|
|
53
|
-
return result;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Parse plan to extract intended actions
|
|
58
|
-
*/
|
|
59
|
-
function parsePlanActions(plan) {
|
|
60
|
-
const actions = {
|
|
61
|
-
routes: [],
|
|
62
|
-
envVars: [],
|
|
63
|
-
files: [],
|
|
64
|
-
authAssumptions: [],
|
|
65
|
-
externalCalls: []
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
// Handle different plan formats
|
|
69
|
-
const planText = typeof plan === "string" ? plan : JSON.stringify(plan);
|
|
70
|
-
|
|
71
|
-
// Extract route references
|
|
72
|
-
const routePatterns = [
|
|
73
|
-
/(?:fetch|axios|api\.)\s*\(\s*['"`]([/][^'"`]+)['"`]/gi,
|
|
74
|
-
/(?:GET|POST|PUT|PATCH|DELETE)\s+([/][^\s]+)/gi,
|
|
75
|
-
/\/api\/[a-z0-9/_-]+/gi
|
|
76
|
-
];
|
|
77
|
-
|
|
78
|
-
for (const pattern of routePatterns) {
|
|
79
|
-
let match;
|
|
80
|
-
while ((match = pattern.exec(planText)) !== null) {
|
|
81
|
-
const path = match[1] || match[0];
|
|
82
|
-
if (path.startsWith("/")) {
|
|
83
|
-
actions.routes.push({
|
|
84
|
-
path: path.replace(/['"`]/g, ""),
|
|
85
|
-
method: inferMethod(match[0])
|
|
86
|
-
});
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
// Extract env var references
|
|
92
|
-
const envPatterns = [
|
|
93
|
-
/process\.env\.([A-Z_][A-Z0-9_]*)/gi,
|
|
94
|
-
/import\.meta\.env\.([A-Z_][A-Z0-9_]*)/gi,
|
|
95
|
-
/\$\{?([A-Z_][A-Z0-9_]*)\}?/g
|
|
96
|
-
];
|
|
97
|
-
|
|
98
|
-
for (const pattern of envPatterns) {
|
|
99
|
-
let match;
|
|
100
|
-
while ((match = pattern.exec(planText)) !== null) {
|
|
101
|
-
if (match[1] && /^[A-Z]/.test(match[1])) {
|
|
102
|
-
actions.envVars.push(match[1]);
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
// Extract file references
|
|
108
|
-
const filePatterns = [
|
|
109
|
-
/(?:create|modify|edit|update|delete|add to)\s+['"`]?([a-z0-9/_.-]+\.[a-z]+)['"`]?/gi,
|
|
110
|
-
/(?:in|at|file)\s+['"`]([^'"`]+)['"`]/gi
|
|
111
|
-
];
|
|
112
|
-
|
|
113
|
-
for (const pattern of filePatterns) {
|
|
114
|
-
let match;
|
|
115
|
-
while ((match = pattern.exec(planText)) !== null) {
|
|
116
|
-
if (match[1]) {
|
|
117
|
-
actions.files.push(match[1]);
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
// Extract auth assumptions
|
|
123
|
-
const authPatterns = [
|
|
124
|
-
/(?:authenticated|logged in|auth required|protected)/gi,
|
|
125
|
-
/(?:public|no auth|unauthenticated)/gi
|
|
126
|
-
];
|
|
127
|
-
|
|
128
|
-
if (/(?:authenticated|logged in|auth required|protected)/i.test(planText)) {
|
|
129
|
-
actions.authAssumptions.push({ type: "requires_auth" });
|
|
130
|
-
}
|
|
131
|
-
if (/(?:public|no auth|unauthenticated)/i.test(planText)) {
|
|
132
|
-
actions.authAssumptions.push({ type: "no_auth" });
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
// Extract external service references
|
|
136
|
-
const externalPatterns = [
|
|
137
|
-
/stripe\./gi,
|
|
138
|
-
/github\./gi,
|
|
139
|
-
/sendgrid\./gi,
|
|
140
|
-
/twilio\./gi,
|
|
141
|
-
/supabase\./gi
|
|
142
|
-
];
|
|
143
|
-
|
|
144
|
-
for (const pattern of externalPatterns) {
|
|
145
|
-
if (pattern.test(planText)) {
|
|
146
|
-
const service = pattern.source.replace(/\\\./g, "").toLowerCase();
|
|
147
|
-
actions.externalCalls.push({ service });
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
return actions;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
function inferMethod(text) {
|
|
155
|
-
const upper = text.toUpperCase();
|
|
156
|
-
if (upper.includes("POST")) return "POST";
|
|
157
|
-
if (upper.includes("PUT")) return "PUT";
|
|
158
|
-
if (upper.includes("PATCH")) return "PATCH";
|
|
159
|
-
if (upper.includes("DELETE")) return "DELETE";
|
|
160
|
-
return "GET";
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
/**
|
|
164
|
-
* Validate route actions against contract
|
|
165
|
-
*/
|
|
166
|
-
function validateRouteActions(actions, routeContract) {
|
|
167
|
-
const violations = [];
|
|
168
|
-
const warnings = [];
|
|
169
|
-
const contractRoutes = new Set(routeContract.routes.map(r => r.path));
|
|
170
|
-
|
|
171
|
-
for (const route of actions.routes) {
|
|
172
|
-
// Check exact match
|
|
173
|
-
if (!contractRoutes.has(route.path)) {
|
|
174
|
-
// Check parameterized match
|
|
175
|
-
const match = routeContract.routes.find(r => matchesParameterized(r.path, route.path));
|
|
176
|
-
|
|
177
|
-
if (!match) {
|
|
178
|
-
violations.push({
|
|
179
|
-
type: "invented_route",
|
|
180
|
-
severity: "BLOCK",
|
|
181
|
-
route: route.path,
|
|
182
|
-
method: route.method,
|
|
183
|
-
message: `Plan references route ${route.method} ${route.path} which does not exist in contract`,
|
|
184
|
-
suggestion: `Available routes: ${routeContract.routes.slice(0, 5).map(r => r.path).join(", ")}...`
|
|
185
|
-
});
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
return { violations, warnings };
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
/**
|
|
194
|
-
* Validate env actions against contract
|
|
195
|
-
*/
|
|
196
|
-
function validateEnvActions(actions, envContract) {
|
|
197
|
-
const violations = [];
|
|
198
|
-
const warnings = [];
|
|
199
|
-
const contractVars = new Set(envContract.vars.map(v => v.name));
|
|
200
|
-
|
|
201
|
-
for (const varName of actions.envVars) {
|
|
202
|
-
if (!contractVars.has(varName)) {
|
|
203
|
-
warnings.push({
|
|
204
|
-
type: "undeclared_env",
|
|
205
|
-
severity: "WARN",
|
|
206
|
-
name: varName,
|
|
207
|
-
message: `Plan uses env var ${varName} which is not in contract`,
|
|
208
|
-
suggestion: "Add to .vibecheck/contracts/env.json or .env.example"
|
|
209
|
-
});
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
return { violations, warnings };
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
/**
|
|
217
|
-
* Validate auth actions against contract
|
|
218
|
-
*/
|
|
219
|
-
function validateAuthActions(actions, authContract) {
|
|
220
|
-
const violations = [];
|
|
221
|
-
const warnings = [];
|
|
222
|
-
|
|
223
|
-
// Check for auth assumptions that conflict with contract
|
|
224
|
-
for (const assumption of actions.authAssumptions) {
|
|
225
|
-
if (assumption.type === "no_auth") {
|
|
226
|
-
// Plan assumes no auth - check if referenced routes are actually public
|
|
227
|
-
warnings.push({
|
|
228
|
-
type: "auth_assumption",
|
|
229
|
-
severity: "WARN",
|
|
230
|
-
message: "Plan assumes some routes are public - verify against auth contract",
|
|
231
|
-
suggestion: `Protected patterns: ${authContract.protectedPatterns.slice(0, 3).join(", ")}...`
|
|
232
|
-
});
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
return { violations, warnings };
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
/**
|
|
240
|
-
* Validate external service actions against contract
|
|
241
|
-
*/
|
|
242
|
-
function validateExternalActions(actions, externalContract) {
|
|
243
|
-
const violations = [];
|
|
244
|
-
const warnings = [];
|
|
245
|
-
const contractServices = new Set(externalContract.services.map(s => s.name));
|
|
246
|
-
|
|
247
|
-
for (const call of actions.externalCalls) {
|
|
248
|
-
if (!contractServices.has(call.service)) {
|
|
249
|
-
warnings.push({
|
|
250
|
-
type: "undeclared_service",
|
|
251
|
-
severity: "WARN",
|
|
252
|
-
service: call.service,
|
|
253
|
-
message: `Plan uses ${call.service} which is not declared in external contract`,
|
|
254
|
-
suggestion: "Add to .vibecheck/contracts/external.json"
|
|
255
|
-
});
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
return { violations, warnings };
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
function matchesParameterized(pattern, actual) {
|
|
263
|
-
const patternParts = pattern.split("/").filter(Boolean);
|
|
264
|
-
const actualParts = actual.split("/").filter(Boolean);
|
|
265
|
-
|
|
266
|
-
if (patternParts.length !== actualParts.length) return false;
|
|
267
|
-
|
|
268
|
-
for (let i = 0; i < patternParts.length; i++) {
|
|
269
|
-
const p = patternParts[i];
|
|
270
|
-
if (p.startsWith(":") || p.startsWith("*")) continue;
|
|
271
|
-
if (p !== actualParts[i]) return false;
|
|
272
|
-
}
|
|
273
|
-
return true;
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
/**
|
|
277
|
-
* Format validation result for display
|
|
278
|
-
*/
|
|
279
|
-
function formatValidationResult(result) {
|
|
280
|
-
const lines = [];
|
|
281
|
-
|
|
282
|
-
if (result.valid) {
|
|
283
|
-
lines.push("✓ Plan validated against contracts");
|
|
284
|
-
} else {
|
|
285
|
-
lines.push("✗ Plan validation failed");
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
if (result.violations.length > 0) {
|
|
289
|
-
lines.push(`\n🛑 VIOLATIONS (${result.violations.length}):`);
|
|
290
|
-
for (const v of result.violations) {
|
|
291
|
-
lines.push(` ✗ ${v.message}`);
|
|
292
|
-
if (v.suggestion) lines.push(` → ${v.suggestion}`);
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
if (result.warnings.length > 0) {
|
|
297
|
-
lines.push(`\n⚠️ WARNINGS (${result.warnings.length}):`);
|
|
298
|
-
for (const w of result.warnings) {
|
|
299
|
-
lines.push(` ⚠ ${w.message}`);
|
|
300
|
-
if (w.suggestion) lines.push(` → ${w.suggestion}`);
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
return lines.join("\n");
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
module.exports = {
|
|
308
|
-
validatePlan,
|
|
309
|
-
parsePlanActions,
|
|
310
|
-
formatValidationResult
|
|
311
|
-
};
|