@vibecheckai/cli 3.9.0 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/bin/runners/context/generators/cursor-enhanced.js +99 -13
- package/bin/runners/lib/unified-cli-output.js +16 -0
- package/bin/runners/runCI.js +353 -0
- package/bin/runners/runCheckpoint.js +2 -2
- package/mcp-server/.eslintrc.json +24 -0
- package/mcp-server/README.md +425 -135
- package/mcp-server/SPEC.md +583 -0
- package/mcp-server/configs/README.md +172 -0
- package/mcp-server/configs/claude-desktop-pro.json +31 -0
- package/mcp-server/configs/claude-desktop-with-workspace.json +25 -0
- package/mcp-server/configs/claude-desktop.json +19 -0
- package/mcp-server/configs/cursor-mcp.json +21 -0
- package/mcp-server/configs/windsurf-mcp.json +17 -0
- package/mcp-server/mcp-config.example.json +9 -0
- package/mcp-server/package.json +49 -34
- package/mcp-server/src/cli.ts +185 -0
- package/mcp-server/src/index.ts +85 -0
- package/mcp-server/src/server.ts +1933 -0
- package/mcp-server/src/services/cache-service.ts +466 -0
- package/mcp-server/src/services/cli-service.ts +345 -0
- package/mcp-server/src/services/context-manager.ts +717 -0
- package/mcp-server/src/services/firewall-service.ts +662 -0
- package/mcp-server/src/services/git-service.ts +671 -0
- package/mcp-server/src/services/index.ts +52 -0
- package/mcp-server/src/services/prompt-builder-service.ts +1031 -0
- package/mcp-server/src/services/session-service.ts +550 -0
- package/mcp-server/src/services/tier-service.ts +470 -0
- package/mcp-server/src/types.ts +351 -0
- package/mcp-server/tsconfig.json +16 -27
- package/package.json +6 -6
- package/mcp-server/.guardrail/audit/audit.log.jsonl +0 -2
- package/mcp-server/.specs/architecture.mdc +0 -90
- package/mcp-server/.specs/security.mdc +0 -30
- package/mcp-server/HARDENING_SUMMARY.md +0 -299
- package/mcp-server/agent-checkpoint.js +0 -364
- package/mcp-server/agent-firewall-interceptor.js +0 -500
- package/mcp-server/architect-tools.js +0 -707
- package/mcp-server/audit-mcp.js +0 -206
- package/mcp-server/authority-tools.js +0 -569
- package/mcp-server/codebase-architect-tools.js +0 -838
- package/mcp-server/conductor/conflict-resolver.js +0 -588
- package/mcp-server/conductor/execution-planner.js +0 -544
- package/mcp-server/conductor/index.js +0 -377
- package/mcp-server/conductor/lock-manager.js +0 -615
- package/mcp-server/conductor/request-queue.js +0 -550
- package/mcp-server/conductor/session-manager.js +0 -500
- package/mcp-server/conductor/tools.js +0 -510
- package/mcp-server/consolidated-tools.js +0 -1170
- package/mcp-server/deprecation-middleware.js +0 -282
- package/mcp-server/handlers/index.ts +0 -15
- package/mcp-server/handlers/tool-handler.ts +0 -593
- package/mcp-server/hygiene-tools.js +0 -428
- package/mcp-server/index-v1.js +0 -698
- package/mcp-server/index.js +0 -2940
- package/mcp-server/intelligence-tools.js +0 -664
- package/mcp-server/intent-drift-tools.js +0 -873
- package/mcp-server/intent-firewall-interceptor.js +0 -529
- package/mcp-server/lib/api-client.cjs +0 -13
- package/mcp-server/lib/cache-wrapper.cjs +0 -383
- package/mcp-server/lib/error-envelope.js +0 -138
- package/mcp-server/lib/executor.ts +0 -499
- package/mcp-server/lib/index.ts +0 -29
- package/mcp-server/lib/logger.cjs +0 -30
- package/mcp-server/lib/rate-limiter.js +0 -166
- package/mcp-server/lib/sandbox.test.ts +0 -519
- package/mcp-server/lib/sandbox.ts +0 -395
- package/mcp-server/lib/types.ts +0 -267
- package/mcp-server/logger.js +0 -173
- package/mcp-server/manifest.json +0 -473
- package/mcp-server/mdc-generator.js +0 -298
- package/mcp-server/premium-tools.js +0 -1275
- package/mcp-server/proof-tools.js +0 -571
- package/mcp-server/registry/tool-registry.js +0 -586
- package/mcp-server/registry/tools.json +0 -619
- package/mcp-server/registry.test.ts +0 -340
- package/mcp-server/test-mcp.js +0 -108
- package/mcp-server/test-tools.js +0 -36
- package/mcp-server/tests/tier-gating.test.js +0 -297
- package/mcp-server/tier-auth.js +0 -767
- package/mcp-server/tools/index.js +0 -72
- package/mcp-server/tools-reorganized.ts +0 -244
- package/mcp-server/tools-v3.js +0 -1004
- package/mcp-server/truth-context.js +0 -622
- package/mcp-server/truth-firewall-tools.js +0 -2183
- package/mcp-server/vibecheck-2.0-tools.js +0 -761
- package/mcp-server/vibecheck-mcp-server-3.2.0.tgz +0 -0
- package/mcp-server/vibecheck-tools.js +0 -1075
|
@@ -1,622 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Truth Context – MCP Tools for Evidence‑Backed AI
|
|
3
|
-
*
|
|
4
|
-
* Core context-engine tools that surface **truth-backed** context for AI agents.
|
|
5
|
-
* Every response is grounded in concrete evidence with file/line citations
|
|
6
|
-
* and explicit confidence scores.
|
|
7
|
-
*
|
|
8
|
-
* This is the "Truth Firewall", exposed to agents as an "Evidence Pack" / "Truth Pack". [web:3]
|
|
9
|
-
*
|
|
10
|
-
* Tools:
|
|
11
|
-
* - vibecheck.ctx – Build a repo-level Truth Pack (routes, auth, billing, env, schema)
|
|
12
|
-
* - vibecheck.verify_claim – Check whether a claim is backed by real evidence
|
|
13
|
-
* - vibecheck.evidence – Pull code-level evidence for a specific file/function
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
import fs from "fs/promises";
|
|
17
|
-
import path from "path";
|
|
18
|
-
import { execSync } from "child_process";
|
|
19
|
-
|
|
20
|
-
// ============================================================================
|
|
21
|
-
// TRUTH CONTEXT TOOLS
|
|
22
|
-
// ============================================================================
|
|
23
|
-
|
|
24
|
-
export const TRUTH_CONTEXT_TOOLS = [
|
|
25
|
-
{
|
|
26
|
-
name: "vibecheck.ctx",
|
|
27
|
-
description: `📋 Build a repo Truth Pack: routes, auth, billing, env vars, schema.
|
|
28
|
-
|
|
29
|
-
Generates an evidence-backed context bundle with file/line citations.
|
|
30
|
-
Use this before the agent makes any architectural or behavioral claims
|
|
31
|
-
about the codebase.
|
|
32
|
-
|
|
33
|
-
Returns:
|
|
34
|
-
- routes: All detected routes with handlers and middleware
|
|
35
|
-
- auth: Auth guards, protected routes, auth flow indicators
|
|
36
|
-
- billing: Payment gates, subscription checks, paid feature indicators
|
|
37
|
-
- env: Environment variables (declared vs used, mismatches)
|
|
38
|
-
- schema: Database schema and TypeScript contracts
|
|
39
|
-
- confidence: Aggregate confidence score (0–1) for the extracted view`,
|
|
40
|
-
inputSchema: {
|
|
41
|
-
type: "object",
|
|
42
|
-
properties: {
|
|
43
|
-
scope: {
|
|
44
|
-
type: "string",
|
|
45
|
-
enum: ["all", "routes", "auth", "billing", "env", "schema"],
|
|
46
|
-
description: "Which slice of context to extract (default: all)",
|
|
47
|
-
default: "all",
|
|
48
|
-
},
|
|
49
|
-
path: {
|
|
50
|
-
type: "string",
|
|
51
|
-
description: "Project root path (default: current working directory)",
|
|
52
|
-
},
|
|
53
|
-
},
|
|
54
|
-
},
|
|
55
|
-
},
|
|
56
|
-
{
|
|
57
|
-
name: "vibecheck.verify_claim",
|
|
58
|
-
description: `🔍 Truth Firewall check – verify that a claim is backed by code.
|
|
59
|
-
|
|
60
|
-
Run this before asserting that something exists, is configured, or is enforced.
|
|
61
|
-
Returns concrete evidence (file/line) when the claim is supported,
|
|
62
|
-
or a structured rejection with an explanation when it is not.
|
|
63
|
-
|
|
64
|
-
Examples:
|
|
65
|
-
- "Route /api/users exists" → VERIFIED with handler at src/routes/users.ts:45
|
|
66
|
-
- "Auth is required for /admin" → VERIFIED via middleware at src/middleware/auth.ts:12
|
|
67
|
-
- "Stripe is configured" → REJECTED: No evidence of Stripe integration found`,
|
|
68
|
-
inputSchema: {
|
|
69
|
-
type: "object",
|
|
70
|
-
properties: {
|
|
71
|
-
claim_type: {
|
|
72
|
-
type: "string",
|
|
73
|
-
enum: [
|
|
74
|
-
"route",
|
|
75
|
-
"endpoint",
|
|
76
|
-
"env_var",
|
|
77
|
-
"middleware",
|
|
78
|
-
"auth_guard",
|
|
79
|
-
"billing_gate",
|
|
80
|
-
"file",
|
|
81
|
-
"function",
|
|
82
|
-
],
|
|
83
|
-
description: "Category of claim to verify",
|
|
84
|
-
},
|
|
85
|
-
claim: {
|
|
86
|
-
type: "string",
|
|
87
|
-
description:
|
|
88
|
-
"The claim subject (e.g. '/api/users', 'AUTH_SECRET', 'authMiddleware')",
|
|
89
|
-
},
|
|
90
|
-
path: {
|
|
91
|
-
type: "string",
|
|
92
|
-
description: "Project root path (default: current working directory)",
|
|
93
|
-
},
|
|
94
|
-
},
|
|
95
|
-
required: ["claim_type", "claim"],
|
|
96
|
-
},
|
|
97
|
-
},
|
|
98
|
-
{
|
|
99
|
-
name: "vibecheck.evidence",
|
|
100
|
-
description: `📎 Retrieve code evidence for a file or symbol.
|
|
101
|
-
|
|
102
|
-
Returns an annotated code snippet with line numbers for precise citation.
|
|
103
|
-
Use this when the agent needs to quote or reason about specific code blocks
|
|
104
|
-
in its response.`,
|
|
105
|
-
inputSchema: {
|
|
106
|
-
type: "object",
|
|
107
|
-
properties: {
|
|
108
|
-
file: {
|
|
109
|
-
type: "string",
|
|
110
|
-
description: "File path relative to the project root",
|
|
111
|
-
},
|
|
112
|
-
function_name: {
|
|
113
|
-
type: "string",
|
|
114
|
-
description: "Optional function/class name to locate within the file",
|
|
115
|
-
},
|
|
116
|
-
line: {
|
|
117
|
-
type: "number",
|
|
118
|
-
description: "Optional 1-based line number to center the snippet on",
|
|
119
|
-
},
|
|
120
|
-
context_lines: {
|
|
121
|
-
type: "number",
|
|
122
|
-
description:
|
|
123
|
-
"Number of lines of context before/after the target (default: 10)",
|
|
124
|
-
default: 10,
|
|
125
|
-
},
|
|
126
|
-
path: {
|
|
127
|
-
type: "string",
|
|
128
|
-
description: "Project root path (default: current working directory)",
|
|
129
|
-
},
|
|
130
|
-
},
|
|
131
|
-
required: ["file"],
|
|
132
|
-
},
|
|
133
|
-
},
|
|
134
|
-
];
|
|
135
|
-
|
|
136
|
-
// ============================================================================
|
|
137
|
-
// TOOL DISPATCH
|
|
138
|
-
// ============================================================================
|
|
139
|
-
|
|
140
|
-
export async function handleTruthContextTool(toolName, args) {
|
|
141
|
-
const projectPath = args.path || process.cwd();
|
|
142
|
-
|
|
143
|
-
switch (toolName) {
|
|
144
|
-
case "vibecheck.ctx":
|
|
145
|
-
return await getTruthPack(projectPath, args.scope || "all");
|
|
146
|
-
case "vibecheck.verify_claim":
|
|
147
|
-
return await verifyClaim(projectPath, args.claim_type, args.claim);
|
|
148
|
-
case "vibecheck.evidence":
|
|
149
|
-
return await getEvidence(projectPath, args.file, args);
|
|
150
|
-
default:
|
|
151
|
-
return { error: `Unknown tool: ${toolName}` };
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
// ============================================================================
|
|
156
|
-
// CONTEXT EXTRACTION
|
|
157
|
-
// ============================================================================
|
|
158
|
-
|
|
159
|
-
async function getTruthPack(projectPath, scope) {
|
|
160
|
-
const truthPack = {
|
|
161
|
-
version: "1.0.0",
|
|
162
|
-
generatedAt: new Date().toISOString(),
|
|
163
|
-
projectPath,
|
|
164
|
-
scope,
|
|
165
|
-
confidence: 0,
|
|
166
|
-
sections: {},
|
|
167
|
-
};
|
|
168
|
-
|
|
169
|
-
try {
|
|
170
|
-
if (scope === "all" || scope === "routes") {
|
|
171
|
-
truthPack.sections.routes = await extractRoutes(projectPath);
|
|
172
|
-
}
|
|
173
|
-
if (scope === "all" || scope === "auth") {
|
|
174
|
-
truthPack.sections.auth = await extractAuth(projectPath);
|
|
175
|
-
}
|
|
176
|
-
if (scope === "all" || scope === "billing") {
|
|
177
|
-
truthPack.sections.billing = await extractBilling(projectPath);
|
|
178
|
-
}
|
|
179
|
-
if (scope === "all" || scope === "env") {
|
|
180
|
-
truthPack.sections.env = await extractEnvVars(projectPath);
|
|
181
|
-
}
|
|
182
|
-
if (scope === "all" || scope === "schema") {
|
|
183
|
-
truthPack.sections.schema = await extractSchema(projectPath);
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
const sections = Object.values(truthPack.sections);
|
|
187
|
-
if (sections.length > 0) {
|
|
188
|
-
truthPack.confidence =
|
|
189
|
-
sections.reduce((sum, section) => sum + (section.confidence || 0), 0) /
|
|
190
|
-
sections.length;
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
return truthPack;
|
|
194
|
-
} catch (error) {
|
|
195
|
-
return {
|
|
196
|
-
error: error.message,
|
|
197
|
-
projectPath,
|
|
198
|
-
suggestion: "Run `vibecheck init` to set up the project",
|
|
199
|
-
};
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
async function extractRoutes(projectPath) {
|
|
204
|
-
const routes = [];
|
|
205
|
-
const routePatterns = [
|
|
206
|
-
/app\.(get|post|put|patch|delete|use)\s*\(\s*['"`]([^'"`]+)['"`]/gi,
|
|
207
|
-
/router\.(get|post|put|patch|delete|use)\s*\(\s*['"`]([^'"`]+)['"`]/gi,
|
|
208
|
-
/@(Get|Post|Put|Patch|Delete)\s*\(\s*['"`]([^'"`]+)['"`]/gi,
|
|
209
|
-
];
|
|
210
|
-
|
|
211
|
-
const files = await findSourceFiles(projectPath, [".ts", ".js", ".tsx", ".jsx"]);
|
|
212
|
-
|
|
213
|
-
for (const file of files.slice(0, 50)) {
|
|
214
|
-
try {
|
|
215
|
-
const content = await fs.readFile(file, "utf8");
|
|
216
|
-
const relPath = path.relative(projectPath, file);
|
|
217
|
-
|
|
218
|
-
for (const pattern of routePatterns) {
|
|
219
|
-
let match;
|
|
220
|
-
pattern.lastIndex = 0;
|
|
221
|
-
while ((match = pattern.exec(content)) !== null) {
|
|
222
|
-
const line = content.substring(0, match.index).split("\n").length;
|
|
223
|
-
routes.push({
|
|
224
|
-
method: match[1].toUpperCase(),
|
|
225
|
-
path: match[2],
|
|
226
|
-
file: relPath,
|
|
227
|
-
line,
|
|
228
|
-
evidence: {
|
|
229
|
-
snippet: content.split("\n")[line - 1]?.trim(),
|
|
230
|
-
verifiedAt: new Date().toISOString(),
|
|
231
|
-
},
|
|
232
|
-
});
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
} catch {
|
|
236
|
-
// Skip unreadable files
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
return {
|
|
241
|
-
count: routes.length,
|
|
242
|
-
routes: routes.slice(0, 100),
|
|
243
|
-
confidence: routes.length > 0 ? 0.8 : 0.2,
|
|
244
|
-
};
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
async function extractAuth(projectPath) {
|
|
248
|
-
const authIndicators = [];
|
|
249
|
-
const authPatterns = [
|
|
250
|
-
/auth(enticate|orize|Middleware|Guard|Check)/gi,
|
|
251
|
-
/isAuthenticated|requireAuth|verifyToken|jwt\.verify/gi,
|
|
252
|
-
/passport\.(authenticate|use)/gi,
|
|
253
|
-
/session\.|cookie\./gi,
|
|
254
|
-
];
|
|
255
|
-
|
|
256
|
-
const files = await findSourceFiles(projectPath, [".ts", ".js"]);
|
|
257
|
-
|
|
258
|
-
for (const file of files.slice(0, 50)) {
|
|
259
|
-
try {
|
|
260
|
-
const content = await fs.readFile(file, "utf8");
|
|
261
|
-
const relPath = path.relative(projectPath, file);
|
|
262
|
-
|
|
263
|
-
for (const pattern of authPatterns) {
|
|
264
|
-
let match;
|
|
265
|
-
pattern.lastIndex = 0;
|
|
266
|
-
while ((match = pattern.exec(content)) !== null) {
|
|
267
|
-
const line = content.substring(0, match.index).split("\n").length;
|
|
268
|
-
authIndicators.push({
|
|
269
|
-
type: "auth_indicator",
|
|
270
|
-
match: match[0],
|
|
271
|
-
file: relPath,
|
|
272
|
-
line,
|
|
273
|
-
});
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
} catch {
|
|
277
|
-
// Skip
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
return {
|
|
282
|
-
count: authIndicators.length,
|
|
283
|
-
indicators: authIndicators.slice(0, 50),
|
|
284
|
-
confidence:
|
|
285
|
-
authIndicators.length > 5
|
|
286
|
-
? 0.8
|
|
287
|
-
: authIndicators.length > 0
|
|
288
|
-
? 0.5
|
|
289
|
-
: 0.1,
|
|
290
|
-
};
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
async function extractBilling(projectPath) {
|
|
294
|
-
const billingIndicators = [];
|
|
295
|
-
const billingPatterns = [
|
|
296
|
-
/stripe|paddle|lemonsqueezy|gumroad/gi,
|
|
297
|
-
/subscription|payment|checkout|invoice/gi,
|
|
298
|
-
/isPro|isPremium|isEnterprise|hasPaid/gi,
|
|
299
|
-
/price|tier|plan/gi,
|
|
300
|
-
];
|
|
301
|
-
|
|
302
|
-
const files = await findSourceFiles(projectPath, [".ts", ".js"]);
|
|
303
|
-
|
|
304
|
-
for (const file of files.slice(0, 30)) {
|
|
305
|
-
try {
|
|
306
|
-
const content = await fs.readFile(file, "utf8");
|
|
307
|
-
const relPath = path.relative(projectPath, file);
|
|
308
|
-
|
|
309
|
-
for (const pattern of billingPatterns) {
|
|
310
|
-
let match;
|
|
311
|
-
pattern.lastIndex = 0;
|
|
312
|
-
while ((match = pattern.exec(content)) !== null) {
|
|
313
|
-
const line = content.substring(0, match.index).split("\n").length;
|
|
314
|
-
billingIndicators.push({
|
|
315
|
-
type: "billing_indicator",
|
|
316
|
-
match: match[0],
|
|
317
|
-
file: relPath,
|
|
318
|
-
line,
|
|
319
|
-
});
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
} catch {
|
|
323
|
-
// Skip
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
return {
|
|
328
|
-
count: billingIndicators.length,
|
|
329
|
-
indicators: billingIndicators.slice(0, 30),
|
|
330
|
-
confidence:
|
|
331
|
-
billingIndicators.length > 3
|
|
332
|
-
? 0.7
|
|
333
|
-
: billingIndicators.length > 0
|
|
334
|
-
? 0.4
|
|
335
|
-
: 0.1,
|
|
336
|
-
};
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
async function extractEnvVars(projectPath) {
|
|
340
|
-
const declared = [];
|
|
341
|
-
const used = [];
|
|
342
|
-
|
|
343
|
-
const envFiles = [".env.example", ".env.local.example", ".env.sample"];
|
|
344
|
-
for (const envFile of envFiles) {
|
|
345
|
-
try {
|
|
346
|
-
const content = await fs.readFile(path.join(projectPath, envFile), "utf8");
|
|
347
|
-
const lines = content.split("\n");
|
|
348
|
-
for (let i = 0; i < lines.length; i++) {
|
|
349
|
-
const match = lines[i].match(/^([A-Z][A-Z0-9_]*)=/);
|
|
350
|
-
if (match) {
|
|
351
|
-
declared.push({
|
|
352
|
-
name: match[1],
|
|
353
|
-
file: envFile,
|
|
354
|
-
line: i + 1,
|
|
355
|
-
});
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
} catch {
|
|
359
|
-
// File does not exist
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
const files = await findSourceFiles(projectPath, [".ts", ".js"]);
|
|
364
|
-
for (const file of files.slice(0, 30)) {
|
|
365
|
-
try {
|
|
366
|
-
const content = await fs.readFile(file, "utf8");
|
|
367
|
-
const relPath = path.relative(projectPath, file);
|
|
368
|
-
const pattern = /process\.env\.([A-Z][A-Z0-9_]*)/g;
|
|
369
|
-
let match;
|
|
370
|
-
while ((match = pattern.exec(content)) !== null) {
|
|
371
|
-
const line = content.substring(0, match.index).split("\n").length;
|
|
372
|
-
used.push({
|
|
373
|
-
name: match[1],
|
|
374
|
-
file: relPath,
|
|
375
|
-
line,
|
|
376
|
-
});
|
|
377
|
-
}
|
|
378
|
-
} catch {
|
|
379
|
-
// Skip
|
|
380
|
-
}
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
const declaredNames = new Set(declared.map((d) => d.name));
|
|
384
|
-
const usedNames = new Set(used.map((u) => u.name));
|
|
385
|
-
const undeclared = [...usedNames].filter((name) => !declaredNames.has(name));
|
|
386
|
-
const unused = [...declaredNames].filter((name) => !usedNames.has(name));
|
|
387
|
-
|
|
388
|
-
return {
|
|
389
|
-
declared: declared.slice(0, 50),
|
|
390
|
-
used: used.slice(0, 50),
|
|
391
|
-
mismatches: {
|
|
392
|
-
undeclared,
|
|
393
|
-
unused,
|
|
394
|
-
},
|
|
395
|
-
confidence: undeclared.length === 0 ? 0.9 : 0.5,
|
|
396
|
-
};
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
async function extractSchema(projectPath) {
|
|
400
|
-
const schemas = [];
|
|
401
|
-
|
|
402
|
-
try {
|
|
403
|
-
const prismaPath = path.join(projectPath, "prisma", "schema.prisma");
|
|
404
|
-
const content = await fs.readFile(prismaPath, "utf8");
|
|
405
|
-
const modelMatches = content.matchAll(/model\s+(\w+)\s*\{/g);
|
|
406
|
-
for (const match of modelMatches) {
|
|
407
|
-
schemas.push({
|
|
408
|
-
type: "prisma_model",
|
|
409
|
-
name: match[1],
|
|
410
|
-
file: "prisma/schema.prisma",
|
|
411
|
-
});
|
|
412
|
-
}
|
|
413
|
-
} catch {
|
|
414
|
-
// No Prisma schema
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
const files = await findSourceFiles(projectPath, [".ts", ".tsx"]);
|
|
418
|
-
for (const file of files.slice(0, 20)) {
|
|
419
|
-
try {
|
|
420
|
-
const content = await fs.readFile(file, "utf8");
|
|
421
|
-
const relPath = path.relative(projectPath, file);
|
|
422
|
-
|
|
423
|
-
const typeMatches = content.matchAll(/(?:interface|type)\s+(\w+)/g);
|
|
424
|
-
for (const match of typeMatches) {
|
|
425
|
-
const line = content.substring(0, match.index).split("\n").length;
|
|
426
|
-
schemas.push({
|
|
427
|
-
type: "typescript_type",
|
|
428
|
-
name: match[1],
|
|
429
|
-
file: relPath,
|
|
430
|
-
line,
|
|
431
|
-
});
|
|
432
|
-
}
|
|
433
|
-
} catch {
|
|
434
|
-
// Skip
|
|
435
|
-
}
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
return {
|
|
439
|
-
count: schemas.length,
|
|
440
|
-
schemas: schemas.slice(0, 50),
|
|
441
|
-
confidence:
|
|
442
|
-
schemas.length > 5 ? 0.7 : schemas.length > 0 ? 0.4 : 0.2,
|
|
443
|
-
};
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
// ============================================================================
|
|
447
|
-
// CLAIM VERIFICATION
|
|
448
|
-
// ============================================================================
|
|
449
|
-
|
|
450
|
-
async function verifyClaim(projectPath, claimType, claim) {
|
|
451
|
-
const result = {
|
|
452
|
-
claim: { type: claimType, value: claim },
|
|
453
|
-
verified: false,
|
|
454
|
-
evidence: null,
|
|
455
|
-
confidence: 0,
|
|
456
|
-
rejection: null,
|
|
457
|
-
};
|
|
458
|
-
|
|
459
|
-
try {
|
|
460
|
-
switch (claimType) {
|
|
461
|
-
case "file": {
|
|
462
|
-
const filePath = path.join(projectPath, claim);
|
|
463
|
-
try {
|
|
464
|
-
await fs.access(filePath);
|
|
465
|
-
const stats = await fs.stat(filePath);
|
|
466
|
-
result.verified = true;
|
|
467
|
-
result.confidence = 1.0;
|
|
468
|
-
result.evidence = {
|
|
469
|
-
file: claim,
|
|
470
|
-
exists: true,
|
|
471
|
-
size: stats.size,
|
|
472
|
-
verifiedAt: new Date().toISOString(),
|
|
473
|
-
};
|
|
474
|
-
} catch {
|
|
475
|
-
result.rejection = `File does not exist: ${claim}`;
|
|
476
|
-
}
|
|
477
|
-
break;
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
case "route":
|
|
481
|
-
case "endpoint": {
|
|
482
|
-
const routes = await extractRoutes(projectPath);
|
|
483
|
-
const matchingRoute = routes.routes.find(
|
|
484
|
-
(route) => route.path === claim || route.path.includes(claim),
|
|
485
|
-
);
|
|
486
|
-
if (matchingRoute) {
|
|
487
|
-
result.verified = true;
|
|
488
|
-
result.confidence = 0.9;
|
|
489
|
-
result.evidence = matchingRoute;
|
|
490
|
-
} else {
|
|
491
|
-
result.rejection = `No route matching "${claim}" found in codebase`;
|
|
492
|
-
}
|
|
493
|
-
break;
|
|
494
|
-
}
|
|
495
|
-
|
|
496
|
-
case "env_var": {
|
|
497
|
-
const envData = await extractEnvVars(projectPath);
|
|
498
|
-
const isDeclared = envData.declared.some((env) => env.name === claim);
|
|
499
|
-
const isUsed = envData.used.some((env) => env.name === claim);
|
|
500
|
-
if (isDeclared || isUsed) {
|
|
501
|
-
result.verified = true;
|
|
502
|
-
result.confidence = isDeclared && isUsed ? 1.0 : 0.7;
|
|
503
|
-
result.evidence = {
|
|
504
|
-
declared: isDeclared,
|
|
505
|
-
used: isUsed,
|
|
506
|
-
locations: [
|
|
507
|
-
...envData.declared.filter((env) => env.name === claim),
|
|
508
|
-
...envData.used.filter((env) => env.name === claim),
|
|
509
|
-
],
|
|
510
|
-
};
|
|
511
|
-
} else {
|
|
512
|
-
result.rejection = `Environment variable "${claim}" not found`;
|
|
513
|
-
}
|
|
514
|
-
break;
|
|
515
|
-
}
|
|
516
|
-
|
|
517
|
-
default:
|
|
518
|
-
result.rejection = `Claim type "${claimType}" verification is not implemented yet`;
|
|
519
|
-
}
|
|
520
|
-
} catch (error) {
|
|
521
|
-
result.rejection = `Verification error: ${error.message}`;
|
|
522
|
-
}
|
|
523
|
-
|
|
524
|
-
return result;
|
|
525
|
-
}
|
|
526
|
-
|
|
527
|
-
// ============================================================================
|
|
528
|
-
// EVIDENCE EXTRACTION
|
|
529
|
-
// ============================================================================
|
|
530
|
-
|
|
531
|
-
async function getEvidence(projectPath, file, options) {
|
|
532
|
-
const filePath = path.join(projectPath, file);
|
|
533
|
-
|
|
534
|
-
try {
|
|
535
|
-
const content = await fs.readFile(filePath, "utf8");
|
|
536
|
-
const lines = content.split("\n");
|
|
537
|
-
|
|
538
|
-
let targetLine = options.line || 1;
|
|
539
|
-
const contextLines = options.context_lines || 10;
|
|
540
|
-
|
|
541
|
-
if (options.function_name) {
|
|
542
|
-
const pattern = new RegExp(
|
|
543
|
-
`(function|const|let|var|class)\\s+${options.function_name}`,
|
|
544
|
-
"i",
|
|
545
|
-
);
|
|
546
|
-
for (let i = 0; i < lines.length; i++) {
|
|
547
|
-
if (pattern.test(lines[i])) {
|
|
548
|
-
targetLine = i + 1;
|
|
549
|
-
break;
|
|
550
|
-
}
|
|
551
|
-
}
|
|
552
|
-
}
|
|
553
|
-
|
|
554
|
-
const startLine = Math.max(1, targetLine - contextLines);
|
|
555
|
-
const endLine = Math.min(lines.length, targetLine + contextLines);
|
|
556
|
-
|
|
557
|
-
const snippet = lines
|
|
558
|
-
.slice(startLine - 1, endLine)
|
|
559
|
-
.map(
|
|
560
|
-
(line, index) =>
|
|
561
|
-
`${String(startLine + index).padStart(4, " ")} | ${line}`,
|
|
562
|
-
)
|
|
563
|
-
.join("\n");
|
|
564
|
-
|
|
565
|
-
return {
|
|
566
|
-
file,
|
|
567
|
-
targetLine,
|
|
568
|
-
startLine,
|
|
569
|
-
endLine,
|
|
570
|
-
totalLines: lines.length,
|
|
571
|
-
snippet,
|
|
572
|
-
verifiedAt: new Date().toISOString(),
|
|
573
|
-
};
|
|
574
|
-
} catch (error) {
|
|
575
|
-
return {
|
|
576
|
-
error: `Cannot read file: ${error.message}`,
|
|
577
|
-
file,
|
|
578
|
-
};
|
|
579
|
-
}
|
|
580
|
-
}
|
|
581
|
-
|
|
582
|
-
// ============================================================================
|
|
583
|
-
// UTILITIES
|
|
584
|
-
// ============================================================================
|
|
585
|
-
|
|
586
|
-
async function findSourceFiles(projectPath, extensions) {
|
|
587
|
-
const files = [];
|
|
588
|
-
|
|
589
|
-
async function walk(dir) {
|
|
590
|
-
try {
|
|
591
|
-
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
592
|
-
for (const entry of entries) {
|
|
593
|
-
const fullPath = path.join(dir, entry.name);
|
|
594
|
-
if (entry.isDirectory()) {
|
|
595
|
-
if (
|
|
596
|
-
!entry.name.startsWith(".") &&
|
|
597
|
-
entry.name !== "node_modules" &&
|
|
598
|
-
entry.name !== "dist" &&
|
|
599
|
-
entry.name !== "build"
|
|
600
|
-
) {
|
|
601
|
-
await walk(fullPath);
|
|
602
|
-
}
|
|
603
|
-
} else if (entry.isFile()) {
|
|
604
|
-
const ext = path.extname(entry.name).toLowerCase();
|
|
605
|
-
if (extensions.includes(ext)) {
|
|
606
|
-
files.push(fullPath);
|
|
607
|
-
}
|
|
608
|
-
}
|
|
609
|
-
}
|
|
610
|
-
} catch {
|
|
611
|
-
// Skip inaccessible directories
|
|
612
|
-
}
|
|
613
|
-
}
|
|
614
|
-
|
|
615
|
-
await walk(projectPath);
|
|
616
|
-
return files;
|
|
617
|
-
}
|
|
618
|
-
|
|
619
|
-
export default {
|
|
620
|
-
TRUTH_CONTEXT_TOOLS,
|
|
621
|
-
handleTruthContextTool,
|
|
622
|
-
};
|