@triedotdev/mcp 1.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/QUICK_START.md +230 -0
- package/README.md +235 -0
- package/dist/chunk-3CS6Z2SL.js +927 -0
- package/dist/chunk-3CS6Z2SL.js.map +1 -0
- package/dist/chunk-6NLHFIYA.js +344 -0
- package/dist/chunk-6NLHFIYA.js.map +1 -0
- package/dist/chunk-DGUM43GV.js +11 -0
- package/dist/chunk-DGUM43GV.js.map +1 -0
- package/dist/chunk-E7CKHS3R.js +7615 -0
- package/dist/chunk-E7CKHS3R.js.map +1 -0
- package/dist/chunk-EYNAGEQK.js +950 -0
- package/dist/chunk-EYNAGEQK.js.map +1 -0
- package/dist/chunk-MR755QGT.js +927 -0
- package/dist/chunk-MR755QGT.js.map +1 -0
- package/dist/cli/create-agent.d.ts +1 -0
- package/dist/cli/create-agent.js +156 -0
- package/dist/cli/create-agent.js.map +1 -0
- package/dist/cli/main.d.ts +1 -0
- package/dist/cli/main.js +280 -0
- package/dist/cli/main.js.map +1 -0
- package/dist/cli/yolo-daemon.d.ts +1 -0
- package/dist/cli/yolo-daemon.js +326 -0
- package/dist/cli/yolo-daemon.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +3882 -0
- package/dist/index.js.map +1 -0
- package/dist/vibe-code-signatures-4CBHUSI7.js +15 -0
- package/dist/vibe-code-signatures-4CBHUSI7.js.map +1 -0
- package/dist/vulnerability-signatures-J3CUQ7VR.js +17 -0
- package/dist/vulnerability-signatures-J3CUQ7VR.js.map +1 -0
- package/package.json +77 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,3882 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
listCustomAgents,
|
|
4
|
+
loadAgentConfig,
|
|
5
|
+
parseDocument
|
|
6
|
+
} from "./chunk-EYNAGEQK.js";
|
|
7
|
+
import {
|
|
8
|
+
AgentRegistry,
|
|
9
|
+
TrieFixTool,
|
|
10
|
+
TrieScanTool,
|
|
11
|
+
getPrompt,
|
|
12
|
+
getSystemPrompt
|
|
13
|
+
} from "./chunk-E7CKHS3R.js";
|
|
14
|
+
import "./chunk-3CS6Z2SL.js";
|
|
15
|
+
import "./chunk-MR755QGT.js";
|
|
16
|
+
import "./chunk-6NLHFIYA.js";
|
|
17
|
+
import "./chunk-DGUM43GV.js";
|
|
18
|
+
|
|
19
|
+
// src/index.ts
|
|
20
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
21
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
22
|
+
import {
|
|
23
|
+
CallToolRequestSchema,
|
|
24
|
+
ListToolsRequestSchema,
|
|
25
|
+
ListResourcesRequestSchema,
|
|
26
|
+
ReadResourceRequestSchema
|
|
27
|
+
} from "@modelcontextprotocol/sdk/types.js";
|
|
28
|
+
import { readdir as readdir2, readFile as readFile6 } from "fs/promises";
|
|
29
|
+
import { join as join6 } from "path";
|
|
30
|
+
|
|
31
|
+
// src/tools/explain.ts
|
|
32
|
+
import { readFile } from "fs/promises";
|
|
33
|
+
import { existsSync } from "fs";
|
|
34
|
+
import { extname, relative, resolve, isAbsolute } from "path";
|
|
35
|
+
var TrieExplainTool = class {
|
|
36
|
+
async execute(args) {
|
|
37
|
+
const { type, target, context, depth = "standard" } = args || {};
|
|
38
|
+
if (!type || !target) {
|
|
39
|
+
return {
|
|
40
|
+
content: [{
|
|
41
|
+
type: "text",
|
|
42
|
+
text: this.getHelpText()
|
|
43
|
+
}]
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
switch (type) {
|
|
47
|
+
case "code":
|
|
48
|
+
return this.explainCode(target, context, depth);
|
|
49
|
+
case "issue":
|
|
50
|
+
return this.explainIssue(target, context);
|
|
51
|
+
case "change":
|
|
52
|
+
return this.explainChange(target, context);
|
|
53
|
+
case "risk":
|
|
54
|
+
return this.explainRisk(target, context);
|
|
55
|
+
default:
|
|
56
|
+
return {
|
|
57
|
+
content: [{
|
|
58
|
+
type: "text",
|
|
59
|
+
text: `Unknown explanation type: ${type}`
|
|
60
|
+
}]
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
async explainCode(target, context, _depth) {
|
|
65
|
+
let code;
|
|
66
|
+
let filePath;
|
|
67
|
+
let language;
|
|
68
|
+
const resolvedPath = isAbsolute(target) ? target : resolve(process.cwd(), target);
|
|
69
|
+
if (existsSync(resolvedPath)) {
|
|
70
|
+
code = await readFile(resolvedPath, "utf-8");
|
|
71
|
+
filePath = relative(process.cwd(), resolvedPath);
|
|
72
|
+
language = this.detectLanguage(resolvedPath);
|
|
73
|
+
} else {
|
|
74
|
+
code = target;
|
|
75
|
+
filePath = "inline";
|
|
76
|
+
language = this.guessLanguage(code);
|
|
77
|
+
}
|
|
78
|
+
const imports = this.extractImports(code, language);
|
|
79
|
+
const exports = this.extractExports(code);
|
|
80
|
+
const functions = this.extractFunctions(code, language);
|
|
81
|
+
const prompt = getPrompt("explain", "code", {
|
|
82
|
+
code,
|
|
83
|
+
language,
|
|
84
|
+
filePath
|
|
85
|
+
});
|
|
86
|
+
const systemPrompt = getSystemPrompt("explain");
|
|
87
|
+
let output = `
|
|
88
|
+
${"\u2501".repeat(60)}
|
|
89
|
+
`;
|
|
90
|
+
output += `\u{1F4D6} CODE EXPLANATION
|
|
91
|
+
`;
|
|
92
|
+
output += `${"\u2501".repeat(60)}
|
|
93
|
+
|
|
94
|
+
`;
|
|
95
|
+
output += `## \u{1F4C2} Source
|
|
96
|
+
|
|
97
|
+
`;
|
|
98
|
+
output += `- **File:** \`${filePath}\`
|
|
99
|
+
`;
|
|
100
|
+
output += `- **Language:** ${language}
|
|
101
|
+
`;
|
|
102
|
+
output += `- **Lines:** ${code.split("\n").length}
|
|
103
|
+
|
|
104
|
+
`;
|
|
105
|
+
output += `## \u{1F50D} Structure Analysis
|
|
106
|
+
|
|
107
|
+
`;
|
|
108
|
+
if (imports.length > 0) {
|
|
109
|
+
output += `**Imports (${imports.length}):**
|
|
110
|
+
`;
|
|
111
|
+
for (const imp of imports.slice(0, 10)) {
|
|
112
|
+
output += `- ${imp}
|
|
113
|
+
`;
|
|
114
|
+
}
|
|
115
|
+
if (imports.length > 10) {
|
|
116
|
+
output += `- *...and ${imports.length - 10} more*
|
|
117
|
+
`;
|
|
118
|
+
}
|
|
119
|
+
output += "\n";
|
|
120
|
+
}
|
|
121
|
+
if (exports.length > 0) {
|
|
122
|
+
output += `**Exports (${exports.length}):**
|
|
123
|
+
`;
|
|
124
|
+
for (const exp of exports) {
|
|
125
|
+
output += `- ${exp}
|
|
126
|
+
`;
|
|
127
|
+
}
|
|
128
|
+
output += "\n";
|
|
129
|
+
}
|
|
130
|
+
if (functions.length > 0) {
|
|
131
|
+
output += `**Functions/Methods (${functions.length}):**
|
|
132
|
+
`;
|
|
133
|
+
for (const fn of functions.slice(0, 15)) {
|
|
134
|
+
output += `- \`${fn}\`
|
|
135
|
+
`;
|
|
136
|
+
}
|
|
137
|
+
if (functions.length > 15) {
|
|
138
|
+
output += `- *...and ${functions.length - 15} more*
|
|
139
|
+
`;
|
|
140
|
+
}
|
|
141
|
+
output += "\n";
|
|
142
|
+
}
|
|
143
|
+
output += `${"\u2500".repeat(60)}
|
|
144
|
+
`;
|
|
145
|
+
output += `## \u{1F9E0} Deep Explanation Request
|
|
146
|
+
|
|
147
|
+
`;
|
|
148
|
+
output += `**Role:** ${systemPrompt.split("\n")[0]}
|
|
149
|
+
|
|
150
|
+
`;
|
|
151
|
+
output += prompt;
|
|
152
|
+
output += `
|
|
153
|
+
${"\u2500".repeat(60)}
|
|
154
|
+
`;
|
|
155
|
+
if (context) {
|
|
156
|
+
output += `
|
|
157
|
+
**Additional Context:** ${context}
|
|
158
|
+
`;
|
|
159
|
+
}
|
|
160
|
+
return { content: [{ type: "text", text: output }] };
|
|
161
|
+
}
|
|
162
|
+
async explainIssue(target, _context) {
|
|
163
|
+
let file = "";
|
|
164
|
+
let line = 0;
|
|
165
|
+
let issue = target;
|
|
166
|
+
let severity = "unknown";
|
|
167
|
+
const match = target.match(/^(.+?):(\d+):(.+)$/);
|
|
168
|
+
if (match) {
|
|
169
|
+
file = match[1];
|
|
170
|
+
line = parseInt(match[2], 10);
|
|
171
|
+
issue = match[3].trim();
|
|
172
|
+
}
|
|
173
|
+
if (/critical|injection|rce|xss/i.test(issue)) severity = "critical";
|
|
174
|
+
else if (/serious|auth|password|secret/i.test(issue)) severity = "serious";
|
|
175
|
+
else if (/moderate|warning/i.test(issue)) severity = "moderate";
|
|
176
|
+
else severity = "low";
|
|
177
|
+
let codeContext = "";
|
|
178
|
+
if (file && existsSync(file)) {
|
|
179
|
+
const content = await readFile(file, "utf-8");
|
|
180
|
+
const lines = content.split("\n");
|
|
181
|
+
const start = Math.max(0, line - 5);
|
|
182
|
+
const end = Math.min(lines.length, line + 5);
|
|
183
|
+
codeContext = lines.slice(start, end).map((l, i) => {
|
|
184
|
+
const lineNum = start + i + 1;
|
|
185
|
+
const marker = lineNum === line ? "\u2192 " : " ";
|
|
186
|
+
return `${marker}${lineNum.toString().padStart(4)} | ${l}`;
|
|
187
|
+
}).join("\n");
|
|
188
|
+
}
|
|
189
|
+
const prompt = getPrompt("explain", "issue", {
|
|
190
|
+
issue,
|
|
191
|
+
severity,
|
|
192
|
+
filePath: file || "unknown",
|
|
193
|
+
line: String(line || "?")
|
|
194
|
+
});
|
|
195
|
+
let output = `
|
|
196
|
+
${"\u2501".repeat(60)}
|
|
197
|
+
`;
|
|
198
|
+
output += `\u{1F50D} ISSUE EXPLANATION
|
|
199
|
+
`;
|
|
200
|
+
output += `${"\u2501".repeat(60)}
|
|
201
|
+
|
|
202
|
+
`;
|
|
203
|
+
output += `## \u{1F4CD} Issue Details
|
|
204
|
+
|
|
205
|
+
`;
|
|
206
|
+
output += `- **Issue:** ${issue}
|
|
207
|
+
`;
|
|
208
|
+
output += `- **Severity:** ${this.getSeverityIcon(severity)} ${severity}
|
|
209
|
+
`;
|
|
210
|
+
if (file) output += `- **File:** \`${file}\`
|
|
211
|
+
`;
|
|
212
|
+
if (line) output += `- **Line:** ${line}
|
|
213
|
+
`;
|
|
214
|
+
output += "\n";
|
|
215
|
+
if (codeContext) {
|
|
216
|
+
output += `## \u{1F4C4} Code Context
|
|
217
|
+
|
|
218
|
+
`;
|
|
219
|
+
output += `\`\`\`
|
|
220
|
+
${codeContext}
|
|
221
|
+
\`\`\`
|
|
222
|
+
|
|
223
|
+
`;
|
|
224
|
+
}
|
|
225
|
+
output += `${"\u2500".repeat(60)}
|
|
226
|
+
`;
|
|
227
|
+
output += `## \u{1F9E0} Explanation Request
|
|
228
|
+
|
|
229
|
+
`;
|
|
230
|
+
output += prompt;
|
|
231
|
+
output += `
|
|
232
|
+
${"\u2500".repeat(60)}
|
|
233
|
+
`;
|
|
234
|
+
return { content: [{ type: "text", text: output }] };
|
|
235
|
+
}
|
|
236
|
+
async explainChange(target, context) {
|
|
237
|
+
const files = target.split(",").map((f) => f.trim());
|
|
238
|
+
let output = `
|
|
239
|
+
${"\u2501".repeat(60)}
|
|
240
|
+
`;
|
|
241
|
+
output += `\u{1F4DD} CHANGE ANALYSIS
|
|
242
|
+
`;
|
|
243
|
+
output += `${"\u2501".repeat(60)}
|
|
244
|
+
|
|
245
|
+
`;
|
|
246
|
+
output += `## \u{1F4C2} Changed Files
|
|
247
|
+
|
|
248
|
+
`;
|
|
249
|
+
for (const file of files) {
|
|
250
|
+
output += `- \`${file}\`
|
|
251
|
+
`;
|
|
252
|
+
}
|
|
253
|
+
output += "\n";
|
|
254
|
+
output += `## \u{1F9E0} Analysis Request
|
|
255
|
+
|
|
256
|
+
`;
|
|
257
|
+
output += `Analyze these changes and explain:
|
|
258
|
+
|
|
259
|
+
`;
|
|
260
|
+
output += `1. **What changed** - Summary of modifications
|
|
261
|
+
`;
|
|
262
|
+
output += `2. **Why it matters** - Impact on the system
|
|
263
|
+
`;
|
|
264
|
+
output += `3. **Dependencies** - What else might be affected
|
|
265
|
+
`;
|
|
266
|
+
output += `4. **Testing needed** - What to test after this change
|
|
267
|
+
`;
|
|
268
|
+
output += `5. **Rollback plan** - How to undo if needed
|
|
269
|
+
|
|
270
|
+
`;
|
|
271
|
+
if (context) {
|
|
272
|
+
output += `**Context:** ${context}
|
|
273
|
+
|
|
274
|
+
`;
|
|
275
|
+
}
|
|
276
|
+
output += `Please review the changed files and provide this analysis.
|
|
277
|
+
`;
|
|
278
|
+
return { content: [{ type: "text", text: output }] };
|
|
279
|
+
}
|
|
280
|
+
async explainRisk(target, context) {
|
|
281
|
+
const resolvedPath = isAbsolute(target) ? target : resolve(process.cwd(), target);
|
|
282
|
+
let output = `
|
|
283
|
+
${"\u2501".repeat(60)}
|
|
284
|
+
`;
|
|
285
|
+
output += `\u26A0\uFE0F RISK ASSESSMENT
|
|
286
|
+
`;
|
|
287
|
+
output += `${"\u2501".repeat(60)}
|
|
288
|
+
|
|
289
|
+
`;
|
|
290
|
+
if (existsSync(resolvedPath)) {
|
|
291
|
+
const code = await readFile(resolvedPath, "utf-8");
|
|
292
|
+
const filePath = relative(process.cwd(), resolvedPath);
|
|
293
|
+
const riskIndicators = this.detectRiskIndicators(code);
|
|
294
|
+
output += `## \u{1F4C2} Target
|
|
295
|
+
|
|
296
|
+
`;
|
|
297
|
+
output += `- **File:** \`${filePath}\`
|
|
298
|
+
`;
|
|
299
|
+
output += `- **Lines:** ${code.split("\n").length}
|
|
300
|
+
|
|
301
|
+
`;
|
|
302
|
+
if (riskIndicators.length > 0) {
|
|
303
|
+
output += `## \u{1F6A8} Risk Indicators Found
|
|
304
|
+
|
|
305
|
+
`;
|
|
306
|
+
for (const indicator of riskIndicators) {
|
|
307
|
+
output += `- ${indicator}
|
|
308
|
+
`;
|
|
309
|
+
}
|
|
310
|
+
output += "\n";
|
|
311
|
+
}
|
|
312
|
+
const prompt = getPrompt("explain", "risk", {
|
|
313
|
+
files: filePath,
|
|
314
|
+
summary: context || "Code change"
|
|
315
|
+
});
|
|
316
|
+
output += `${"\u2500".repeat(60)}
|
|
317
|
+
`;
|
|
318
|
+
output += `## \u{1F9E0} Risk Analysis Request
|
|
319
|
+
|
|
320
|
+
`;
|
|
321
|
+
output += prompt;
|
|
322
|
+
output += `
|
|
323
|
+
${"\u2500".repeat(60)}
|
|
324
|
+
`;
|
|
325
|
+
} else {
|
|
326
|
+
output += `## \u{1F4CB} Feature/Change
|
|
327
|
+
|
|
328
|
+
`;
|
|
329
|
+
output += `${target}
|
|
330
|
+
|
|
331
|
+
`;
|
|
332
|
+
output += `## \u{1F9E0} Risk Analysis Request
|
|
333
|
+
|
|
334
|
+
`;
|
|
335
|
+
output += `Analyze the risks of this change:
|
|
336
|
+
|
|
337
|
+
`;
|
|
338
|
+
output += `1. **Technical risks** - What could break?
|
|
339
|
+
`;
|
|
340
|
+
output += `2. **Security risks** - Any vulnerabilities introduced?
|
|
341
|
+
`;
|
|
342
|
+
output += `3. **Performance risks** - Any slowdowns?
|
|
343
|
+
`;
|
|
344
|
+
output += `4. **Data risks** - Any data integrity concerns?
|
|
345
|
+
`;
|
|
346
|
+
output += `5. **User impact** - How might users be affected?
|
|
347
|
+
`;
|
|
348
|
+
output += `6. **Mitigation** - How to reduce these risks?
|
|
349
|
+
`;
|
|
350
|
+
}
|
|
351
|
+
return { content: [{ type: "text", text: output }] };
|
|
352
|
+
}
|
|
353
|
+
detectRiskIndicators(code) {
|
|
354
|
+
const indicators = [];
|
|
355
|
+
const checks = [
|
|
356
|
+
{ pattern: /delete|drop|truncate/i, message: "\u26A0\uFE0F Destructive operations detected" },
|
|
357
|
+
{ pattern: /password|secret|key|token/i, message: "\u{1F510} Credential handling detected" },
|
|
358
|
+
{ pattern: /exec|eval|spawn/i, message: "\u{1F489} Code execution patterns detected" },
|
|
359
|
+
{ pattern: /SELECT.*FROM|INSERT|UPDATE|DELETE/i, message: "\u{1F5C4}\uFE0F Direct database operations" },
|
|
360
|
+
{ pattern: /fetch|axios|request|http/i, message: "\u{1F310} External API calls detected" },
|
|
361
|
+
{ pattern: /process\.env/i, message: "\u2699\uFE0F Environment variable usage" },
|
|
362
|
+
{ pattern: /fs\.|writeFile|readFile/i, message: "\u{1F4C1} File system operations" },
|
|
363
|
+
{ pattern: /setTimeout|setInterval/i, message: "\u23F0 Async timing operations" },
|
|
364
|
+
{ pattern: /try\s*{/i, message: "\u{1F6E1}\uFE0F Error handling present" },
|
|
365
|
+
{ pattern: /catch\s*\(/i, message: "\u{1F6E1}\uFE0F Exception handling present" }
|
|
366
|
+
];
|
|
367
|
+
for (const { pattern, message } of checks) {
|
|
368
|
+
if (pattern.test(code)) {
|
|
369
|
+
indicators.push(message);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
return indicators;
|
|
373
|
+
}
|
|
374
|
+
extractImports(code, _language) {
|
|
375
|
+
const imports = [];
|
|
376
|
+
const lines = code.split("\n");
|
|
377
|
+
for (const line of lines) {
|
|
378
|
+
const es6Match = line.match(/import\s+(?:{[^}]+}|\*\s+as\s+\w+|\w+)\s+from\s+['"]([^'"]+)['"]/);
|
|
379
|
+
if (es6Match) {
|
|
380
|
+
imports.push(es6Match[1]);
|
|
381
|
+
continue;
|
|
382
|
+
}
|
|
383
|
+
const cjsMatch = line.match(/require\s*\(['"]([^'"]+)['"]\)/);
|
|
384
|
+
if (cjsMatch) {
|
|
385
|
+
imports.push(cjsMatch[1]);
|
|
386
|
+
continue;
|
|
387
|
+
}
|
|
388
|
+
const pyMatch = line.match(/^(?:from\s+(\S+)\s+)?import\s+(\S+)/);
|
|
389
|
+
if (pyMatch && _language === "python") {
|
|
390
|
+
imports.push(pyMatch[1] || pyMatch[2]);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
return [...new Set(imports)];
|
|
394
|
+
}
|
|
395
|
+
extractExports(code) {
|
|
396
|
+
const exports = [];
|
|
397
|
+
const lines = code.split("\n");
|
|
398
|
+
for (const line of lines) {
|
|
399
|
+
const es6Match = line.match(/export\s+(?:default\s+)?(?:class|function|const|let|var|interface|type)\s+(\w+)/);
|
|
400
|
+
if (es6Match) {
|
|
401
|
+
exports.push(es6Match[1]);
|
|
402
|
+
}
|
|
403
|
+
const namedMatch = line.match(/export\s*\{([^}]+)\}/);
|
|
404
|
+
if (namedMatch) {
|
|
405
|
+
const names = namedMatch[1].split(",").map((n) => n.trim().split(/\s+as\s+/)[0].trim());
|
|
406
|
+
exports.push(...names);
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
return [...new Set(exports)];
|
|
410
|
+
}
|
|
411
|
+
extractFunctions(code, _language) {
|
|
412
|
+
const functions = [];
|
|
413
|
+
const lines = code.split("\n");
|
|
414
|
+
for (const line of lines) {
|
|
415
|
+
const funcMatch = line.match(/(?:async\s+)?function\s+(\w+)/);
|
|
416
|
+
if (funcMatch) {
|
|
417
|
+
functions.push(funcMatch[1]);
|
|
418
|
+
continue;
|
|
419
|
+
}
|
|
420
|
+
const arrowMatch = line.match(/(?:const|let|var)\s+(\w+)\s*=\s*(?:async\s*)?\(/);
|
|
421
|
+
if (arrowMatch) {
|
|
422
|
+
functions.push(arrowMatch[1]);
|
|
423
|
+
continue;
|
|
424
|
+
}
|
|
425
|
+
const methodMatch = line.match(/^\s+(?:async\s+)?(\w+)\s*\([^)]*\)\s*(?::\s*\w+)?\s*\{/);
|
|
426
|
+
if (methodMatch && !["if", "for", "while", "switch", "catch"].includes(methodMatch[1])) {
|
|
427
|
+
functions.push(methodMatch[1]);
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
return [...new Set(functions)];
|
|
431
|
+
}
|
|
432
|
+
detectLanguage(filePath) {
|
|
433
|
+
const ext = extname(filePath).toLowerCase();
|
|
434
|
+
const langMap = {
|
|
435
|
+
".ts": "typescript",
|
|
436
|
+
".tsx": "tsx",
|
|
437
|
+
".js": "javascript",
|
|
438
|
+
".jsx": "jsx",
|
|
439
|
+
".py": "python",
|
|
440
|
+
".go": "go",
|
|
441
|
+
".rs": "rust",
|
|
442
|
+
".java": "java",
|
|
443
|
+
".rb": "ruby",
|
|
444
|
+
".php": "php",
|
|
445
|
+
".vue": "vue",
|
|
446
|
+
".svelte": "svelte"
|
|
447
|
+
};
|
|
448
|
+
return langMap[ext] || "plaintext";
|
|
449
|
+
}
|
|
450
|
+
guessLanguage(code) {
|
|
451
|
+
if (/import.*from|export\s+(default|const|function|class)/.test(code)) return "typescript";
|
|
452
|
+
if (/def\s+\w+.*:/.test(code)) return "python";
|
|
453
|
+
if (/func\s+\w+.*\{/.test(code)) return "go";
|
|
454
|
+
if (/fn\s+\w+.*->/.test(code)) return "rust";
|
|
455
|
+
return "javascript";
|
|
456
|
+
}
|
|
457
|
+
getSeverityIcon(severity) {
|
|
458
|
+
const icons = {
|
|
459
|
+
critical: "\u{1F534}",
|
|
460
|
+
serious: "\u{1F7E0}",
|
|
461
|
+
moderate: "\u{1F7E1}",
|
|
462
|
+
low: "\u{1F535}",
|
|
463
|
+
unknown: "\u26AA"
|
|
464
|
+
};
|
|
465
|
+
return icons[severity] || "\u26AA";
|
|
466
|
+
}
|
|
467
|
+
getHelpText() {
|
|
468
|
+
return `
|
|
469
|
+
${"\u2501".repeat(60)}
|
|
470
|
+
\u{1F4D6} TRIE EXPLAIN - AI-POWERED CODE EXPLANATION
|
|
471
|
+
${"\u2501".repeat(60)}
|
|
472
|
+
|
|
473
|
+
## Usage
|
|
474
|
+
|
|
475
|
+
### Explain code:
|
|
476
|
+
\`\`\`
|
|
477
|
+
trie_explain type:"code" target:"src/app.ts"
|
|
478
|
+
\`\`\`
|
|
479
|
+
|
|
480
|
+
### Explain an issue:
|
|
481
|
+
\`\`\`
|
|
482
|
+
trie_explain type:"issue" target:"SQL injection vulnerability"
|
|
483
|
+
\`\`\`
|
|
484
|
+
or with file context:
|
|
485
|
+
\`\`\`
|
|
486
|
+
trie_explain type:"issue" target:"src/db.ts:42:Unvalidated input"
|
|
487
|
+
\`\`\`
|
|
488
|
+
|
|
489
|
+
### Explain changes:
|
|
490
|
+
\`\`\`
|
|
491
|
+
trie_explain type:"change" target:"src/auth.ts, src/user.ts"
|
|
492
|
+
\`\`\`
|
|
493
|
+
|
|
494
|
+
### Assess risk:
|
|
495
|
+
\`\`\`
|
|
496
|
+
trie_explain type:"risk" target:"src/payment.ts"
|
|
497
|
+
\`\`\`
|
|
498
|
+
or for a feature:
|
|
499
|
+
\`\`\`
|
|
500
|
+
trie_explain type:"risk" target:"Adding Stripe integration for payments"
|
|
501
|
+
\`\`\`
|
|
502
|
+
|
|
503
|
+
## Explanation Types
|
|
504
|
+
|
|
505
|
+
| Type | Description |
|
|
506
|
+
|------|-------------|
|
|
507
|
+
| code | What does this code do? |
|
|
508
|
+
| issue | Why is this a problem? |
|
|
509
|
+
| change | What's the impact? |
|
|
510
|
+
| risk | What could go wrong? |
|
|
511
|
+
`;
|
|
512
|
+
}
|
|
513
|
+
};
|
|
514
|
+
|
|
515
|
+
// src/tools/test.ts
|
|
516
|
+
import { readFile as readFile2 } from "fs/promises";
|
|
517
|
+
import { existsSync as existsSync2 } from "fs";
|
|
518
|
+
import { extname as extname2, relative as relative2, resolve as resolve2, isAbsolute as isAbsolute2, dirname, basename, join } from "path";
|
|
519
|
+
var TrieTestTool = class {
|
|
520
|
+
async execute(args) {
|
|
521
|
+
const { action, files, framework, style = "unit" } = args || {};
|
|
522
|
+
if (!action || !files || files.length === 0) {
|
|
523
|
+
return {
|
|
524
|
+
content: [{
|
|
525
|
+
type: "text",
|
|
526
|
+
text: this.getHelpText()
|
|
527
|
+
}]
|
|
528
|
+
};
|
|
529
|
+
}
|
|
530
|
+
switch (action) {
|
|
531
|
+
case "generate":
|
|
532
|
+
return this.generateTests(files, framework, style);
|
|
533
|
+
case "coverage":
|
|
534
|
+
return this.analyzeCoverage(files);
|
|
535
|
+
case "suggest":
|
|
536
|
+
return this.suggestTests(files);
|
|
537
|
+
case "run":
|
|
538
|
+
return this.runTestsInfo(files);
|
|
539
|
+
default:
|
|
540
|
+
return {
|
|
541
|
+
content: [{
|
|
542
|
+
type: "text",
|
|
543
|
+
text: `Unknown action: ${action}`
|
|
544
|
+
}]
|
|
545
|
+
};
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
async generateTests(files, framework, style) {
|
|
549
|
+
const detectedFramework = framework || await this.detectTestFramework();
|
|
550
|
+
let output = `
|
|
551
|
+
${"\u2501".repeat(60)}
|
|
552
|
+
`;
|
|
553
|
+
output += `\u{1F9EA} TEST GENERATION
|
|
554
|
+
`;
|
|
555
|
+
output += `${"\u2501".repeat(60)}
|
|
556
|
+
|
|
557
|
+
`;
|
|
558
|
+
output += `## \u2699\uFE0F Configuration
|
|
559
|
+
|
|
560
|
+
`;
|
|
561
|
+
output += `- **Framework:** ${detectedFramework}
|
|
562
|
+
`;
|
|
563
|
+
output += `- **Style:** ${style}
|
|
564
|
+
`;
|
|
565
|
+
output += `- **Files:** ${files.length}
|
|
566
|
+
|
|
567
|
+
`;
|
|
568
|
+
const allUnits = [];
|
|
569
|
+
for (const file of files) {
|
|
570
|
+
const resolvedPath = isAbsolute2(file) ? file : resolve2(process.cwd(), file);
|
|
571
|
+
if (!existsSync2(resolvedPath)) {
|
|
572
|
+
output += `\u26A0\uFE0F File not found: ${file}
|
|
573
|
+
`;
|
|
574
|
+
continue;
|
|
575
|
+
}
|
|
576
|
+
const code = await readFile2(resolvedPath, "utf-8");
|
|
577
|
+
const language = this.detectLanguage(resolvedPath);
|
|
578
|
+
const relativePath = relative2(process.cwd(), resolvedPath);
|
|
579
|
+
const units = this.extractTestableUnits(code, language);
|
|
580
|
+
allUnits.push({ file: relativePath, units });
|
|
581
|
+
output += `### \u{1F4C4} ${relativePath}
|
|
582
|
+
|
|
583
|
+
`;
|
|
584
|
+
output += `Found **${units.length}** testable units:
|
|
585
|
+
|
|
586
|
+
`;
|
|
587
|
+
for (const unit of units) {
|
|
588
|
+
const complexityIcon = unit.complexity > 10 ? "\u{1F534}" : unit.complexity > 5 ? "\u{1F7E1}" : "\u{1F7E2}";
|
|
589
|
+
output += `- ${complexityIcon} \`${unit.name}\` (${unit.type}, complexity: ${unit.complexity})
|
|
590
|
+
`;
|
|
591
|
+
if (unit.dependencies.length > 0) {
|
|
592
|
+
output += ` - Dependencies: ${unit.dependencies.join(", ")}
|
|
593
|
+
`;
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
output += "\n";
|
|
597
|
+
}
|
|
598
|
+
output += `${"\u2500".repeat(60)}
|
|
599
|
+
`;
|
|
600
|
+
output += `## \u{1F9E0} Test Generation Request
|
|
601
|
+
|
|
602
|
+
`;
|
|
603
|
+
const systemPrompt = getSystemPrompt("test");
|
|
604
|
+
output += `**Role:** ${systemPrompt.split("\n")[0]}
|
|
605
|
+
|
|
606
|
+
`;
|
|
607
|
+
for (const { file, units } of allUnits) {
|
|
608
|
+
if (units.length === 0) continue;
|
|
609
|
+
const code = await readFile2(resolve2(process.cwd(), file), "utf-8");
|
|
610
|
+
const language = this.detectLanguage(file);
|
|
611
|
+
const prompt = getPrompt("test", "generate", {
|
|
612
|
+
code,
|
|
613
|
+
language,
|
|
614
|
+
filePath: file,
|
|
615
|
+
framework: detectedFramework
|
|
616
|
+
});
|
|
617
|
+
output += `### Tests for ${file}
|
|
618
|
+
|
|
619
|
+
`;
|
|
620
|
+
output += prompt;
|
|
621
|
+
output += "\n\n";
|
|
622
|
+
}
|
|
623
|
+
output += `${"\u2500".repeat(60)}
|
|
624
|
+
`;
|
|
625
|
+
output += `
|
|
626
|
+
## \u{1F4DD} Expected Test Output
|
|
627
|
+
|
|
628
|
+
`;
|
|
629
|
+
output += `Generate complete test files with:
|
|
630
|
+
`;
|
|
631
|
+
output += `- All imports and setup
|
|
632
|
+
`;
|
|
633
|
+
output += `- Tests for each function/method
|
|
634
|
+
`;
|
|
635
|
+
output += `- Edge cases and error scenarios
|
|
636
|
+
`;
|
|
637
|
+
output += `- Mock requirements clearly stated
|
|
638
|
+
`;
|
|
639
|
+
output += `- Ready to copy and run
|
|
640
|
+
`;
|
|
641
|
+
return { content: [{ type: "text", text: output }] };
|
|
642
|
+
}
|
|
643
|
+
async analyzeCoverage(files) {
|
|
644
|
+
let output = `
|
|
645
|
+
${"\u2501".repeat(60)}
|
|
646
|
+
`;
|
|
647
|
+
output += `\u{1F4CA} COVERAGE ANALYSIS
|
|
648
|
+
`;
|
|
649
|
+
output += `${"\u2501".repeat(60)}
|
|
650
|
+
|
|
651
|
+
`;
|
|
652
|
+
for (const file of files) {
|
|
653
|
+
const resolvedPath = isAbsolute2(file) ? file : resolve2(process.cwd(), file);
|
|
654
|
+
if (!existsSync2(resolvedPath)) {
|
|
655
|
+
output += `\u26A0\uFE0F File not found: ${file}
|
|
656
|
+
`;
|
|
657
|
+
continue;
|
|
658
|
+
}
|
|
659
|
+
const code = await readFile2(resolvedPath, "utf-8");
|
|
660
|
+
const language = this.detectLanguage(resolvedPath);
|
|
661
|
+
const relativePath = relative2(process.cwd(), resolvedPath);
|
|
662
|
+
const units = this.extractTestableUnits(code, language);
|
|
663
|
+
const testFile = await this.findTestFile(resolvedPath);
|
|
664
|
+
let testCode = "";
|
|
665
|
+
let testedUnits = [];
|
|
666
|
+
if (testFile) {
|
|
667
|
+
testCode = await readFile2(testFile, "utf-8");
|
|
668
|
+
testedUnits = this.findTestedUnits(testCode, units.map((u) => u.name));
|
|
669
|
+
}
|
|
670
|
+
const coverage = units.length > 0 ? Math.round(testedUnits.length / units.length * 100) : 0;
|
|
671
|
+
const coverageIcon = coverage >= 80 ? "\u{1F7E2}" : coverage >= 50 ? "\u{1F7E1}" : "\u{1F534}";
|
|
672
|
+
output += `### \u{1F4C4} ${relativePath}
|
|
673
|
+
|
|
674
|
+
`;
|
|
675
|
+
output += `**Coverage:** ${coverageIcon} ${coverage}% (${testedUnits.length}/${units.length} units)
|
|
676
|
+
`;
|
|
677
|
+
if (testFile) {
|
|
678
|
+
output += `**Test file:** \`${relative2(process.cwd(), testFile)}\`
|
|
679
|
+
`;
|
|
680
|
+
} else {
|
|
681
|
+
output += `**Test file:** \u274C Not found
|
|
682
|
+
`;
|
|
683
|
+
}
|
|
684
|
+
output += "\n";
|
|
685
|
+
const untested = units.filter((u) => !testedUnits.includes(u.name));
|
|
686
|
+
if (untested.length > 0) {
|
|
687
|
+
output += `**Missing Tests:**
|
|
688
|
+
`;
|
|
689
|
+
for (const unit of untested) {
|
|
690
|
+
const priority = unit.complexity > 5 ? "\u{1F534} HIGH" : "\u{1F7E1} MEDIUM";
|
|
691
|
+
output += `- ${priority}: \`${unit.name}\` (${unit.type})
|
|
692
|
+
`;
|
|
693
|
+
}
|
|
694
|
+
output += "\n";
|
|
695
|
+
}
|
|
696
|
+
if (testedUnits.length > 0) {
|
|
697
|
+
output += `**Tested:**
|
|
698
|
+
`;
|
|
699
|
+
for (const name of testedUnits) {
|
|
700
|
+
output += `- \u2705 \`${name}\`
|
|
701
|
+
`;
|
|
702
|
+
}
|
|
703
|
+
output += "\n";
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
output += `${"\u2500".repeat(60)}
|
|
707
|
+
`;
|
|
708
|
+
output += `## \u{1F4CB} Recommendations
|
|
709
|
+
|
|
710
|
+
`;
|
|
711
|
+
output += `To improve coverage:
|
|
712
|
+
`;
|
|
713
|
+
output += `1. Focus on high-complexity untested functions first
|
|
714
|
+
`;
|
|
715
|
+
output += `2. Add edge case tests for existing coverage
|
|
716
|
+
`;
|
|
717
|
+
output += `3. Consider integration tests for complex interactions
|
|
718
|
+
`;
|
|
719
|
+
output += `
|
|
720
|
+
Run \`trie_test action:generate files:["file.ts"]\` to generate missing tests.
|
|
721
|
+
`;
|
|
722
|
+
return { content: [{ type: "text", text: output }] };
|
|
723
|
+
}
|
|
724
|
+
async suggestTests(files) {
|
|
725
|
+
let output = `
|
|
726
|
+
${"\u2501".repeat(60)}
|
|
727
|
+
`;
|
|
728
|
+
output += `\u{1F4A1} TEST SUGGESTIONS
|
|
729
|
+
`;
|
|
730
|
+
output += `${"\u2501".repeat(60)}
|
|
731
|
+
|
|
732
|
+
`;
|
|
733
|
+
for (const file of files) {
|
|
734
|
+
const resolvedPath = isAbsolute2(file) ? file : resolve2(process.cwd(), file);
|
|
735
|
+
if (!existsSync2(resolvedPath)) {
|
|
736
|
+
output += `\u26A0\uFE0F File not found: ${file}
|
|
737
|
+
`;
|
|
738
|
+
continue;
|
|
739
|
+
}
|
|
740
|
+
const code = await readFile2(resolvedPath, "utf-8");
|
|
741
|
+
const relativePath = relative2(process.cwd(), resolvedPath);
|
|
742
|
+
const patterns = this.detectTestablePatterns(code);
|
|
743
|
+
output += `### \u{1F4C4} ${relativePath}
|
|
744
|
+
|
|
745
|
+
`;
|
|
746
|
+
if (patterns.length === 0) {
|
|
747
|
+
output += `No specific test suggestions for this file.
|
|
748
|
+
|
|
749
|
+
`;
|
|
750
|
+
continue;
|
|
751
|
+
}
|
|
752
|
+
output += `| Priority | Pattern | Suggested Test |
|
|
753
|
+
`;
|
|
754
|
+
output += `|----------|---------|----------------|
|
|
755
|
+
`;
|
|
756
|
+
for (const pattern of patterns) {
|
|
757
|
+
output += `| ${pattern.priority} | ${pattern.name} | ${pattern.suggestion} |
|
|
758
|
+
`;
|
|
759
|
+
}
|
|
760
|
+
output += "\n";
|
|
761
|
+
}
|
|
762
|
+
return { content: [{ type: "text", text: output }] };
|
|
763
|
+
}
|
|
764
|
+
async runTestsInfo(files) {
|
|
765
|
+
const framework = await this.detectTestFramework();
|
|
766
|
+
let output = `
|
|
767
|
+
${"\u2501".repeat(60)}
|
|
768
|
+
`;
|
|
769
|
+
output += `\u{1F3C3} RUN TESTS
|
|
770
|
+
`;
|
|
771
|
+
output += `${"\u2501".repeat(60)}
|
|
772
|
+
|
|
773
|
+
`;
|
|
774
|
+
output += `## Detected Configuration
|
|
775
|
+
|
|
776
|
+
`;
|
|
777
|
+
output += `- **Framework:** ${framework}
|
|
778
|
+
`;
|
|
779
|
+
output += `- **Files:** ${files.join(", ")}
|
|
780
|
+
|
|
781
|
+
`;
|
|
782
|
+
output += `## Commands
|
|
783
|
+
|
|
784
|
+
`;
|
|
785
|
+
switch (framework) {
|
|
786
|
+
case "jest":
|
|
787
|
+
output += `\`\`\`bash
|
|
788
|
+
`;
|
|
789
|
+
output += `# Run all tests
|
|
790
|
+
`;
|
|
791
|
+
output += `npx jest
|
|
792
|
+
|
|
793
|
+
`;
|
|
794
|
+
output += `# Run specific files
|
|
795
|
+
`;
|
|
796
|
+
output += `npx jest ${files.join(" ")}
|
|
797
|
+
|
|
798
|
+
`;
|
|
799
|
+
output += `# Run with coverage
|
|
800
|
+
`;
|
|
801
|
+
output += `npx jest --coverage
|
|
802
|
+
`;
|
|
803
|
+
output += `\`\`\`
|
|
804
|
+
`;
|
|
805
|
+
break;
|
|
806
|
+
case "vitest":
|
|
807
|
+
output += `\`\`\`bash
|
|
808
|
+
`;
|
|
809
|
+
output += `# Run all tests
|
|
810
|
+
`;
|
|
811
|
+
output += `npx vitest run
|
|
812
|
+
|
|
813
|
+
`;
|
|
814
|
+
output += `# Run specific files
|
|
815
|
+
`;
|
|
816
|
+
output += `npx vitest run ${files.join(" ")}
|
|
817
|
+
|
|
818
|
+
`;
|
|
819
|
+
output += `# Run with coverage
|
|
820
|
+
`;
|
|
821
|
+
output += `npx vitest run --coverage
|
|
822
|
+
`;
|
|
823
|
+
output += `\`\`\`
|
|
824
|
+
`;
|
|
825
|
+
break;
|
|
826
|
+
case "pytest":
|
|
827
|
+
output += `\`\`\`bash
|
|
828
|
+
`;
|
|
829
|
+
output += `# Run all tests
|
|
830
|
+
`;
|
|
831
|
+
output += `pytest
|
|
832
|
+
|
|
833
|
+
`;
|
|
834
|
+
output += `# Run specific files
|
|
835
|
+
`;
|
|
836
|
+
output += `pytest ${files.join(" ")}
|
|
837
|
+
|
|
838
|
+
`;
|
|
839
|
+
output += `# Run with coverage
|
|
840
|
+
`;
|
|
841
|
+
output += `pytest --cov
|
|
842
|
+
`;
|
|
843
|
+
output += `\`\`\`
|
|
844
|
+
`;
|
|
845
|
+
break;
|
|
846
|
+
default:
|
|
847
|
+
output += `Run your test framework with the specified files.
|
|
848
|
+
`;
|
|
849
|
+
}
|
|
850
|
+
output += `
|
|
851
|
+
*Note: This tool provides test commands but doesn't execute them directly.*
|
|
852
|
+
`;
|
|
853
|
+
return { content: [{ type: "text", text: output }] };
|
|
854
|
+
}
|
|
855
|
+
extractTestableUnits(code, language) {
|
|
856
|
+
const units = [];
|
|
857
|
+
const lines = code.split("\n");
|
|
858
|
+
for (let i = 0; i < lines.length; i++) {
|
|
859
|
+
const line = lines[i] || "";
|
|
860
|
+
const funcMatch = line.match(/(?:export\s+)?(?:async\s+)?function\s+(\w+)\s*\(([^)]*)\)/);
|
|
861
|
+
if (funcMatch) {
|
|
862
|
+
const endLine = this.findBlockEnd(lines, i);
|
|
863
|
+
const body = lines.slice(i, endLine + 1).join("\n");
|
|
864
|
+
units.push({
|
|
865
|
+
name: funcMatch[1],
|
|
866
|
+
type: "function",
|
|
867
|
+
startLine: i + 1,
|
|
868
|
+
endLine: endLine + 1,
|
|
869
|
+
signature: `${funcMatch[1]}(${funcMatch[2]})`,
|
|
870
|
+
complexity: this.calculateComplexity(body),
|
|
871
|
+
dependencies: this.extractDependencies(body)
|
|
872
|
+
});
|
|
873
|
+
}
|
|
874
|
+
const arrowMatch = line.match(/(?:export\s+)?(?:const|let)\s+(\w+)\s*=\s*(?:async\s*)?\([^)]*\)\s*(?:=>|:)/);
|
|
875
|
+
if (arrowMatch) {
|
|
876
|
+
const endLine = this.findBlockEnd(lines, i);
|
|
877
|
+
const body = lines.slice(i, endLine + 1).join("\n");
|
|
878
|
+
units.push({
|
|
879
|
+
name: arrowMatch[1],
|
|
880
|
+
type: "function",
|
|
881
|
+
startLine: i + 1,
|
|
882
|
+
endLine: endLine + 1,
|
|
883
|
+
signature: arrowMatch[1],
|
|
884
|
+
complexity: this.calculateComplexity(body),
|
|
885
|
+
dependencies: this.extractDependencies(body)
|
|
886
|
+
});
|
|
887
|
+
}
|
|
888
|
+
const classMatch = line.match(/(?:export\s+)?class\s+(\w+)/);
|
|
889
|
+
if (classMatch) {
|
|
890
|
+
const endLine = this.findBlockEnd(lines, i);
|
|
891
|
+
units.push({
|
|
892
|
+
name: classMatch[1],
|
|
893
|
+
type: "class",
|
|
894
|
+
startLine: i + 1,
|
|
895
|
+
endLine: endLine + 1,
|
|
896
|
+
signature: `class ${classMatch[1]}`,
|
|
897
|
+
complexity: this.calculateComplexity(lines.slice(i, endLine + 1).join("\n")),
|
|
898
|
+
dependencies: []
|
|
899
|
+
});
|
|
900
|
+
}
|
|
901
|
+
const componentMatch = line.match(/(?:export\s+)?(?:const|function)\s+([A-Z]\w+)\s*[=:]/);
|
|
902
|
+
if (componentMatch && /jsx|tsx/.test(language)) {
|
|
903
|
+
const endLine = this.findBlockEnd(lines, i);
|
|
904
|
+
units.push({
|
|
905
|
+
name: componentMatch[1],
|
|
906
|
+
type: "component",
|
|
907
|
+
startLine: i + 1,
|
|
908
|
+
endLine: endLine + 1,
|
|
909
|
+
signature: componentMatch[1],
|
|
910
|
+
complexity: this.calculateComplexity(lines.slice(i, endLine + 1).join("\n")),
|
|
911
|
+
dependencies: this.extractDependencies(lines.slice(i, endLine + 1).join("\n"))
|
|
912
|
+
});
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
return units;
|
|
916
|
+
}
|
|
917
|
+
findBlockEnd(lines, startLine) {
|
|
918
|
+
let braceCount = 0;
|
|
919
|
+
let started = false;
|
|
920
|
+
for (let i = startLine; i < lines.length; i++) {
|
|
921
|
+
const line = lines[i] || "";
|
|
922
|
+
for (const char of line) {
|
|
923
|
+
if (char === "{") {
|
|
924
|
+
braceCount++;
|
|
925
|
+
started = true;
|
|
926
|
+
} else if (char === "}") {
|
|
927
|
+
braceCount--;
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
if (started && braceCount === 0) {
|
|
931
|
+
return i;
|
|
932
|
+
}
|
|
933
|
+
}
|
|
934
|
+
return lines.length - 1;
|
|
935
|
+
}
|
|
936
|
+
calculateComplexity(code) {
|
|
937
|
+
let complexity = 1;
|
|
938
|
+
const patterns = [
|
|
939
|
+
/if\s*\(/g,
|
|
940
|
+
/else\s+if/g,
|
|
941
|
+
/\?\s*[^:]/g,
|
|
942
|
+
// Conditionals
|
|
943
|
+
/for\s*\(/g,
|
|
944
|
+
/while\s*\(/g,
|
|
945
|
+
/do\s*\{/g,
|
|
946
|
+
// Loops
|
|
947
|
+
/catch\s*\(/g,
|
|
948
|
+
// Error handling
|
|
949
|
+
/&&/g,
|
|
950
|
+
/\|\|/g,
|
|
951
|
+
// Logical operators
|
|
952
|
+
/case\s+/g
|
|
953
|
+
// Switch cases
|
|
954
|
+
];
|
|
955
|
+
for (const pattern of patterns) {
|
|
956
|
+
const matches = code.match(pattern);
|
|
957
|
+
if (matches) {
|
|
958
|
+
complexity += matches.length;
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
return complexity;
|
|
962
|
+
}
|
|
963
|
+
extractDependencies(code) {
|
|
964
|
+
const deps = [];
|
|
965
|
+
const calls = code.match(/\b([a-z]\w+)\s*\(/gi) || [];
|
|
966
|
+
const builtins = ["if", "for", "while", "switch", "catch", "function", "return", "throw", "new", "await", "async"];
|
|
967
|
+
for (const call of calls) {
|
|
968
|
+
const name = call.replace(/\s*\($/, "");
|
|
969
|
+
if (!builtins.includes(name.toLowerCase()) && !deps.includes(name)) {
|
|
970
|
+
deps.push(name);
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
return deps.slice(0, 10);
|
|
974
|
+
}
|
|
975
|
+
async findTestFile(sourcePath) {
|
|
976
|
+
const dir = dirname(sourcePath);
|
|
977
|
+
const base = basename(sourcePath, extname2(sourcePath));
|
|
978
|
+
const ext = extname2(sourcePath);
|
|
979
|
+
const patterns = [
|
|
980
|
+
`${base}.test${ext}`,
|
|
981
|
+
`${base}.spec${ext}`,
|
|
982
|
+
`${base}_test${ext}`,
|
|
983
|
+
`test_${base}${ext}`
|
|
984
|
+
];
|
|
985
|
+
for (const pattern of patterns) {
|
|
986
|
+
const testPath = join(dir, pattern);
|
|
987
|
+
if (existsSync2(testPath)) {
|
|
988
|
+
return testPath;
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
const testsDir = join(dir, "__tests__");
|
|
992
|
+
if (existsSync2(testsDir)) {
|
|
993
|
+
for (const pattern of patterns) {
|
|
994
|
+
const testPath = join(testsDir, pattern);
|
|
995
|
+
if (existsSync2(testPath)) {
|
|
996
|
+
return testPath;
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
return null;
|
|
1001
|
+
}
|
|
1002
|
+
findTestedUnits(testCode, unitNames) {
|
|
1003
|
+
const tested = [];
|
|
1004
|
+
for (const name of unitNames) {
|
|
1005
|
+
const patterns = [
|
|
1006
|
+
new RegExp(`describe\\s*\\([^)]*${name}`, "i"),
|
|
1007
|
+
new RegExp(`it\\s*\\([^)]*${name}`, "i"),
|
|
1008
|
+
new RegExp(`test\\s*\\([^)]*${name}`, "i"),
|
|
1009
|
+
new RegExp(`expect\\s*\\([^)]*${name}`, "i")
|
|
1010
|
+
];
|
|
1011
|
+
for (const pattern of patterns) {
|
|
1012
|
+
if (pattern.test(testCode)) {
|
|
1013
|
+
tested.push(name);
|
|
1014
|
+
break;
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
return tested;
|
|
1019
|
+
}
|
|
1020
|
+
detectTestablePatterns(code) {
|
|
1021
|
+
const patterns = [];
|
|
1022
|
+
const checks = [
|
|
1023
|
+
{ pattern: /async\s+function|await\s+/i, priority: "\u{1F534} HIGH", name: "Async code", suggestion: "Test async flows and error handling" },
|
|
1024
|
+
{ pattern: /try\s*{[^}]*catch/i, priority: "\u{1F534} HIGH", name: "Error handling", suggestion: "Test both success and error paths" },
|
|
1025
|
+
{ pattern: /if\s*\([^)]*&&[^)]*\)/i, priority: "\u{1F7E1} MED", name: "Complex conditionals", suggestion: "Test all condition branches" },
|
|
1026
|
+
{ pattern: /\.map\(|\.filter\(|\.reduce\(/i, priority: "\u{1F7E1} MED", name: "Array operations", suggestion: "Test with empty, single, multiple items" },
|
|
1027
|
+
{ pattern: /fetch\(|axios\.|request\(/i, priority: "\u{1F534} HIGH", name: "HTTP requests", suggestion: "Mock API calls, test error responses" },
|
|
1028
|
+
{ pattern: /localStorage|sessionStorage/i, priority: "\u{1F7E1} MED", name: "Storage usage", suggestion: "Mock storage, test read/write" },
|
|
1029
|
+
{ pattern: /setTimeout|setInterval/i, priority: "\u{1F7E1} MED", name: "Timers", suggestion: "Use fake timers in tests" },
|
|
1030
|
+
{ pattern: /useState|useEffect|useCallback/i, priority: "\u{1F534} HIGH", name: "React hooks", suggestion: "Test hook behavior and updates" },
|
|
1031
|
+
{ pattern: /form|input|submit/i, priority: "\u{1F7E1} MED", name: "Form handling", suggestion: "Test validation and submission" }
|
|
1032
|
+
];
|
|
1033
|
+
for (const { pattern, priority, name, suggestion } of checks) {
|
|
1034
|
+
if (pattern.test(code)) {
|
|
1035
|
+
patterns.push({ priority, name, suggestion });
|
|
1036
|
+
}
|
|
1037
|
+
}
|
|
1038
|
+
return patterns;
|
|
1039
|
+
}
|
|
1040
|
+
async detectTestFramework() {
|
|
1041
|
+
const packagePath = resolve2(process.cwd(), "package.json");
|
|
1042
|
+
if (existsSync2(packagePath)) {
|
|
1043
|
+
try {
|
|
1044
|
+
const pkg = JSON.parse(await readFile2(packagePath, "utf-8"));
|
|
1045
|
+
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
1046
|
+
if (deps.vitest) return "vitest";
|
|
1047
|
+
if (deps.jest) return "jest";
|
|
1048
|
+
if (deps.mocha) return "mocha";
|
|
1049
|
+
} catch {
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
1052
|
+
if (existsSync2(resolve2(process.cwd(), "pytest.ini")) || existsSync2(resolve2(process.cwd(), "pyproject.toml"))) {
|
|
1053
|
+
return "pytest";
|
|
1054
|
+
}
|
|
1055
|
+
return "jest";
|
|
1056
|
+
}
|
|
1057
|
+
detectLanguage(filePath) {
|
|
1058
|
+
const ext = extname2(filePath).toLowerCase();
|
|
1059
|
+
const langMap = {
|
|
1060
|
+
".ts": "typescript",
|
|
1061
|
+
".tsx": "tsx",
|
|
1062
|
+
".js": "javascript",
|
|
1063
|
+
".jsx": "jsx",
|
|
1064
|
+
".py": "python",
|
|
1065
|
+
".go": "go",
|
|
1066
|
+
".rs": "rust"
|
|
1067
|
+
};
|
|
1068
|
+
return langMap[ext] || "javascript";
|
|
1069
|
+
}
|
|
1070
|
+
getHelpText() {
|
|
1071
|
+
return `
|
|
1072
|
+
${"\u2501".repeat(60)}
|
|
1073
|
+
\u{1F9EA} TRIE TEST - AI-POWERED TEST GENERATION
|
|
1074
|
+
${"\u2501".repeat(60)}
|
|
1075
|
+
|
|
1076
|
+
## Usage
|
|
1077
|
+
|
|
1078
|
+
### Generate tests:
|
|
1079
|
+
\`\`\`
|
|
1080
|
+
trie_test action:"generate" files:["src/utils.ts"]
|
|
1081
|
+
\`\`\`
|
|
1082
|
+
|
|
1083
|
+
### Analyze coverage:
|
|
1084
|
+
\`\`\`
|
|
1085
|
+
trie_test action:"coverage" files:["src/utils.ts"]
|
|
1086
|
+
\`\`\`
|
|
1087
|
+
|
|
1088
|
+
### Get test suggestions:
|
|
1089
|
+
\`\`\`
|
|
1090
|
+
trie_test action:"suggest" files:["src/app.ts"]
|
|
1091
|
+
\`\`\`
|
|
1092
|
+
|
|
1093
|
+
### Get run commands:
|
|
1094
|
+
\`\`\`
|
|
1095
|
+
trie_test action:"run" files:["src/utils.test.ts"]
|
|
1096
|
+
\`\`\`
|
|
1097
|
+
|
|
1098
|
+
## Options
|
|
1099
|
+
|
|
1100
|
+
| Option | Values | Description |
|
|
1101
|
+
|--------|--------|-------------|
|
|
1102
|
+
| action | generate, coverage, suggest, run | What to do |
|
|
1103
|
+
| files | Array of paths | Files to analyze |
|
|
1104
|
+
| framework | jest, vitest, mocha, pytest | Test framework |
|
|
1105
|
+
| style | unit, integration, e2e, all | Test style |
|
|
1106
|
+
`;
|
|
1107
|
+
}
|
|
1108
|
+
};
|
|
1109
|
+
|
|
1110
|
+
// src/tools/register-agent.ts
|
|
1111
|
+
var TrieRegisterAgentTool = class {
|
|
1112
|
+
async execute(args) {
|
|
1113
|
+
const { name, path } = args;
|
|
1114
|
+
return {
|
|
1115
|
+
content: [
|
|
1116
|
+
{
|
|
1117
|
+
type: "text",
|
|
1118
|
+
text: `\u{1F916} Register agent tool called with name: ${name}, path: ${path}
|
|
1119
|
+
|
|
1120
|
+
TODO: Implement register agent functionality`
|
|
1121
|
+
}
|
|
1122
|
+
]
|
|
1123
|
+
};
|
|
1124
|
+
}
|
|
1125
|
+
};
|
|
1126
|
+
|
|
1127
|
+
// src/tools/watch.ts
|
|
1128
|
+
import { watch } from "fs";
|
|
1129
|
+
import { stat } from "fs/promises";
|
|
1130
|
+
import { join as join2, extname as extname3, basename as basename2 } from "path";
|
|
1131
|
+
import { existsSync as existsSync3 } from "fs";
|
|
1132
|
+
var WATCH_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
1133
|
+
".ts",
|
|
1134
|
+
".tsx",
|
|
1135
|
+
".js",
|
|
1136
|
+
".jsx",
|
|
1137
|
+
".mjs",
|
|
1138
|
+
".vue",
|
|
1139
|
+
".svelte",
|
|
1140
|
+
".astro",
|
|
1141
|
+
".py",
|
|
1142
|
+
".go",
|
|
1143
|
+
".rs"
|
|
1144
|
+
]);
|
|
1145
|
+
var SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
1146
|
+
"node_modules",
|
|
1147
|
+
".git",
|
|
1148
|
+
"dist",
|
|
1149
|
+
"build",
|
|
1150
|
+
".next",
|
|
1151
|
+
".nuxt",
|
|
1152
|
+
"coverage",
|
|
1153
|
+
".turbo",
|
|
1154
|
+
".cache"
|
|
1155
|
+
]);
|
|
1156
|
+
var TrieWatchTool = class {
|
|
1157
|
+
scanTool = new TrieScanTool();
|
|
1158
|
+
fixTool = new TrieFixTool();
|
|
1159
|
+
state = {
|
|
1160
|
+
isRunning: false,
|
|
1161
|
+
yoloMode: false,
|
|
1162
|
+
lastScan: /* @__PURE__ */ new Map(),
|
|
1163
|
+
pendingFiles: /* @__PURE__ */ new Set(),
|
|
1164
|
+
scanDebounceTimer: null,
|
|
1165
|
+
issueCache: /* @__PURE__ */ new Map(),
|
|
1166
|
+
totalIssuesFound: 0,
|
|
1167
|
+
totalIssuesFixed: 0,
|
|
1168
|
+
filesScanned: 0
|
|
1169
|
+
};
|
|
1170
|
+
watchers = /* @__PURE__ */ new Map();
|
|
1171
|
+
async execute(args) {
|
|
1172
|
+
const { action, directory, debounceMs = 1e3, yolo = false } = args;
|
|
1173
|
+
switch (action) {
|
|
1174
|
+
case "start":
|
|
1175
|
+
return this.startWatching(directory || process.cwd(), debounceMs, yolo);
|
|
1176
|
+
case "stop":
|
|
1177
|
+
return this.stopWatching();
|
|
1178
|
+
case "status":
|
|
1179
|
+
return this.getStatus();
|
|
1180
|
+
case "issues":
|
|
1181
|
+
return this.getCurrentIssues();
|
|
1182
|
+
case "yolo":
|
|
1183
|
+
return this.toggleYoloMode();
|
|
1184
|
+
default:
|
|
1185
|
+
return {
|
|
1186
|
+
content: [{
|
|
1187
|
+
type: "text",
|
|
1188
|
+
text: `Unknown action: ${action}. Use 'start', 'stop', 'status', 'issues', or 'yolo'.`
|
|
1189
|
+
}]
|
|
1190
|
+
};
|
|
1191
|
+
}
|
|
1192
|
+
}
|
|
1193
|
+
async startWatching(directory, debounceMs, yoloMode) {
|
|
1194
|
+
if (this.state.isRunning) {
|
|
1195
|
+
return {
|
|
1196
|
+
content: [{
|
|
1197
|
+
type: "text",
|
|
1198
|
+
text: "\u26A0\uFE0F Watch mode is already running. Use `trie_watch stop` to stop it first."
|
|
1199
|
+
}]
|
|
1200
|
+
};
|
|
1201
|
+
}
|
|
1202
|
+
this.state.isRunning = true;
|
|
1203
|
+
this.state.yoloMode = yoloMode;
|
|
1204
|
+
this.state.issueCache.clear();
|
|
1205
|
+
this.state.totalIssuesFound = 0;
|
|
1206
|
+
this.state.totalIssuesFixed = 0;
|
|
1207
|
+
this.state.filesScanned = 0;
|
|
1208
|
+
const modeEmoji = yoloMode ? "\u{1F525}" : "\u{1F441}\uFE0F";
|
|
1209
|
+
const modeName = yoloMode ? "YOLO MODE" : "WATCH MODE";
|
|
1210
|
+
console.error("\n\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501");
|
|
1211
|
+
console.error(`${modeEmoji} TRIE AGENT - AUTONOMOUS ${modeName}`);
|
|
1212
|
+
console.error("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n");
|
|
1213
|
+
console.error(`\u{1F4C2} Watching: ${directory}`);
|
|
1214
|
+
console.error(`\u23F1\uFE0F Debounce: ${debounceMs}ms`);
|
|
1215
|
+
if (yoloMode) {
|
|
1216
|
+
console.error("\u{1F525} YOLO MODE: Auto-fixing issues as they occur!");
|
|
1217
|
+
}
|
|
1218
|
+
console.error("");
|
|
1219
|
+
await this.watchDirectory(directory, debounceMs);
|
|
1220
|
+
console.error("\u{1F50D} Running initial scan...\n");
|
|
1221
|
+
const initialResult = await this.scanTool.execute({ directory });
|
|
1222
|
+
const yoloMessage = yoloMode ? `
|
|
1223
|
+
### \u{1F525} YOLO MODE ENABLED
|
|
1224
|
+
Issues will be **automatically fixed** as they're detected!
|
|
1225
|
+
- High-confidence fixes applied immediately
|
|
1226
|
+
- No confirmation needed
|
|
1227
|
+
- Living on the edge \u{1F680}
|
|
1228
|
+
|
|
1229
|
+
` : `
|
|
1230
|
+
### \u{1F4A1} Want auto-fixes?
|
|
1231
|
+
Run \`trie_watch yolo\` to enable YOLO mode and auto-fix issues as they occur!
|
|
1232
|
+
|
|
1233
|
+
`;
|
|
1234
|
+
return {
|
|
1235
|
+
content: [{
|
|
1236
|
+
type: "text",
|
|
1237
|
+
text: `${modeEmoji} **AUTONOMOUS ${modeName} ACTIVATED**
|
|
1238
|
+
|
|
1239
|
+
Trie Agent is now watching for file changes and will automatically scan them.
|
|
1240
|
+
|
|
1241
|
+
**Watching:** \`${directory}\`
|
|
1242
|
+
**Debounce:** ${debounceMs}ms (waits for you to stop typing)
|
|
1243
|
+
**YOLO Mode:** ${yoloMode ? "\u{1F525} ON - Auto-fixing!" : "\u274C OFF"}
|
|
1244
|
+
|
|
1245
|
+
### How it works:
|
|
1246
|
+
1. \u{1F4DD} You write/edit code
|
|
1247
|
+
2. \u{1F441}\uFE0F Trie detects the change
|
|
1248
|
+
3. \u{1F50D} Automatically scans the modified file
|
|
1249
|
+
4. ${yoloMode ? "\u{1F527} Auto-fixes issues immediately!" : "\u{1F6A8} Reports issues immediately"}
|
|
1250
|
+
${yoloMessage}
|
|
1251
|
+
### Commands:
|
|
1252
|
+
- \`trie_watch status\` - See current watch status
|
|
1253
|
+
- \`trie_watch yolo\` - Toggle YOLO mode (auto-fix)
|
|
1254
|
+
- \`trie_watch issues\` - Get all issues found so far
|
|
1255
|
+
- \`trie_watch stop\` - Stop autonomous mode
|
|
1256
|
+
|
|
1257
|
+
---
|
|
1258
|
+
|
|
1259
|
+
${initialResult.content?.[0]?.text || "Initial scan complete."}`
|
|
1260
|
+
}]
|
|
1261
|
+
};
|
|
1262
|
+
}
|
|
1263
|
+
toggleYoloMode() {
|
|
1264
|
+
if (!this.state.isRunning) {
|
|
1265
|
+
return {
|
|
1266
|
+
content: [{
|
|
1267
|
+
type: "text",
|
|
1268
|
+
text: `\u26A0\uFE0F Watch mode is not running. Start it first with:
|
|
1269
|
+
|
|
1270
|
+
\`trie_watch start\` - Normal mode
|
|
1271
|
+
\`trie_watch start yolo:true\` - YOLO mode from the start`
|
|
1272
|
+
}]
|
|
1273
|
+
};
|
|
1274
|
+
}
|
|
1275
|
+
this.state.yoloMode = !this.state.yoloMode;
|
|
1276
|
+
if (this.state.yoloMode) {
|
|
1277
|
+
console.error("\n\u{1F525} YOLO MODE ACTIVATED! Auto-fixing issues as they occur...\n");
|
|
1278
|
+
return {
|
|
1279
|
+
content: [{
|
|
1280
|
+
type: "text",
|
|
1281
|
+
text: `\u{1F525} **YOLO MODE ACTIVATED**
|
|
1282
|
+
|
|
1283
|
+
Issues will now be **automatically fixed** as they're detected!
|
|
1284
|
+
|
|
1285
|
+
\u26A0\uFE0F **Warning:** This will modify your files without confirmation.
|
|
1286
|
+
- High-confidence fixes only
|
|
1287
|
+
- Backs up original code in memory
|
|
1288
|
+
- You can always undo with git
|
|
1289
|
+
|
|
1290
|
+
Run \`trie_watch yolo\` again to disable.`
|
|
1291
|
+
}]
|
|
1292
|
+
};
|
|
1293
|
+
} else {
|
|
1294
|
+
console.error("\n\u{1F441}\uFE0F YOLO mode disabled. Back to watch-only mode.\n");
|
|
1295
|
+
return {
|
|
1296
|
+
content: [{
|
|
1297
|
+
type: "text",
|
|
1298
|
+
text: `\u{1F441}\uFE0F **YOLO MODE DISABLED**
|
|
1299
|
+
|
|
1300
|
+
Back to normal watch mode. Issues will be reported but not auto-fixed.
|
|
1301
|
+
|
|
1302
|
+
Run \`trie_watch yolo\` to re-enable.`
|
|
1303
|
+
}]
|
|
1304
|
+
};
|
|
1305
|
+
}
|
|
1306
|
+
}
|
|
1307
|
+
async watchDirectory(dir, debounceMs) {
|
|
1308
|
+
if (!existsSync3(dir)) return;
|
|
1309
|
+
try {
|
|
1310
|
+
const dirStat = await stat(dir);
|
|
1311
|
+
if (!dirStat.isDirectory()) return;
|
|
1312
|
+
const dirName = basename2(dir);
|
|
1313
|
+
if (SKIP_DIRS.has(dirName) || dirName.startsWith(".")) return;
|
|
1314
|
+
const watcher = watch(dir, { persistent: true }, async (_eventType, filename) => {
|
|
1315
|
+
if (!filename) return;
|
|
1316
|
+
const fullPath = join2(dir, filename);
|
|
1317
|
+
const ext = extname3(filename).toLowerCase();
|
|
1318
|
+
if (!WATCH_EXTENSIONS.has(ext)) return;
|
|
1319
|
+
if (!existsSync3(fullPath)) return;
|
|
1320
|
+
this.state.pendingFiles.add(fullPath);
|
|
1321
|
+
if (this.state.scanDebounceTimer) {
|
|
1322
|
+
clearTimeout(this.state.scanDebounceTimer);
|
|
1323
|
+
}
|
|
1324
|
+
this.state.scanDebounceTimer = setTimeout(() => {
|
|
1325
|
+
this.processPendingFiles();
|
|
1326
|
+
}, debounceMs);
|
|
1327
|
+
});
|
|
1328
|
+
this.watchers.set(dir, watcher);
|
|
1329
|
+
const { readdir: readdir3 } = await import("fs/promises");
|
|
1330
|
+
const entries = await readdir3(dir, { withFileTypes: true });
|
|
1331
|
+
for (const entry of entries) {
|
|
1332
|
+
if (entry.isDirectory()) {
|
|
1333
|
+
await this.watchDirectory(join2(dir, entry.name), debounceMs);
|
|
1334
|
+
}
|
|
1335
|
+
}
|
|
1336
|
+
} catch (error) {
|
|
1337
|
+
}
|
|
1338
|
+
}
|
|
1339
|
+
async processPendingFiles() {
|
|
1340
|
+
if (this.state.pendingFiles.size === 0) return;
|
|
1341
|
+
const files = Array.from(this.state.pendingFiles);
|
|
1342
|
+
this.state.pendingFiles.clear();
|
|
1343
|
+
const modeEmoji = this.state.yoloMode ? "\u{1F525}" : "\u{1F441}\uFE0F";
|
|
1344
|
+
console.error(`
|
|
1345
|
+
${modeEmoji} Detected changes in ${files.length} file(s):`);
|
|
1346
|
+
for (const file of files) {
|
|
1347
|
+
console.error(` \u2022 ${basename2(file)}`);
|
|
1348
|
+
}
|
|
1349
|
+
console.error("");
|
|
1350
|
+
try {
|
|
1351
|
+
const result = await this.scanTool.execute({ files });
|
|
1352
|
+
const resultText = result.content?.[0]?.text || "";
|
|
1353
|
+
this.state.filesScanned += files.length;
|
|
1354
|
+
const issueMatch = resultText.match(/(\d+) total/);
|
|
1355
|
+
if (issueMatch?.[1] !== void 0) {
|
|
1356
|
+
const newIssues = parseInt(issueMatch[1], 10);
|
|
1357
|
+
this.state.totalIssuesFound += newIssues;
|
|
1358
|
+
if (newIssues > 0) {
|
|
1359
|
+
console.error(`
|
|
1360
|
+
\u{1F6A8} Found ${newIssues} issues in changed files!`);
|
|
1361
|
+
const criticalMatch = resultText.match(/(\d+) CRITICAL/);
|
|
1362
|
+
const seriousMatch = resultText.match(/(\d+) SERIOUS/);
|
|
1363
|
+
const moderateMatch = resultText.match(/(\d+) MODERATE/);
|
|
1364
|
+
if (criticalMatch) {
|
|
1365
|
+
console.error(` \u{1F534} ${criticalMatch[1]} critical issues`);
|
|
1366
|
+
}
|
|
1367
|
+
if (seriousMatch) {
|
|
1368
|
+
console.error(` \u{1F7E0} ${seriousMatch[1]} serious issues`);
|
|
1369
|
+
}
|
|
1370
|
+
if (moderateMatch) {
|
|
1371
|
+
console.error(` \u{1F7E1} ${moderateMatch[1]} moderate issues`);
|
|
1372
|
+
}
|
|
1373
|
+
if (this.state.yoloMode) {
|
|
1374
|
+
console.error("\n\u{1F525} YOLO MODE: Attempting auto-fix...");
|
|
1375
|
+
await this.autoFixIssues(files);
|
|
1376
|
+
} else {
|
|
1377
|
+
console.error("\n\u{1F4A1} Run `trie_watch yolo` to auto-fix issues");
|
|
1378
|
+
}
|
|
1379
|
+
} else {
|
|
1380
|
+
console.error(" \u2705 No issues found - code looks good!");
|
|
1381
|
+
}
|
|
1382
|
+
}
|
|
1383
|
+
for (const file of files) {
|
|
1384
|
+
this.state.lastScan.set(file, Date.now());
|
|
1385
|
+
}
|
|
1386
|
+
} catch (error) {
|
|
1387
|
+
console.error(`\u274C Scan error: ${error}`);
|
|
1388
|
+
}
|
|
1389
|
+
}
|
|
1390
|
+
async autoFixIssues(files) {
|
|
1391
|
+
try {
|
|
1392
|
+
const fixResult = await this.fixTool.execute({
|
|
1393
|
+
files,
|
|
1394
|
+
autoApprove: true,
|
|
1395
|
+
minConfidence: 0.8
|
|
1396
|
+
// Only fix high-confidence issues
|
|
1397
|
+
});
|
|
1398
|
+
const fixText = fixResult.content?.[0]?.text || "";
|
|
1399
|
+
const fixedMatch = fixText.match(/(\d+) issues? fixed/i);
|
|
1400
|
+
if (fixedMatch?.[1] !== void 0) {
|
|
1401
|
+
const fixed = parseInt(fixedMatch[1], 10);
|
|
1402
|
+
this.state.totalIssuesFixed += fixed;
|
|
1403
|
+
console.error(` \u2705 Auto-fixed ${fixed} issues!`);
|
|
1404
|
+
} else if (fixText.includes("No issues")) {
|
|
1405
|
+
console.error(" \u2139\uFE0F No auto-fixable issues found");
|
|
1406
|
+
} else {
|
|
1407
|
+
const firstLine = fixText.split("\n")[0];
|
|
1408
|
+
console.error(` \u{1F527} ${firstLine}`);
|
|
1409
|
+
}
|
|
1410
|
+
} catch (error) {
|
|
1411
|
+
console.error(` \u26A0\uFE0F Auto-fix error: ${error}`);
|
|
1412
|
+
}
|
|
1413
|
+
}
|
|
1414
|
+
stopWatching() {
|
|
1415
|
+
if (!this.state.isRunning) {
|
|
1416
|
+
return {
|
|
1417
|
+
content: [{
|
|
1418
|
+
type: "text",
|
|
1419
|
+
text: "\u26A0\uFE0F Watch mode is not running."
|
|
1420
|
+
}]
|
|
1421
|
+
};
|
|
1422
|
+
}
|
|
1423
|
+
for (const [_dir, watcher] of this.watchers) {
|
|
1424
|
+
watcher.close();
|
|
1425
|
+
}
|
|
1426
|
+
this.watchers.clear();
|
|
1427
|
+
if (this.state.scanDebounceTimer) {
|
|
1428
|
+
clearTimeout(this.state.scanDebounceTimer);
|
|
1429
|
+
}
|
|
1430
|
+
const wasYolo = this.state.yoloMode;
|
|
1431
|
+
this.state.isRunning = false;
|
|
1432
|
+
this.state.yoloMode = false;
|
|
1433
|
+
const modeEmoji = wasYolo ? "\u{1F525}" : "\u{1F441}\uFE0F";
|
|
1434
|
+
console.error(`
|
|
1435
|
+
${modeEmoji} Watch mode stopped.
|
|
1436
|
+
`);
|
|
1437
|
+
const yoloStats = wasYolo ? `
|
|
1438
|
+
- \u{1F525} **Issues auto-fixed: ${this.state.totalIssuesFixed}**` : "";
|
|
1439
|
+
return {
|
|
1440
|
+
content: [{
|
|
1441
|
+
type: "text",
|
|
1442
|
+
text: `${modeEmoji} **AUTONOMOUS MODE STOPPED**
|
|
1443
|
+
|
|
1444
|
+
### Session Summary:
|
|
1445
|
+
- Files scanned: ${this.state.filesScanned}
|
|
1446
|
+
- Total issues found: ${this.state.totalIssuesFound}${yoloStats}
|
|
1447
|
+
- Directories watched: ${this.watchers.size}
|
|
1448
|
+
${wasYolo ? `
|
|
1449
|
+
\u{1F525} YOLO mode was ON - issues were auto-fixed as they occurred!` : ""}
|
|
1450
|
+
|
|
1451
|
+
Use \`trie_watch start\` to start watching again.
|
|
1452
|
+
Use \`trie_watch start yolo:true\` for YOLO mode.`
|
|
1453
|
+
}]
|
|
1454
|
+
};
|
|
1455
|
+
}
|
|
1456
|
+
getStatus() {
|
|
1457
|
+
if (!this.state.isRunning) {
|
|
1458
|
+
return {
|
|
1459
|
+
content: [{
|
|
1460
|
+
type: "text",
|
|
1461
|
+
text: `\u{1F441}\uFE0F **Watch Mode Status: STOPPED**
|
|
1462
|
+
|
|
1463
|
+
Use \`trie_watch start\` to begin autonomous scanning.
|
|
1464
|
+
Use \`trie_watch start yolo:true\` for YOLO mode (auto-fix).`
|
|
1465
|
+
}]
|
|
1466
|
+
};
|
|
1467
|
+
}
|
|
1468
|
+
const modeEmoji = this.state.yoloMode ? "\u{1F525}" : "\u{1F441}\uFE0F";
|
|
1469
|
+
const modeName = this.state.yoloMode ? "YOLO MODE" : "WATCH MODE";
|
|
1470
|
+
const recentScans = Array.from(this.state.lastScan.entries()).sort((a, b) => b[1] - a[1]).slice(0, 5).map(([file, time]) => {
|
|
1471
|
+
const ago = Math.round((Date.now() - time) / 1e3);
|
|
1472
|
+
return `- \`${basename2(file)}\` (${ago}s ago)`;
|
|
1473
|
+
}).join("\n");
|
|
1474
|
+
const yoloStats = this.state.yoloMode ? `
|
|
1475
|
+
- \u{1F525} Issues auto-fixed: ${this.state.totalIssuesFixed}` : "";
|
|
1476
|
+
return {
|
|
1477
|
+
content: [{
|
|
1478
|
+
type: "text",
|
|
1479
|
+
text: `${modeEmoji} **${modeName} Status: RUNNING**
|
|
1480
|
+
|
|
1481
|
+
### Stats:
|
|
1482
|
+
- Directories watched: ${this.watchers.size}
|
|
1483
|
+
- Files scanned this session: ${this.state.filesScanned}
|
|
1484
|
+
- Total issues found: ${this.state.totalIssuesFound}${yoloStats}
|
|
1485
|
+
- Pending files: ${this.state.pendingFiles.size}
|
|
1486
|
+
- YOLO Mode: ${this.state.yoloMode ? "\u{1F525} ON" : "\u274C OFF"}
|
|
1487
|
+
|
|
1488
|
+
### Recently Scanned:
|
|
1489
|
+
${recentScans || "(none yet)"}
|
|
1490
|
+
|
|
1491
|
+
### Commands:
|
|
1492
|
+
- \`trie_watch yolo\` - ${this.state.yoloMode ? "Disable" : "Enable"} YOLO mode
|
|
1493
|
+
- \`trie_watch issues\` - Get all issues found
|
|
1494
|
+
- \`trie_watch stop\` - Stop watching`
|
|
1495
|
+
}]
|
|
1496
|
+
};
|
|
1497
|
+
}
|
|
1498
|
+
getCurrentIssues() {
|
|
1499
|
+
return {
|
|
1500
|
+
content: [{
|
|
1501
|
+
type: "text",
|
|
1502
|
+
text: `\u{1F4CB} **Issues Found This Session**
|
|
1503
|
+
|
|
1504
|
+
Total issues: ${this.state.totalIssuesFound}
|
|
1505
|
+
Files scanned: ${this.state.filesScanned}
|
|
1506
|
+
|
|
1507
|
+
To get a full report, run \`trie_scan\` on your codebase.`
|
|
1508
|
+
}]
|
|
1509
|
+
};
|
|
1510
|
+
}
|
|
1511
|
+
};
|
|
1512
|
+
|
|
1513
|
+
// src/tools/agent.ts
|
|
1514
|
+
import { readdir, readFile as readFile4 } from "fs/promises";
|
|
1515
|
+
import { existsSync as existsSync4 } from "fs";
|
|
1516
|
+
import { join as join3, extname as extname5, isAbsolute as isAbsolute3, resolve as resolve3, basename as basename4 } from "path";
|
|
1517
|
+
|
|
1518
|
+
// src/ai/engine.ts
|
|
1519
|
+
import { readFile as readFile3 } from "fs/promises";
|
|
1520
|
+
import { extname as extname4, relative as relative3, basename as basename3 } from "path";
|
|
1521
|
+
function detectLanguage(filePath) {
|
|
1522
|
+
const ext = extname4(filePath).toLowerCase();
|
|
1523
|
+
const langMap = {
|
|
1524
|
+
".ts": "typescript",
|
|
1525
|
+
".tsx": "tsx",
|
|
1526
|
+
".js": "javascript",
|
|
1527
|
+
".jsx": "jsx",
|
|
1528
|
+
".py": "python",
|
|
1529
|
+
".go": "go",
|
|
1530
|
+
".rs": "rust",
|
|
1531
|
+
".java": "java",
|
|
1532
|
+
".rb": "ruby",
|
|
1533
|
+
".php": "php",
|
|
1534
|
+
".vue": "vue",
|
|
1535
|
+
".svelte": "svelte",
|
|
1536
|
+
".sql": "sql",
|
|
1537
|
+
".json": "json",
|
|
1538
|
+
".yaml": "yaml",
|
|
1539
|
+
".yml": "yaml",
|
|
1540
|
+
".md": "markdown",
|
|
1541
|
+
".html": "html",
|
|
1542
|
+
".css": "css",
|
|
1543
|
+
".scss": "scss"
|
|
1544
|
+
};
|
|
1545
|
+
return langMap[ext] || "plaintext";
|
|
1546
|
+
}
|
|
1547
|
+
function analyzeFileContext(content, _filePath) {
|
|
1548
|
+
const summary = {
|
|
1549
|
+
hasAuth: false,
|
|
1550
|
+
hasDatabase: false,
|
|
1551
|
+
hasUserInput: false,
|
|
1552
|
+
hasFileOps: false,
|
|
1553
|
+
hasCrypto: false,
|
|
1554
|
+
hasExternalAPIs: false,
|
|
1555
|
+
exports: [],
|
|
1556
|
+
imports: []
|
|
1557
|
+
};
|
|
1558
|
+
if (/auth|login|session|jwt|token|password|credential|oauth/i.test(content)) {
|
|
1559
|
+
summary.hasAuth = true;
|
|
1560
|
+
}
|
|
1561
|
+
if (/prisma|sequelize|knex|mongodb|sql|query|database|postgres|mysql|sqlite/i.test(content)) {
|
|
1562
|
+
summary.hasDatabase = true;
|
|
1563
|
+
}
|
|
1564
|
+
if (/req\.body|req\.params|req\.query|formData|input|textarea|userinput/i.test(content)) {
|
|
1565
|
+
summary.hasUserInput = true;
|
|
1566
|
+
}
|
|
1567
|
+
if (/readFile|writeFile|createReadStream|createWriteStream|fs\.|multer|upload/i.test(content)) {
|
|
1568
|
+
summary.hasFileOps = true;
|
|
1569
|
+
}
|
|
1570
|
+
if (/crypto|bcrypt|argon|scrypt|encrypt|decrypt|hash|sign|verify/i.test(content)) {
|
|
1571
|
+
summary.hasCrypto = true;
|
|
1572
|
+
}
|
|
1573
|
+
if (/fetch\(|axios|http\.|https\.|request\(|got\(|api\//i.test(content)) {
|
|
1574
|
+
summary.hasExternalAPIs = true;
|
|
1575
|
+
}
|
|
1576
|
+
if (/from\s+['"]react['"]|import\s+React/i.test(content)) {
|
|
1577
|
+
summary.framework = "React";
|
|
1578
|
+
} else if (/from\s+['"]next|NextRequest|getServerSideProps/i.test(content)) {
|
|
1579
|
+
summary.framework = "Next.js";
|
|
1580
|
+
} else if (/from\s+['"]express['"]|express\(\)/i.test(content)) {
|
|
1581
|
+
summary.framework = "Express";
|
|
1582
|
+
} else if (/from\s+['"]fastify['"]|fastify\(\)/i.test(content)) {
|
|
1583
|
+
summary.framework = "Fastify";
|
|
1584
|
+
} else if (/from\s+['"]vue['"]|defineComponent/i.test(content)) {
|
|
1585
|
+
summary.framework = "Vue";
|
|
1586
|
+
}
|
|
1587
|
+
const exportMatches = content.match(/export\s+(const|function|class|interface|type)\s+(\w+)/g) || [];
|
|
1588
|
+
summary.exports = exportMatches.map((m) => m.split(/\s+/).pop() || "").filter(Boolean).slice(0, 10);
|
|
1589
|
+
const importMatches = content.match(/import\s+.*from\s+['"]([^'"]+)['"]/g) || [];
|
|
1590
|
+
summary.imports = importMatches.map((m) => {
|
|
1591
|
+
const match = m.match(/from\s+['"]([^'"]+)['"]/);
|
|
1592
|
+
return match ? match[1] : "";
|
|
1593
|
+
}).filter((s) => Boolean(s)).slice(0, 10);
|
|
1594
|
+
return summary;
|
|
1595
|
+
}
|
|
1596
|
+
function buildPromptContext(contexts, agent) {
|
|
1597
|
+
let contextSummary = "";
|
|
1598
|
+
const hasAuth = contexts.some((c) => c.summary?.hasAuth);
|
|
1599
|
+
const hasDatabase = contexts.some((c) => c.summary?.hasDatabase);
|
|
1600
|
+
const hasUserInput = contexts.some((c) => c.summary?.hasUserInput);
|
|
1601
|
+
const hasFileOps = contexts.some((c) => c.summary?.hasFileOps);
|
|
1602
|
+
const hasCrypto = contexts.some((c) => c.summary?.hasCrypto);
|
|
1603
|
+
const hasExternalAPIs = contexts.some((c) => c.summary?.hasExternalAPIs);
|
|
1604
|
+
const frameworks = [...new Set(contexts.map((c) => c.summary?.framework).filter(Boolean))];
|
|
1605
|
+
contextSummary += "## Code Context Summary\n\n";
|
|
1606
|
+
contextSummary += `**Files analyzed:** ${contexts.length}
|
|
1607
|
+
`;
|
|
1608
|
+
contextSummary += `**Total lines:** ${contexts.reduce((sum, c) => sum + c.lineCount, 0)}
|
|
1609
|
+
`;
|
|
1610
|
+
if (frameworks.length > 0) {
|
|
1611
|
+
contextSummary += `**Frameworks detected:** ${frameworks.join(", ")}
|
|
1612
|
+
`;
|
|
1613
|
+
}
|
|
1614
|
+
const features = [];
|
|
1615
|
+
if (hasAuth) features.push("Authentication/Authorization");
|
|
1616
|
+
if (hasDatabase) features.push("Database operations");
|
|
1617
|
+
if (hasUserInput) features.push("User input handling");
|
|
1618
|
+
if (hasFileOps) features.push("File operations");
|
|
1619
|
+
if (hasCrypto) features.push("Cryptography");
|
|
1620
|
+
if (hasExternalAPIs) features.push("External API calls");
|
|
1621
|
+
if (features.length > 0) {
|
|
1622
|
+
contextSummary += `**Key features:** ${features.join(", ")}
|
|
1623
|
+
`;
|
|
1624
|
+
}
|
|
1625
|
+
contextSummary += "\n";
|
|
1626
|
+
if (agent === "security") {
|
|
1627
|
+
if (hasAuth) contextSummary += "\u26A0\uFE0F **Auth code detected** - Review authentication flows carefully\n";
|
|
1628
|
+
if (hasDatabase) contextSummary += "\u26A0\uFE0F **Database ops detected** - Check for injection vulnerabilities\n";
|
|
1629
|
+
if (hasUserInput) contextSummary += "\u26A0\uFE0F **User input detected** - Verify input validation\n";
|
|
1630
|
+
} else if (agent === "privacy") {
|
|
1631
|
+
if (hasAuth) contextSummary += "\u26A0\uFE0F **Auth code detected** - Review credential handling\n";
|
|
1632
|
+
if (hasDatabase) contextSummary += "\u26A0\uFE0F **Database ops detected** - Check PII storage\n";
|
|
1633
|
+
} else if (agent === "bugs") {
|
|
1634
|
+
if (hasExternalAPIs) contextSummary += "\u26A0\uFE0F **External APIs detected** - Check error handling\n";
|
|
1635
|
+
if (hasFileOps) contextSummary += "\u26A0\uFE0F **File ops detected** - Verify error handling and cleanup\n";
|
|
1636
|
+
}
|
|
1637
|
+
return contextSummary;
|
|
1638
|
+
}
|
|
1639
|
+
async function buildAnalysis(request) {
|
|
1640
|
+
const { agent, files, context = {}, depth = "standard" } = request;
|
|
1641
|
+
const codeContexts = [];
|
|
1642
|
+
console.error(`\u{1F52C} AI Engine: Preparing ${agent} analysis for ${files.length} files...`);
|
|
1643
|
+
for (const filePath of files) {
|
|
1644
|
+
try {
|
|
1645
|
+
const content = await readFile3(filePath, "utf-8");
|
|
1646
|
+
const language = detectLanguage(filePath);
|
|
1647
|
+
const summary = analyzeFileContext(content, filePath);
|
|
1648
|
+
console.error(` \u{1F4C4} ${basename3(filePath)} - ${content.split("\n").length} lines`);
|
|
1649
|
+
codeContexts.push({
|
|
1650
|
+
filePath,
|
|
1651
|
+
relativePath: relative3(process.cwd(), filePath),
|
|
1652
|
+
language,
|
|
1653
|
+
content: depth === "quick" && content.length > 5e3 ? content.substring(0, 5e3) + "\n\n... (truncated for quick analysis)" : content,
|
|
1654
|
+
lineCount: content.split("\n").length,
|
|
1655
|
+
summary
|
|
1656
|
+
});
|
|
1657
|
+
} catch (error) {
|
|
1658
|
+
console.error(` \u26A0\uFE0F Error reading ${filePath}:`, error);
|
|
1659
|
+
}
|
|
1660
|
+
}
|
|
1661
|
+
const codeBlocks = codeContexts.map(
|
|
1662
|
+
(ctx) => `### File: ${ctx.relativePath}
|
|
1663
|
+
\`\`\`${ctx.language}
|
|
1664
|
+
${ctx.content}
|
|
1665
|
+
\`\`\``
|
|
1666
|
+
).join("\n\n");
|
|
1667
|
+
const promptContext = buildPromptContext(codeContexts, agent);
|
|
1668
|
+
const variables = {
|
|
1669
|
+
...context,
|
|
1670
|
+
code: codeBlocks,
|
|
1671
|
+
language: codeContexts[0]?.language || "unknown",
|
|
1672
|
+
filePath: codeContexts.map((c) => c.relativePath).join(", ")
|
|
1673
|
+
};
|
|
1674
|
+
const analysisPrompt = getPrompt(agent, "analysis", variables);
|
|
1675
|
+
const systemPrompt = getSystemPrompt(agent);
|
|
1676
|
+
const suggestedFollowUps = [];
|
|
1677
|
+
if (codeContexts.some((c) => c.summary?.hasAuth)) {
|
|
1678
|
+
suggestedFollowUps.push("\u{1F510} Review authentication implementation");
|
|
1679
|
+
}
|
|
1680
|
+
if (codeContexts.some((c) => c.summary?.hasDatabase)) {
|
|
1681
|
+
suggestedFollowUps.push("\u{1F5C4}\uFE0F Check database query security");
|
|
1682
|
+
}
|
|
1683
|
+
if (codeContexts.some((c) => c.summary?.hasUserInput)) {
|
|
1684
|
+
suggestedFollowUps.push("\u{1F4DD} Verify input validation");
|
|
1685
|
+
}
|
|
1686
|
+
const filesSummary = codeContexts.map((c) => {
|
|
1687
|
+
const features = [];
|
|
1688
|
+
if (c.summary?.hasAuth) features.push("auth");
|
|
1689
|
+
if (c.summary?.hasDatabase) features.push("db");
|
|
1690
|
+
if (c.summary?.hasUserInput) features.push("input");
|
|
1691
|
+
if (c.summary?.framework) features.push(c.summary.framework);
|
|
1692
|
+
const featureStr = features.length > 0 ? ` [${features.join(", ")}]` : "";
|
|
1693
|
+
return `\`${c.relativePath}\` (${c.lineCount} lines)${featureStr}`;
|
|
1694
|
+
}).join("\n");
|
|
1695
|
+
console.error(`\u{1F9E0} AI Engine: Analysis prepared - ready for Claude`);
|
|
1696
|
+
return {
|
|
1697
|
+
agent,
|
|
1698
|
+
systemPrompt,
|
|
1699
|
+
prompt: promptContext + "\n" + analysisPrompt,
|
|
1700
|
+
codeContext: codeContexts,
|
|
1701
|
+
suggestedFollowUps,
|
|
1702
|
+
filesSummary
|
|
1703
|
+
};
|
|
1704
|
+
}
|
|
1705
|
+
function formatAnalysisResponse(result, options = {}) {
|
|
1706
|
+
const { agent, systemPrompt, prompt, codeContext, suggestedFollowUps, filesSummary } = result;
|
|
1707
|
+
const includePrompt = options.includePrompt ?? true;
|
|
1708
|
+
let output = "";
|
|
1709
|
+
output += `
|
|
1710
|
+
${"\u2501".repeat(60)}
|
|
1711
|
+
`;
|
|
1712
|
+
output += `\u{1F9E0} ${agent.toUpperCase()} AGENT - AI ANALYSIS
|
|
1713
|
+
`;
|
|
1714
|
+
output += `${"\u2501".repeat(60)}
|
|
1715
|
+
|
|
1716
|
+
`;
|
|
1717
|
+
output += `## \u{1F4C2} Files for Analysis
|
|
1718
|
+
|
|
1719
|
+
`;
|
|
1720
|
+
output += filesSummary + "\n\n";
|
|
1721
|
+
if (suggestedFollowUps.length > 0) {
|
|
1722
|
+
output += `## \u{1F3AF} Focus Areas
|
|
1723
|
+
|
|
1724
|
+
`;
|
|
1725
|
+
for (const followUp of suggestedFollowUps) {
|
|
1726
|
+
output += `- ${followUp}
|
|
1727
|
+
`;
|
|
1728
|
+
}
|
|
1729
|
+
output += "\n";
|
|
1730
|
+
}
|
|
1731
|
+
output += `${"\u2500".repeat(60)}
|
|
1732
|
+
`;
|
|
1733
|
+
output += `## \u{1F9E0} Analysis Request for Claude
|
|
1734
|
+
|
|
1735
|
+
`;
|
|
1736
|
+
if (includePrompt) {
|
|
1737
|
+
output += `**Role:** ${systemPrompt.split("\n")[0]}
|
|
1738
|
+
|
|
1739
|
+
`;
|
|
1740
|
+
output += `---
|
|
1741
|
+
|
|
1742
|
+
`;
|
|
1743
|
+
output += prompt;
|
|
1744
|
+
output += `
|
|
1745
|
+
|
|
1746
|
+
${"\u2500".repeat(60)}
|
|
1747
|
+
`;
|
|
1748
|
+
} else {
|
|
1749
|
+
output += `*Run with \`output: "full"\` to see the complete analysis prompt.*
|
|
1750
|
+
|
|
1751
|
+
`;
|
|
1752
|
+
output += `**Summary:** ${codeContext.length} files ready for ${agent} analysis
|
|
1753
|
+
`;
|
|
1754
|
+
}
|
|
1755
|
+
output += `
|
|
1756
|
+
**Claude:** Please analyze the code above according to the ${agent} analysis instructions and provide your findings.
|
|
1757
|
+
`;
|
|
1758
|
+
return output;
|
|
1759
|
+
}
|
|
1760
|
+
|
|
1761
|
+
// src/knowledge/index.ts
|
|
1762
|
+
var SECURITY_SOURCES = [
|
|
1763
|
+
{
|
|
1764
|
+
name: "OWASP Top 10",
|
|
1765
|
+
url: "https://owasp.org/Top10/",
|
|
1766
|
+
description: "Most critical web application security risks"
|
|
1767
|
+
},
|
|
1768
|
+
{
|
|
1769
|
+
name: "NIST NVD",
|
|
1770
|
+
url: "https://nvd.nist.gov/",
|
|
1771
|
+
description: "National Vulnerability Database"
|
|
1772
|
+
},
|
|
1773
|
+
{
|
|
1774
|
+
name: "CVE Database",
|
|
1775
|
+
url: "https://cve.mitre.org/",
|
|
1776
|
+
description: "Common Vulnerabilities and Exposures"
|
|
1777
|
+
},
|
|
1778
|
+
{
|
|
1779
|
+
name: "Snyk Vulnerability DB",
|
|
1780
|
+
url: "https://security.snyk.io/",
|
|
1781
|
+
description: "Open source vulnerability database"
|
|
1782
|
+
},
|
|
1783
|
+
{
|
|
1784
|
+
name: "GitHub Advisory Database",
|
|
1785
|
+
url: "https://github.com/advisories",
|
|
1786
|
+
description: "Security advisories from GitHub"
|
|
1787
|
+
}
|
|
1788
|
+
];
|
|
1789
|
+
var DOCS_SOURCES = {
|
|
1790
|
+
react: [
|
|
1791
|
+
{ name: "React Docs", url: "https://react.dev/", description: "Official React documentation" },
|
|
1792
|
+
{ name: "React Security", url: "https://react.dev/reference/react-dom/components/common#dangerously-setting-the-inner-html", description: "React security guidance" }
|
|
1793
|
+
],
|
|
1794
|
+
nextjs: [
|
|
1795
|
+
{ name: "Next.js Docs", url: "https://nextjs.org/docs", description: "Official Next.js documentation" },
|
|
1796
|
+
{ name: "Next.js Security", url: "https://nextjs.org/docs/advanced-features/security-headers", description: "Next.js security headers" }
|
|
1797
|
+
],
|
|
1798
|
+
express: [
|
|
1799
|
+
{ name: "Express Docs", url: "https://expressjs.com/", description: "Official Express.js documentation" },
|
|
1800
|
+
{ name: "Express Security", url: "https://expressjs.com/en/advanced/best-practice-security.html", description: "Express security best practices" }
|
|
1801
|
+
],
|
|
1802
|
+
node: [
|
|
1803
|
+
{ name: "Node.js Docs", url: "https://nodejs.org/docs/", description: "Official Node.js documentation" },
|
|
1804
|
+
{ name: "Node.js Security", url: "https://nodejs.org/en/docs/guides/security/", description: "Node.js security guidance" }
|
|
1805
|
+
],
|
|
1806
|
+
typescript: [
|
|
1807
|
+
{ name: "TypeScript Docs", url: "https://www.typescriptlang.org/docs/", description: "Official TypeScript documentation" }
|
|
1808
|
+
]
|
|
1809
|
+
};
|
|
1810
|
+
var COMPLIANCE_SOURCES = {
|
|
1811
|
+
gdpr: [
|
|
1812
|
+
{ name: "GDPR Official Text", url: "https://gdpr.eu/", description: "Complete GDPR regulation text" },
|
|
1813
|
+
{ name: "GDPR Checklist", url: "https://gdpr.eu/checklist/", description: "GDPR compliance checklist" }
|
|
1814
|
+
],
|
|
1815
|
+
hipaa: [
|
|
1816
|
+
{ name: "HIPAA Guidelines", url: "https://www.hhs.gov/hipaa/", description: "Official HIPAA guidance" }
|
|
1817
|
+
],
|
|
1818
|
+
wcag: [
|
|
1819
|
+
{ name: "WCAG 2.1", url: "https://www.w3.org/WAI/WCAG21/quickref/", description: "WCAG 2.1 quick reference" }
|
|
1820
|
+
],
|
|
1821
|
+
pci: [
|
|
1822
|
+
{ name: "PCI DSS", url: "https://www.pcisecuritystandards.org/", description: "Payment Card Industry standards" }
|
|
1823
|
+
]
|
|
1824
|
+
};
|
|
1825
|
+
function generateCVELookup(library, version) {
|
|
1826
|
+
let prompt = `## \u{1F512} Security Vulnerability Lookup
|
|
1827
|
+
|
|
1828
|
+
`;
|
|
1829
|
+
prompt += `**Library:** ${library}
|
|
1830
|
+
`;
|
|
1831
|
+
if (version) prompt += `**Version:** ${version}
|
|
1832
|
+
`;
|
|
1833
|
+
prompt += `
|
|
1834
|
+
`;
|
|
1835
|
+
prompt += `### Action Required
|
|
1836
|
+
|
|
1837
|
+
`;
|
|
1838
|
+
prompt += `Please check for known vulnerabilities:
|
|
1839
|
+
|
|
1840
|
+
`;
|
|
1841
|
+
prompt += `1. Search the **NIST NVD** (https://nvd.nist.gov/) for CVEs affecting \`${library}\`
|
|
1842
|
+
`;
|
|
1843
|
+
prompt += `2. Check **Snyk** (https://security.snyk.io/) for vulnerability reports
|
|
1844
|
+
`;
|
|
1845
|
+
prompt += `3. Review **GitHub Advisory Database** for any security advisories
|
|
1846
|
+
`;
|
|
1847
|
+
prompt += `4. Check npm audit / yarn audit if it's an npm package
|
|
1848
|
+
|
|
1849
|
+
`;
|
|
1850
|
+
prompt += `### Report Format
|
|
1851
|
+
|
|
1852
|
+
`;
|
|
1853
|
+
prompt += `For each vulnerability found, provide:
|
|
1854
|
+
`;
|
|
1855
|
+
prompt += `- CVE ID (if applicable)
|
|
1856
|
+
`;
|
|
1857
|
+
prompt += `- Severity (Critical/High/Medium/Low)
|
|
1858
|
+
`;
|
|
1859
|
+
prompt += `- Affected versions
|
|
1860
|
+
`;
|
|
1861
|
+
prompt += `- Description of the vulnerability
|
|
1862
|
+
`;
|
|
1863
|
+
prompt += `- Remediation (upgrade version, patches, mitigations)
|
|
1864
|
+
`;
|
|
1865
|
+
return prompt;
|
|
1866
|
+
}
|
|
1867
|
+
function generateDocsLookup(topic, framework) {
|
|
1868
|
+
let prompt = `## \u{1F4DA} Documentation Lookup
|
|
1869
|
+
|
|
1870
|
+
`;
|
|
1871
|
+
prompt += `**Topic:** ${topic}
|
|
1872
|
+
`;
|
|
1873
|
+
if (framework) prompt += `**Framework:** ${framework}
|
|
1874
|
+
`;
|
|
1875
|
+
prompt += `
|
|
1876
|
+
`;
|
|
1877
|
+
const sources = framework ? DOCS_SOURCES[framework.toLowerCase()] : [];
|
|
1878
|
+
if (sources && sources.length > 0) {
|
|
1879
|
+
prompt += `### Recommended Sources
|
|
1880
|
+
|
|
1881
|
+
`;
|
|
1882
|
+
for (const source of sources) {
|
|
1883
|
+
prompt += `- [${source.name}](${source.url}) - ${source.description}
|
|
1884
|
+
`;
|
|
1885
|
+
}
|
|
1886
|
+
prompt += `
|
|
1887
|
+
`;
|
|
1888
|
+
}
|
|
1889
|
+
prompt += `### Information Needed
|
|
1890
|
+
|
|
1891
|
+
`;
|
|
1892
|
+
prompt += `Please look up the latest documentation and provide:
|
|
1893
|
+
|
|
1894
|
+
`;
|
|
1895
|
+
prompt += `1. Current best practices for "${topic}"
|
|
1896
|
+
`;
|
|
1897
|
+
prompt += `2. Common pitfalls to avoid
|
|
1898
|
+
`;
|
|
1899
|
+
prompt += `3. Code examples demonstrating correct usage
|
|
1900
|
+
`;
|
|
1901
|
+
prompt += `4. Any recent changes or deprecations
|
|
1902
|
+
`;
|
|
1903
|
+
return prompt;
|
|
1904
|
+
}
|
|
1905
|
+
function generateSecurityLookup(pattern, context) {
|
|
1906
|
+
let prompt = `## \u{1F6E1}\uFE0F Security Best Practices Lookup
|
|
1907
|
+
|
|
1908
|
+
`;
|
|
1909
|
+
prompt += `**Pattern:** ${pattern}
|
|
1910
|
+
`;
|
|
1911
|
+
if (context) prompt += `**Context:** ${context}
|
|
1912
|
+
`;
|
|
1913
|
+
prompt += `
|
|
1914
|
+
`;
|
|
1915
|
+
prompt += `### Reference Sources
|
|
1916
|
+
|
|
1917
|
+
`;
|
|
1918
|
+
for (const source of SECURITY_SOURCES.slice(0, 3)) {
|
|
1919
|
+
prompt += `- [${source.name}](${source.url})
|
|
1920
|
+
`;
|
|
1921
|
+
}
|
|
1922
|
+
prompt += `
|
|
1923
|
+
`;
|
|
1924
|
+
prompt += `### Analysis Requested
|
|
1925
|
+
|
|
1926
|
+
`;
|
|
1927
|
+
prompt += `Please research and provide:
|
|
1928
|
+
|
|
1929
|
+
`;
|
|
1930
|
+
prompt += `1. **OWASP guidance** for this security pattern
|
|
1931
|
+
`;
|
|
1932
|
+
prompt += `2. **Attack vectors** - how this could be exploited
|
|
1933
|
+
`;
|
|
1934
|
+
prompt += `3. **Defense strategies** - recommended mitigations
|
|
1935
|
+
`;
|
|
1936
|
+
prompt += `4. **Code examples** - secure implementation patterns
|
|
1937
|
+
`;
|
|
1938
|
+
prompt += `5. **Testing approaches** - how to verify security
|
|
1939
|
+
`;
|
|
1940
|
+
return prompt;
|
|
1941
|
+
}
|
|
1942
|
+
function generateComplianceLookup(regulation, requirement) {
|
|
1943
|
+
let prompt = `## \u2696\uFE0F Compliance Requirement Lookup
|
|
1944
|
+
|
|
1945
|
+
`;
|
|
1946
|
+
prompt += `**Regulation:** ${regulation}
|
|
1947
|
+
`;
|
|
1948
|
+
if (requirement) prompt += `**Specific Requirement:** ${requirement}
|
|
1949
|
+
`;
|
|
1950
|
+
prompt += `
|
|
1951
|
+
`;
|
|
1952
|
+
const sources = COMPLIANCE_SOURCES[regulation.toLowerCase()];
|
|
1953
|
+
if (sources && sources.length > 0) {
|
|
1954
|
+
prompt += `### Official Sources
|
|
1955
|
+
|
|
1956
|
+
`;
|
|
1957
|
+
for (const source of sources) {
|
|
1958
|
+
prompt += `- [${source.name}](${source.url})
|
|
1959
|
+
`;
|
|
1960
|
+
}
|
|
1961
|
+
prompt += `
|
|
1962
|
+
`;
|
|
1963
|
+
}
|
|
1964
|
+
prompt += `### Compliance Information Needed
|
|
1965
|
+
|
|
1966
|
+
`;
|
|
1967
|
+
prompt += `Please research and provide:
|
|
1968
|
+
|
|
1969
|
+
`;
|
|
1970
|
+
prompt += `1. **Specific requirement text** from the regulation
|
|
1971
|
+
`;
|
|
1972
|
+
prompt += `2. **Technical requirements** for compliance
|
|
1973
|
+
`;
|
|
1974
|
+
prompt += `3. **Implementation guidance** with code examples
|
|
1975
|
+
`;
|
|
1976
|
+
prompt += `4. **Documentation requirements** - what records to keep
|
|
1977
|
+
`;
|
|
1978
|
+
prompt += `5. **Penalties** for non-compliance
|
|
1979
|
+
`;
|
|
1980
|
+
return prompt;
|
|
1981
|
+
}
|
|
1982
|
+
function generateChangelogLookup(library, fromVersion, toVersion) {
|
|
1983
|
+
let prompt = `## \u{1F4CB} Changelog Lookup
|
|
1984
|
+
|
|
1985
|
+
`;
|
|
1986
|
+
prompt += `**Library:** ${library}
|
|
1987
|
+
`;
|
|
1988
|
+
prompt += `**Current Version:** ${fromVersion}
|
|
1989
|
+
`;
|
|
1990
|
+
if (toVersion) prompt += `**Target Version:** ${toVersion}
|
|
1991
|
+
`;
|
|
1992
|
+
prompt += `
|
|
1993
|
+
`;
|
|
1994
|
+
prompt += `### Information Needed
|
|
1995
|
+
|
|
1996
|
+
`;
|
|
1997
|
+
prompt += `Please find the changelog/release notes and provide:
|
|
1998
|
+
|
|
1999
|
+
`;
|
|
2000
|
+
prompt += `1. **Breaking changes** between versions
|
|
2001
|
+
`;
|
|
2002
|
+
prompt += `2. **New features** added
|
|
2003
|
+
`;
|
|
2004
|
+
prompt += `3. **Deprecations** to be aware of
|
|
2005
|
+
`;
|
|
2006
|
+
prompt += `4. **Security fixes** included
|
|
2007
|
+
`;
|
|
2008
|
+
prompt += `5. **Migration guide** if available
|
|
2009
|
+
`;
|
|
2010
|
+
prompt += `6. **Known issues** with the upgrade
|
|
2011
|
+
`;
|
|
2012
|
+
return prompt;
|
|
2013
|
+
}
|
|
2014
|
+
function lookupKnowledge(request) {
|
|
2015
|
+
const { type, query, context = {} } = request;
|
|
2016
|
+
switch (type) {
|
|
2017
|
+
case "cve":
|
|
2018
|
+
return generateCVELookup(query, context.version);
|
|
2019
|
+
case "docs":
|
|
2020
|
+
return generateDocsLookup(query, context.framework);
|
|
2021
|
+
case "security":
|
|
2022
|
+
return generateSecurityLookup(query, context.context);
|
|
2023
|
+
case "best-practices":
|
|
2024
|
+
return generateComplianceLookup(query, context.requirement);
|
|
2025
|
+
case "changelog":
|
|
2026
|
+
return generateChangelogLookup(query, context.fromVersion || "current", context.toVersion);
|
|
2027
|
+
default:
|
|
2028
|
+
return `Unknown knowledge request type: ${type}`;
|
|
2029
|
+
}
|
|
2030
|
+
}
|
|
2031
|
+
|
|
2032
|
+
// src/tools/agent.ts
|
|
2033
|
+
var SCANNABLE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
2034
|
+
".ts",
|
|
2035
|
+
".tsx",
|
|
2036
|
+
".js",
|
|
2037
|
+
".jsx",
|
|
2038
|
+
".mjs",
|
|
2039
|
+
".cjs",
|
|
2040
|
+
".vue",
|
|
2041
|
+
".svelte",
|
|
2042
|
+
".astro",
|
|
2043
|
+
".py",
|
|
2044
|
+
".go",
|
|
2045
|
+
".rs"
|
|
2046
|
+
]);
|
|
2047
|
+
var SKIP_DIRS2 = /* @__PURE__ */ new Set([
|
|
2048
|
+
"node_modules",
|
|
2049
|
+
".git",
|
|
2050
|
+
"dist",
|
|
2051
|
+
"build",
|
|
2052
|
+
".next",
|
|
2053
|
+
".nuxt",
|
|
2054
|
+
"coverage",
|
|
2055
|
+
".nyc_output",
|
|
2056
|
+
"__pycache__",
|
|
2057
|
+
".pytest_cache",
|
|
2058
|
+
"vendor",
|
|
2059
|
+
".venv",
|
|
2060
|
+
"venv",
|
|
2061
|
+
"target",
|
|
2062
|
+
".turbo",
|
|
2063
|
+
".cache"
|
|
2064
|
+
]);
|
|
2065
|
+
var AGENT_TO_AI_TYPE = {
|
|
2066
|
+
"security": "security",
|
|
2067
|
+
"privacy": "privacy",
|
|
2068
|
+
"legal": "legal",
|
|
2069
|
+
"design-engineer": "accessibility",
|
|
2070
|
+
"software-architect": "architecture",
|
|
2071
|
+
"bug-finding": "bugs",
|
|
2072
|
+
"user-testing": "ux",
|
|
2073
|
+
"typecheck": "types",
|
|
2074
|
+
"devops": "devops",
|
|
2075
|
+
"comprehension": "explain",
|
|
2076
|
+
"test": "test",
|
|
2077
|
+
"trie_clean": "vibe"
|
|
2078
|
+
};
|
|
2079
|
+
var TrieAgentTool = class {
|
|
2080
|
+
agentRegistry = new AgentRegistry();
|
|
2081
|
+
async execute(args) {
|
|
2082
|
+
const { agent, files, directory, depth = "standard", lookup, output = "full" } = args;
|
|
2083
|
+
if (lookup) {
|
|
2084
|
+
return this.handleKnowledgeLookup(lookup);
|
|
2085
|
+
}
|
|
2086
|
+
if (!agent) {
|
|
2087
|
+
return this.listAgents();
|
|
2088
|
+
}
|
|
2089
|
+
const agentInstance = this.agentRegistry.getAgent(agent);
|
|
2090
|
+
if (!agentInstance) {
|
|
2091
|
+
return {
|
|
2092
|
+
content: [{
|
|
2093
|
+
type: "text",
|
|
2094
|
+
text: `\u274C Agent not found: ${agent}
|
|
2095
|
+
|
|
2096
|
+
Available agents:
|
|
2097
|
+
${this.agentRegistry.getAgentNames().map((n) => `- ${n}`).join("\n")}`
|
|
2098
|
+
}]
|
|
2099
|
+
};
|
|
2100
|
+
}
|
|
2101
|
+
let filesToScan = files || [];
|
|
2102
|
+
if (!filesToScan.length) {
|
|
2103
|
+
const scanDir = directory || process.cwd();
|
|
2104
|
+
console.error(`
|
|
2105
|
+
\u{1F50D} Discovering files in: ${scanDir}`);
|
|
2106
|
+
filesToScan = await this.discoverFiles(scanDir);
|
|
2107
|
+
console.error(` Found ${filesToScan.length} files
|
|
2108
|
+
`);
|
|
2109
|
+
} else {
|
|
2110
|
+
filesToScan = filesToScan.map(
|
|
2111
|
+
(f) => isAbsolute3(f) ? f : resolve3(process.cwd(), f)
|
|
2112
|
+
);
|
|
2113
|
+
}
|
|
2114
|
+
const validFiles = filesToScan.filter((f) => existsSync4(f));
|
|
2115
|
+
if (validFiles.length === 0) {
|
|
2116
|
+
return {
|
|
2117
|
+
content: [{
|
|
2118
|
+
type: "text",
|
|
2119
|
+
text: `\u274C No valid files found to scan.`
|
|
2120
|
+
}]
|
|
2121
|
+
};
|
|
2122
|
+
}
|
|
2123
|
+
const startTime = Date.now();
|
|
2124
|
+
const aiAgentType = AGENT_TO_AI_TYPE[agent];
|
|
2125
|
+
if (aiAgentType) {
|
|
2126
|
+
return this.runAIAnalysis(
|
|
2127
|
+
aiAgentType,
|
|
2128
|
+
validFiles,
|
|
2129
|
+
agentInstance.name,
|
|
2130
|
+
agentInstance.description,
|
|
2131
|
+
depth,
|
|
2132
|
+
output
|
|
2133
|
+
);
|
|
2134
|
+
} else {
|
|
2135
|
+
return this.runStaticAnalysis(agentInstance, validFiles, startTime);
|
|
2136
|
+
}
|
|
2137
|
+
}
|
|
2138
|
+
async runAIAnalysis(agentType, files, agentName, agentDescription, depth, outputMode) {
|
|
2139
|
+
const startTime = Date.now();
|
|
2140
|
+
console.error(`
|
|
2141
|
+
\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501`);
|
|
2142
|
+
console.error(`\u{1F9E0} Running AI-POWERED ${agentName.toUpperCase()} analysis`);
|
|
2143
|
+
console.error(`\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
|
|
2144
|
+
`);
|
|
2145
|
+
console.error(`\u{1F4C4} ${agentDescription}`);
|
|
2146
|
+
console.error(`\u{1F4C2} Analyzing ${files.length} files (${depth} depth)...
|
|
2147
|
+
`);
|
|
2148
|
+
try {
|
|
2149
|
+
const analysis = await buildAnalysis({
|
|
2150
|
+
agent: agentType,
|
|
2151
|
+
files,
|
|
2152
|
+
depth
|
|
2153
|
+
});
|
|
2154
|
+
const executionTime = Date.now() - startTime;
|
|
2155
|
+
const includePrompt = outputMode === "full";
|
|
2156
|
+
let output = formatAnalysisResponse(analysis, { includePrompt });
|
|
2157
|
+
if (analysis.suggestedFollowUps.length > 0) {
|
|
2158
|
+
output += this.generateKnowledgeSuggestions(analysis.suggestedFollowUps, agentType);
|
|
2159
|
+
}
|
|
2160
|
+
output += `
|
|
2161
|
+
*Analysis completed in ${(executionTime / 1e3).toFixed(2)}s*
|
|
2162
|
+
`;
|
|
2163
|
+
return {
|
|
2164
|
+
content: [{
|
|
2165
|
+
type: "text",
|
|
2166
|
+
text: output
|
|
2167
|
+
}]
|
|
2168
|
+
};
|
|
2169
|
+
} catch (error) {
|
|
2170
|
+
return {
|
|
2171
|
+
content: [{
|
|
2172
|
+
type: "text",
|
|
2173
|
+
text: `\u274C Analysis error: ${error instanceof Error ? error.message : String(error)}`
|
|
2174
|
+
}]
|
|
2175
|
+
};
|
|
2176
|
+
}
|
|
2177
|
+
}
|
|
2178
|
+
async runStaticAnalysis(agentInstance, files, startTime) {
|
|
2179
|
+
console.error(`
|
|
2180
|
+
\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501`);
|
|
2181
|
+
console.error(`\u{1F50D} Running ${agentInstance.name.toUpperCase()} static analysis`);
|
|
2182
|
+
console.error(`\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
|
|
2183
|
+
`);
|
|
2184
|
+
console.error(`\u{1F4C4} ${agentInstance.description}`);
|
|
2185
|
+
console.error(`\u{1F4C2} Scanning ${files.length} files...
|
|
2186
|
+
`);
|
|
2187
|
+
try {
|
|
2188
|
+
const result = await agentInstance.scan(files, { workingDir: process.cwd() });
|
|
2189
|
+
const executionTime = Date.now() - startTime;
|
|
2190
|
+
return {
|
|
2191
|
+
content: [{
|
|
2192
|
+
type: "text",
|
|
2193
|
+
text: await this.formatStaticResult(agentInstance.name, result.issues, files, executionTime)
|
|
2194
|
+
}]
|
|
2195
|
+
};
|
|
2196
|
+
} catch (error) {
|
|
2197
|
+
return {
|
|
2198
|
+
content: [{
|
|
2199
|
+
type: "text",
|
|
2200
|
+
text: `\u274C Agent error: ${error instanceof Error ? error.message : String(error)}`
|
|
2201
|
+
}]
|
|
2202
|
+
};
|
|
2203
|
+
}
|
|
2204
|
+
}
|
|
2205
|
+
generateKnowledgeSuggestions(followUps, agentType) {
|
|
2206
|
+
if (followUps.length === 0) {
|
|
2207
|
+
return "";
|
|
2208
|
+
}
|
|
2209
|
+
let output = `
|
|
2210
|
+
## \u{1F517} Suggested Follow-ups
|
|
2211
|
+
|
|
2212
|
+
`;
|
|
2213
|
+
for (const followUp of followUps) {
|
|
2214
|
+
output += `- ${followUp}
|
|
2215
|
+
`;
|
|
2216
|
+
}
|
|
2217
|
+
if (agentType === "security") {
|
|
2218
|
+
output += `- **CVE Check**: Run \`trie_security lookup:"cve" query:"[library-name]"\` to check for vulnerabilities
|
|
2219
|
+
`;
|
|
2220
|
+
}
|
|
2221
|
+
return output;
|
|
2222
|
+
}
|
|
2223
|
+
handleKnowledgeLookup(lookup) {
|
|
2224
|
+
const result = lookupKnowledge({
|
|
2225
|
+
type: lookup.type,
|
|
2226
|
+
query: lookup.query,
|
|
2227
|
+
context: lookup.context ?? {}
|
|
2228
|
+
});
|
|
2229
|
+
return {
|
|
2230
|
+
content: [{
|
|
2231
|
+
type: "text",
|
|
2232
|
+
text: result
|
|
2233
|
+
}]
|
|
2234
|
+
};
|
|
2235
|
+
}
|
|
2236
|
+
listAgents() {
|
|
2237
|
+
const agents = this.agentRegistry.getAgentDescriptions();
|
|
2238
|
+
const agentList = agents.map((a) => {
|
|
2239
|
+
const command = this.getAgentCommand(a.name);
|
|
2240
|
+
const aiPowered = AGENT_TO_AI_TYPE[a.name] ? "\u{1F9E0}" : "\u{1F50D}";
|
|
2241
|
+
return `| \`${command}\` | ${aiPowered} ${a.name} | ${a.description} |`;
|
|
2242
|
+
}).join("\n");
|
|
2243
|
+
return {
|
|
2244
|
+
content: [{
|
|
2245
|
+
type: "text",
|
|
2246
|
+
text: `# \u{1F916} Available Agents
|
|
2247
|
+
|
|
2248
|
+
| Command | Agent | Description |
|
|
2249
|
+
|---------|-------|-------------|
|
|
2250
|
+
${agentList}
|
|
2251
|
+
|
|
2252
|
+
**Legend:** \u{1F9E0} = AI-powered deep analysis, \u{1F50D} = Static pattern matching
|
|
2253
|
+
|
|
2254
|
+
## Usage
|
|
2255
|
+
|
|
2256
|
+
### Run a specific agent:
|
|
2257
|
+
\`\`\`
|
|
2258
|
+
trie_security # Security vulnerabilities
|
|
2259
|
+
trie_privacy # Privacy/GDPR compliance
|
|
2260
|
+
trie_accessibility # WCAG accessibility audit
|
|
2261
|
+
trie_bugs # Bug detection
|
|
2262
|
+
trie_ux # User experience testing
|
|
2263
|
+
trie_types # Type safety analysis
|
|
2264
|
+
trie_architecture # Architecture review
|
|
2265
|
+
trie_devops # DevOps readiness
|
|
2266
|
+
trie_legal # Legal compliance
|
|
2267
|
+
\`\`\`
|
|
2268
|
+
|
|
2269
|
+
### With options:
|
|
2270
|
+
\`\`\`
|
|
2271
|
+
trie_security files:["src/auth.ts"] depth:"deep"
|
|
2272
|
+
\`\`\`
|
|
2273
|
+
|
|
2274
|
+
### Knowledge lookup:
|
|
2275
|
+
\`\`\`
|
|
2276
|
+
trie_security lookup:{type:"cve", query:"lodash"}
|
|
2277
|
+
trie_security lookup:{type:"docs", query:"XSS prevention", context:{framework:"react"}}
|
|
2278
|
+
\`\`\`
|
|
2279
|
+
|
|
2280
|
+
### Run all agents:
|
|
2281
|
+
\`\`\`
|
|
2282
|
+
trie_scan # Full scan with smart triaging
|
|
2283
|
+
\`\`\``
|
|
2284
|
+
}]
|
|
2285
|
+
};
|
|
2286
|
+
}
|
|
2287
|
+
getAgentCommand(agentName) {
|
|
2288
|
+
const commandMap = {
|
|
2289
|
+
"security": "trie_security",
|
|
2290
|
+
"privacy": "trie_privacy",
|
|
2291
|
+
"legal": "trie_legal",
|
|
2292
|
+
"design-engineer": "trie_accessibility",
|
|
2293
|
+
"software-architect": "trie_architecture",
|
|
2294
|
+
"bug-finding": "trie_bugs",
|
|
2295
|
+
"user-testing": "trie_ux",
|
|
2296
|
+
"typecheck": "trie_types",
|
|
2297
|
+
"devops": "trie_devops",
|
|
2298
|
+
"comprehension": "trie_explain",
|
|
2299
|
+
"test": "trie_test"
|
|
2300
|
+
};
|
|
2301
|
+
return commandMap[agentName] || `trie_scan --agent ${agentName}`;
|
|
2302
|
+
}
|
|
2303
|
+
async formatStaticResult(agentName, issues, files, executionTime) {
|
|
2304
|
+
const critical = issues.filter((i) => i.severity === "critical").length;
|
|
2305
|
+
const serious = issues.filter((i) => i.severity === "serious").length;
|
|
2306
|
+
const moderate = issues.filter((i) => i.severity === "moderate").length;
|
|
2307
|
+
const low = issues.filter((i) => i.severity === "low").length;
|
|
2308
|
+
const agentEmoji = this.getAgentEmoji(agentName);
|
|
2309
|
+
let output = `
|
|
2310
|
+
`;
|
|
2311
|
+
output += `# ${agentEmoji} ${agentName.toUpperCase()} SCAN
|
|
2312
|
+
|
|
2313
|
+
`;
|
|
2314
|
+
output += `**Files:** ${files.length} | **Time:** ${(executionTime / 1e3).toFixed(2)}s
|
|
2315
|
+
|
|
2316
|
+
`;
|
|
2317
|
+
if (issues.length === 0) {
|
|
2318
|
+
output += `## \u2705 No Issues Found
|
|
2319
|
+
|
|
2320
|
+
`;
|
|
2321
|
+
output += `Your code passed all ${agentName} checks.
|
|
2322
|
+
|
|
2323
|
+
`;
|
|
2324
|
+
return output;
|
|
2325
|
+
}
|
|
2326
|
+
output += `## \u{1F3AF} ${issues.length} Issues Found
|
|
2327
|
+
|
|
2328
|
+
`;
|
|
2329
|
+
if (critical > 0) output += `\u{1F534} ${critical} Critical `;
|
|
2330
|
+
if (serious > 0) output += `\u{1F7E0} ${serious} Serious `;
|
|
2331
|
+
if (moderate > 0) output += `\u{1F7E1} ${moderate} Moderate `;
|
|
2332
|
+
if (low > 0) output += `\u{1F535} ${low} Low`;
|
|
2333
|
+
output += `
|
|
2334
|
+
|
|
2335
|
+
`;
|
|
2336
|
+
const sorted = [...issues].sort((a, b) => {
|
|
2337
|
+
const severityOrder = { critical: 0, serious: 1, moderate: 2, low: 3 };
|
|
2338
|
+
if (severityOrder[a.severity] !== severityOrder[b.severity]) {
|
|
2339
|
+
return severityOrder[a.severity] - severityOrder[b.severity];
|
|
2340
|
+
}
|
|
2341
|
+
return (a.line || 0) - (b.line || 0);
|
|
2342
|
+
});
|
|
2343
|
+
for (const issue of sorted) {
|
|
2344
|
+
const icon = { critical: "\u{1F534}", serious: "\u{1F7E0}", moderate: "\u{1F7E1}", low: "\u{1F535}" }[issue.severity];
|
|
2345
|
+
output += `---
|
|
2346
|
+
|
|
2347
|
+
`;
|
|
2348
|
+
output += `${icon} **${issue.issue}**
|
|
2349
|
+
|
|
2350
|
+
`;
|
|
2351
|
+
output += `\u{1F4CD} \`${issue.file}:${issue.line || "?"}\`
|
|
2352
|
+
|
|
2353
|
+
`;
|
|
2354
|
+
const snippet = await this.getCodeSnippet(issue.file, issue.line);
|
|
2355
|
+
if (snippet) {
|
|
2356
|
+
output += `\`\`\`
|
|
2357
|
+
${snippet}
|
|
2358
|
+
\`\`\`
|
|
2359
|
+
|
|
2360
|
+
`;
|
|
2361
|
+
}
|
|
2362
|
+
output += `**Fix:** ${issue.fix}
|
|
2363
|
+
|
|
2364
|
+
`;
|
|
2365
|
+
if (issue.cwe) output += `CWE: ${issue.cwe}
|
|
2366
|
+
`;
|
|
2367
|
+
if (issue.regulation) output += `Regulation: ${issue.regulation}
|
|
2368
|
+
`;
|
|
2369
|
+
output += `<details>
|
|
2370
|
+
<summary>\u{1F4AC} Prompt to fix this</summary>
|
|
2371
|
+
|
|
2372
|
+
`;
|
|
2373
|
+
output += `\`\`\`
|
|
2374
|
+
Fix the ${issue.issue.toLowerCase()} in ${basename4(issue.file)}${issue.line ? ` at line ${issue.line}` : ""}.
|
|
2375
|
+
|
|
2376
|
+
${issue.fix}
|
|
2377
|
+
\`\`\`
|
|
2378
|
+
|
|
2379
|
+
`;
|
|
2380
|
+
output += `</details>
|
|
2381
|
+
|
|
2382
|
+
`;
|
|
2383
|
+
}
|
|
2384
|
+
output += `---
|
|
2385
|
+
`;
|
|
2386
|
+
output += `*${agentName} scan completed in ${(executionTime / 1e3).toFixed(2)}s*
|
|
2387
|
+
`;
|
|
2388
|
+
return output;
|
|
2389
|
+
}
|
|
2390
|
+
/**
|
|
2391
|
+
* Get a code snippet around a specific line
|
|
2392
|
+
*/
|
|
2393
|
+
async getCodeSnippet(filePath, line) {
|
|
2394
|
+
if (!line || !existsSync4(filePath)) return null;
|
|
2395
|
+
try {
|
|
2396
|
+
const content = await readFile4(filePath, "utf-8");
|
|
2397
|
+
const lines = content.split("\n");
|
|
2398
|
+
const start = Math.max(0, line - 3);
|
|
2399
|
+
const end = Math.min(lines.length, line + 2);
|
|
2400
|
+
return lines.slice(start, end).map((l, idx) => {
|
|
2401
|
+
const lineNum = start + idx + 1;
|
|
2402
|
+
const marker = lineNum === line ? "\u2192" : " ";
|
|
2403
|
+
return `${marker} ${lineNum.toString().padStart(4)} | ${l}`;
|
|
2404
|
+
}).join("\n");
|
|
2405
|
+
} catch {
|
|
2406
|
+
return null;
|
|
2407
|
+
}
|
|
2408
|
+
}
|
|
2409
|
+
getAgentEmoji(agentName) {
|
|
2410
|
+
const emojis = {
|
|
2411
|
+
"security": "\u{1F512}",
|
|
2412
|
+
"privacy": "\u{1F464}",
|
|
2413
|
+
"legal": "\u2696\uFE0F",
|
|
2414
|
+
"design-engineer": "\u267F",
|
|
2415
|
+
"software-architect": "\u{1F3D7}\uFE0F",
|
|
2416
|
+
"bug-finding": "\u{1F41B}",
|
|
2417
|
+
"user-testing": "\u{1F3AF}",
|
|
2418
|
+
"typecheck": "\u{1F4DD}",
|
|
2419
|
+
"devops": "\u2699\uFE0F",
|
|
2420
|
+
"comprehension": "\u{1F4D6}",
|
|
2421
|
+
"test": "\u{1F9EA}"
|
|
2422
|
+
};
|
|
2423
|
+
return emojis[agentName] || "\u{1F916}";
|
|
2424
|
+
}
|
|
2425
|
+
async discoverFiles(dir, maxFiles = 200) {
|
|
2426
|
+
const files = [];
|
|
2427
|
+
async function walk(currentDir) {
|
|
2428
|
+
if (files.length >= maxFiles) return;
|
|
2429
|
+
try {
|
|
2430
|
+
const entries = await readdir(currentDir, { withFileTypes: true });
|
|
2431
|
+
for (const entry of entries) {
|
|
2432
|
+
if (files.length >= maxFiles) break;
|
|
2433
|
+
const fullPath = join3(currentDir, entry.name);
|
|
2434
|
+
if (entry.isDirectory()) {
|
|
2435
|
+
if (!SKIP_DIRS2.has(entry.name) && !entry.name.startsWith(".")) {
|
|
2436
|
+
await walk(fullPath);
|
|
2437
|
+
}
|
|
2438
|
+
} else if (entry.isFile()) {
|
|
2439
|
+
const ext = extname5(entry.name).toLowerCase();
|
|
2440
|
+
if (SCANNABLE_EXTENSIONS.has(ext)) {
|
|
2441
|
+
files.push(fullPath);
|
|
2442
|
+
}
|
|
2443
|
+
}
|
|
2444
|
+
}
|
|
2445
|
+
} catch (error) {
|
|
2446
|
+
}
|
|
2447
|
+
}
|
|
2448
|
+
await walk(dir);
|
|
2449
|
+
return files;
|
|
2450
|
+
}
|
|
2451
|
+
};
|
|
2452
|
+
|
|
2453
|
+
// src/tools/create-agent.ts
|
|
2454
|
+
import { existsSync as existsSync5 } from "fs";
|
|
2455
|
+
import { mkdir, writeFile } from "fs/promises";
|
|
2456
|
+
import { join as join4, basename as basename5, extname as extname6 } from "path";
|
|
2457
|
+
var TrieCreateAgentTool = class {
|
|
2458
|
+
async execute(args) {
|
|
2459
|
+
const { filePath, documentContent, agentName, displayName, description, category } = args;
|
|
2460
|
+
if (!agentName) {
|
|
2461
|
+
return this.errorResponse("Missing required parameter: agentName");
|
|
2462
|
+
}
|
|
2463
|
+
if (!filePath && !documentContent) {
|
|
2464
|
+
return this.errorResponse(
|
|
2465
|
+
"Provide either filePath (path to PDF/TXT/MD file) or documentContent (raw text from drag-and-drop)"
|
|
2466
|
+
);
|
|
2467
|
+
}
|
|
2468
|
+
try {
|
|
2469
|
+
let rawText;
|
|
2470
|
+
let title = agentName;
|
|
2471
|
+
let wordCount = 0;
|
|
2472
|
+
if (filePath) {
|
|
2473
|
+
if (!existsSync5(filePath)) {
|
|
2474
|
+
return this.errorResponse(`File not found: ${filePath}`);
|
|
2475
|
+
}
|
|
2476
|
+
const ext = filePath.toLowerCase().split(".").pop();
|
|
2477
|
+
if (!["pdf", "txt", "md", "markdown", "rtf"].includes(ext || "")) {
|
|
2478
|
+
return this.errorResponse(
|
|
2479
|
+
`Unsupported file type: .${ext}
|
|
2480
|
+
Supported types: .pdf, .txt, .md, .rtf`
|
|
2481
|
+
);
|
|
2482
|
+
}
|
|
2483
|
+
const document = await parseDocument(filePath);
|
|
2484
|
+
rawText = document.rawText;
|
|
2485
|
+
title = document.metadata.title || basename5(filePath, extname6(filePath));
|
|
2486
|
+
wordCount = document.metadata.wordCount;
|
|
2487
|
+
} else {
|
|
2488
|
+
rawText = documentContent;
|
|
2489
|
+
wordCount = rawText.split(/\s+/).filter((w) => w.length > 0).length;
|
|
2490
|
+
const firstLine = rawText.split("\n")[0]?.trim();
|
|
2491
|
+
if (firstLine && firstLine.length < 100) {
|
|
2492
|
+
title = firstLine;
|
|
2493
|
+
}
|
|
2494
|
+
}
|
|
2495
|
+
const chunks = this.chunkText(rawText, 6e3);
|
|
2496
|
+
const extractionPrompt = this.buildExtractionPrompt(
|
|
2497
|
+
chunks,
|
|
2498
|
+
title,
|
|
2499
|
+
agentName,
|
|
2500
|
+
category,
|
|
2501
|
+
displayName,
|
|
2502
|
+
description
|
|
2503
|
+
);
|
|
2504
|
+
return {
|
|
2505
|
+
content: [
|
|
2506
|
+
{
|
|
2507
|
+
type: "text",
|
|
2508
|
+
text: this.formatExtractionRequest(
|
|
2509
|
+
agentName,
|
|
2510
|
+
title,
|
|
2511
|
+
wordCount,
|
|
2512
|
+
chunks.length,
|
|
2513
|
+
extractionPrompt
|
|
2514
|
+
)
|
|
2515
|
+
}
|
|
2516
|
+
]
|
|
2517
|
+
};
|
|
2518
|
+
} catch (error) {
|
|
2519
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
2520
|
+
return this.errorResponse(`Failed to parse document: ${errorMessage}`);
|
|
2521
|
+
}
|
|
2522
|
+
}
|
|
2523
|
+
/**
|
|
2524
|
+
* Chunk text into manageable pieces
|
|
2525
|
+
*/
|
|
2526
|
+
chunkText(text, maxChunkSize) {
|
|
2527
|
+
if (text.length <= maxChunkSize) {
|
|
2528
|
+
return [text];
|
|
2529
|
+
}
|
|
2530
|
+
const chunks = [];
|
|
2531
|
+
const paragraphs = text.split(/\n\s*\n/);
|
|
2532
|
+
let currentChunk = "";
|
|
2533
|
+
for (const para of paragraphs) {
|
|
2534
|
+
if (currentChunk.length + para.length + 2 > maxChunkSize) {
|
|
2535
|
+
if (currentChunk) chunks.push(currentChunk.trim());
|
|
2536
|
+
currentChunk = para;
|
|
2537
|
+
} else {
|
|
2538
|
+
currentChunk += (currentChunk ? "\n\n" : "") + para;
|
|
2539
|
+
}
|
|
2540
|
+
}
|
|
2541
|
+
if (currentChunk) chunks.push(currentChunk.trim());
|
|
2542
|
+
return chunks;
|
|
2543
|
+
}
|
|
2544
|
+
/**
|
|
2545
|
+
* Build the extraction prompt for Claude
|
|
2546
|
+
*/
|
|
2547
|
+
buildExtractionPrompt(chunks, title, agentName, category, displayName, description) {
|
|
2548
|
+
const prefix = agentName.toUpperCase().replace(/[^A-Z]/g, "").slice(0, 4) || "CUST";
|
|
2549
|
+
const documentContent = chunks.length === 1 ? chunks[0] : chunks.map((c, i) => `--- SECTION ${i + 1}/${chunks.length} ---
|
|
2550
|
+
${c}`).join("\n\n");
|
|
2551
|
+
return `# Create Code Review Agent: "${displayName || this.formatDisplayName(agentName)}"
|
|
2552
|
+
|
|
2553
|
+
## Your Task
|
|
2554
|
+
Analyze the following document and extract structured knowledge to create a code review agent.
|
|
2555
|
+
|
|
2556
|
+
## Document Information
|
|
2557
|
+
- **Title**: ${title}
|
|
2558
|
+
- **Agent Name**: ${agentName}
|
|
2559
|
+
- **Category**: ${category || "auto-detect"}
|
|
2560
|
+
${description ? `- **Description**: ${description}` : ""}
|
|
2561
|
+
|
|
2562
|
+
## Document Content
|
|
2563
|
+
${documentContent}
|
|
2564
|
+
|
|
2565
|
+
---
|
|
2566
|
+
|
|
2567
|
+
## Instructions
|
|
2568
|
+
|
|
2569
|
+
Please analyze this document and produce a **single JSON object** with the following structure. This will be used to create a custom code review agent.
|
|
2570
|
+
|
|
2571
|
+
**IMPORTANT**: Your response should be ONLY the JSON object, no other text.
|
|
2572
|
+
|
|
2573
|
+
\`\`\`json
|
|
2574
|
+
{
|
|
2575
|
+
"agentConfig": {
|
|
2576
|
+
"name": "${this.sanitizeAgentName(agentName)}",
|
|
2577
|
+
"displayName": "${displayName || this.formatDisplayName(agentName)}",
|
|
2578
|
+
"description": "${description || `Code review agent based on ${title}`}",
|
|
2579
|
+
"version": "1.0.0",
|
|
2580
|
+
"category": "string (technical | legal | policy | security | architecture | general)"
|
|
2581
|
+
},
|
|
2582
|
+
"knowledge": {
|
|
2583
|
+
"domain": "string (technical | legal | policy | security | architecture | general)",
|
|
2584
|
+
"summary": "2-3 paragraph summary of the document's key insights for code review",
|
|
2585
|
+
"coreConcepts": [
|
|
2586
|
+
{
|
|
2587
|
+
"name": "string",
|
|
2588
|
+
"description": "string",
|
|
2589
|
+
"importance": "critical | important | supplementary",
|
|
2590
|
+
"keywords": ["string"]
|
|
2591
|
+
}
|
|
2592
|
+
],
|
|
2593
|
+
"bestPractices": [
|
|
2594
|
+
{
|
|
2595
|
+
"name": "string",
|
|
2596
|
+
"description": "string",
|
|
2597
|
+
"rationale": "why this is important",
|
|
2598
|
+
"codeExample": "optional code example or null"
|
|
2599
|
+
}
|
|
2600
|
+
],
|
|
2601
|
+
"antiPatterns": [
|
|
2602
|
+
{
|
|
2603
|
+
"name": "string",
|
|
2604
|
+
"description": "string",
|
|
2605
|
+
"whyBad": "why to avoid",
|
|
2606
|
+
"betterAlternative": "what to do instead"
|
|
2607
|
+
}
|
|
2608
|
+
],
|
|
2609
|
+
"glossary": {
|
|
2610
|
+
"term": "definition"
|
|
2611
|
+
}
|
|
2612
|
+
},
|
|
2613
|
+
"detectionRules": [
|
|
2614
|
+
{
|
|
2615
|
+
"id": "${prefix}-001",
|
|
2616
|
+
"name": "Rule Name",
|
|
2617
|
+
"description": "What this rule detects",
|
|
2618
|
+
"severity": "critical | serious | moderate | low | info",
|
|
2619
|
+
"patterns": {
|
|
2620
|
+
"regex": ["JavaScript regex patterns"],
|
|
2621
|
+
"keywords": ["words that indicate this issue"],
|
|
2622
|
+
"semantic": "Natural language description for AI detection"
|
|
2623
|
+
},
|
|
2624
|
+
"fix": {
|
|
2625
|
+
"description": "How to fix this issue",
|
|
2626
|
+
"example": "Code example or null",
|
|
2627
|
+
"autoFixable": false
|
|
2628
|
+
},
|
|
2629
|
+
"category": "string"
|
|
2630
|
+
}
|
|
2631
|
+
],
|
|
2632
|
+
"prompts": {
|
|
2633
|
+
"systemPrompt": "You are an expert code reviewer specializing in [topic]. Your role is to...",
|
|
2634
|
+
"analysisPrompt": "Review this code for issues related to [topic]. Look for: ...\\n\\nCode:\\n\\\`\\\`\\\`{{language}}\\n{{code}}\\n\\\`\\\`\\\`\\n\\nFile: {{filePath}}",
|
|
2635
|
+
"fixPrompt": "Fix this issue: {{issue}}\\n\\nCode:\\n\\\`\\\`\\\`{{language}}\\n{{code}}\\n\\\`\\\`\\\`"
|
|
2636
|
+
}
|
|
2637
|
+
}
|
|
2638
|
+
\`\`\`
|
|
2639
|
+
|
|
2640
|
+
## Guidelines
|
|
2641
|
+
|
|
2642
|
+
1. **Core Concepts**: Extract 10-20 key concepts that are fundamental to the material
|
|
2643
|
+
2. **Best Practices**: Extract 10-15 recommended approaches with rationale
|
|
2644
|
+
3. **Anti-Patterns**: Extract 10-15 things to avoid with explanations
|
|
2645
|
+
4. **Detection Rules**: Generate 15-30 rules with regex patterns that could detect issues in code
|
|
2646
|
+
5. **Prompts**: Create prompts that embody the expertise from this document
|
|
2647
|
+
|
|
2648
|
+
Focus on extracting actionable, code-reviewable knowledge. The agent should be able to find violations of the principles in this document.
|
|
2649
|
+
|
|
2650
|
+
**Output ONLY the JSON object, starting with \`{\` and ending with \`}\`.**`;
|
|
2651
|
+
}
|
|
2652
|
+
/**
|
|
2653
|
+
* Format the extraction request output
|
|
2654
|
+
*/
|
|
2655
|
+
formatExtractionRequest(agentName, title, wordCount, chunkCount, prompt) {
|
|
2656
|
+
return `
|
|
2657
|
+
\u{1F4DA} **Document Parsed Successfully!**
|
|
2658
|
+
|
|
2659
|
+
${"\u2501".repeat(50)}
|
|
2660
|
+
|
|
2661
|
+
**Creating Agent:** \`${agentName}\`
|
|
2662
|
+
**Source Document:** ${title}
|
|
2663
|
+
**Word Count:** ${wordCount.toLocaleString()}
|
|
2664
|
+
**Processing Chunks:** ${chunkCount}
|
|
2665
|
+
|
|
2666
|
+
${"\u2501".repeat(50)}
|
|
2667
|
+
|
|
2668
|
+
## Next Step
|
|
2669
|
+
|
|
2670
|
+
I've prepared the document for analysis. Please process the following prompt to extract the knowledge, then pass the result to \`trie_save_agent\` to save the agent.
|
|
2671
|
+
|
|
2672
|
+
${"\u2500".repeat(50)}
|
|
2673
|
+
|
|
2674
|
+
${prompt}
|
|
2675
|
+
`;
|
|
2676
|
+
}
|
|
2677
|
+
sanitizeAgentName(name) {
|
|
2678
|
+
return name.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
|
|
2679
|
+
}
|
|
2680
|
+
formatDisplayName(name) {
|
|
2681
|
+
return name.split(/[-_]/).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
2682
|
+
}
|
|
2683
|
+
errorResponse(message) {
|
|
2684
|
+
return {
|
|
2685
|
+
content: [
|
|
2686
|
+
{
|
|
2687
|
+
type: "text",
|
|
2688
|
+
text: `\u274C **Error:** ${message}`
|
|
2689
|
+
}
|
|
2690
|
+
],
|
|
2691
|
+
isError: true
|
|
2692
|
+
};
|
|
2693
|
+
}
|
|
2694
|
+
};
|
|
2695
|
+
var TrieSaveAgentTool = class {
|
|
2696
|
+
async execute(args) {
|
|
2697
|
+
const { agentConfig, knowledge, detectionRules, prompts, sourceFile } = args;
|
|
2698
|
+
if (!agentConfig?.name) {
|
|
2699
|
+
return this.errorResponse("Missing agentConfig.name");
|
|
2700
|
+
}
|
|
2701
|
+
if (!knowledge?.summary) {
|
|
2702
|
+
return this.errorResponse("Missing knowledge.summary");
|
|
2703
|
+
}
|
|
2704
|
+
if (!prompts?.systemPrompt) {
|
|
2705
|
+
return this.errorResponse("Missing prompts.systemPrompt");
|
|
2706
|
+
}
|
|
2707
|
+
try {
|
|
2708
|
+
const fullConfig = {
|
|
2709
|
+
name: this.sanitizeAgentName(agentConfig.name),
|
|
2710
|
+
displayName: agentConfig.displayName || this.formatDisplayName(agentConfig.name),
|
|
2711
|
+
description: agentConfig.description,
|
|
2712
|
+
version: agentConfig.version || "1.0.0",
|
|
2713
|
+
category: agentConfig.category || knowledge.domain || "general",
|
|
2714
|
+
source: {
|
|
2715
|
+
type: "document",
|
|
2716
|
+
originalFile: sourceFile || "user-provided",
|
|
2717
|
+
fileType: "txt",
|
|
2718
|
+
compressedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2719
|
+
},
|
|
2720
|
+
systemPrompt: prompts.systemPrompt,
|
|
2721
|
+
analysisPrompt: prompts.analysisPrompt,
|
|
2722
|
+
fixPrompt: prompts.fixPrompt,
|
|
2723
|
+
activationRules: this.buildActivationRules(knowledge, detectionRules),
|
|
2724
|
+
patterns: detectionRules.map((rule, i) => {
|
|
2725
|
+
const fixObj = {
|
|
2726
|
+
description: rule.fix?.description || "Review and fix manually",
|
|
2727
|
+
autoFixable: rule.fix?.autoFixable || false
|
|
2728
|
+
};
|
|
2729
|
+
if (rule.fix?.example) {
|
|
2730
|
+
fixObj.example = rule.fix.example;
|
|
2731
|
+
}
|
|
2732
|
+
return {
|
|
2733
|
+
id: rule.id || `CUST-${String(i + 1).padStart(3, "0")}`,
|
|
2734
|
+
name: rule.name,
|
|
2735
|
+
description: rule.description,
|
|
2736
|
+
severity: rule.severity || "moderate",
|
|
2737
|
+
patterns: {
|
|
2738
|
+
regex: rule.patterns?.regex || [],
|
|
2739
|
+
keywords: rule.patterns?.keywords || [],
|
|
2740
|
+
semantic: rule.patterns?.semantic || ""
|
|
2741
|
+
},
|
|
2742
|
+
fix: fixObj,
|
|
2743
|
+
category: rule.category || agentConfig.category
|
|
2744
|
+
};
|
|
2745
|
+
}),
|
|
2746
|
+
knowledge: {
|
|
2747
|
+
domain: knowledge.domain || "general",
|
|
2748
|
+
summary: knowledge.summary,
|
|
2749
|
+
coreConcepts: knowledge.coreConcepts.map((c) => ({
|
|
2750
|
+
name: c.name,
|
|
2751
|
+
description: c.description,
|
|
2752
|
+
importance: c.importance || "important",
|
|
2753
|
+
relatedPatterns: c.relatedPatterns || [],
|
|
2754
|
+
keywords: c.keywords || []
|
|
2755
|
+
})),
|
|
2756
|
+
bestPractices: knowledge.bestPractices.map((bp) => {
|
|
2757
|
+
const practice = {
|
|
2758
|
+
name: bp.name,
|
|
2759
|
+
description: bp.description,
|
|
2760
|
+
rationale: bp.rationale,
|
|
2761
|
+
category: bp.category || agentConfig.category
|
|
2762
|
+
};
|
|
2763
|
+
if (bp.codeExample) {
|
|
2764
|
+
practice.codeExample = bp.codeExample;
|
|
2765
|
+
}
|
|
2766
|
+
return practice;
|
|
2767
|
+
}),
|
|
2768
|
+
antiPatterns: knowledge.antiPatterns.map((ap) => ({
|
|
2769
|
+
name: ap.name,
|
|
2770
|
+
description: ap.description,
|
|
2771
|
+
whyBad: ap.whyBad,
|
|
2772
|
+
betterAlternative: ap.betterAlternative
|
|
2773
|
+
})),
|
|
2774
|
+
detectionRules: [],
|
|
2775
|
+
glossary: knowledge.glossary || {},
|
|
2776
|
+
sourceDocument: {
|
|
2777
|
+
title: agentConfig.displayName || agentConfig.name,
|
|
2778
|
+
wordCount: 0,
|
|
2779
|
+
compressionRatio: 0
|
|
2780
|
+
}
|
|
2781
|
+
}
|
|
2782
|
+
};
|
|
2783
|
+
const configPath = await this.saveAgentConfig(fullConfig);
|
|
2784
|
+
return {
|
|
2785
|
+
content: [
|
|
2786
|
+
{
|
|
2787
|
+
type: "text",
|
|
2788
|
+
text: this.formatSuccessResponse(fullConfig, configPath)
|
|
2789
|
+
}
|
|
2790
|
+
]
|
|
2791
|
+
};
|
|
2792
|
+
} catch (error) {
|
|
2793
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
2794
|
+
return this.errorResponse(`Failed to save agent: ${errorMessage}`);
|
|
2795
|
+
}
|
|
2796
|
+
}
|
|
2797
|
+
buildActivationRules(knowledge, detectionRules) {
|
|
2798
|
+
const domainRules = {
|
|
2799
|
+
technical: {
|
|
2800
|
+
filePatterns: ["*.ts", "*.tsx", "*.js", "*.jsx", "*.py", "*.go", "*.rs"],
|
|
2801
|
+
contextSignals: ["touchesUI", "touchesAPI"],
|
|
2802
|
+
priority: 2
|
|
2803
|
+
},
|
|
2804
|
+
legal: {
|
|
2805
|
+
filePatterns: ["*"],
|
|
2806
|
+
contextSignals: ["touchesUserData", "touchesAuth", "touchesPayments"],
|
|
2807
|
+
priority: 2
|
|
2808
|
+
},
|
|
2809
|
+
policy: {
|
|
2810
|
+
filePatterns: ["*"],
|
|
2811
|
+
contextSignals: ["touchesAuth", "touchesAPI", "touchesDatabase"],
|
|
2812
|
+
priority: 3
|
|
2813
|
+
},
|
|
2814
|
+
security: {
|
|
2815
|
+
filePatterns: ["*"],
|
|
2816
|
+
contextSignals: ["touchesAuth", "touchesCrypto", "touchesAPI", "touchesDatabase"],
|
|
2817
|
+
priority: 1
|
|
2818
|
+
},
|
|
2819
|
+
architecture: {
|
|
2820
|
+
filePatterns: ["*.ts", "*.tsx", "*.js", "*.jsx", "*.py", "*.go"],
|
|
2821
|
+
contextSignals: ["touchesAPI", "touchesDatabase"],
|
|
2822
|
+
priority: 2
|
|
2823
|
+
},
|
|
2824
|
+
general: {
|
|
2825
|
+
filePatterns: ["*"],
|
|
2826
|
+
contextSignals: [],
|
|
2827
|
+
priority: 3
|
|
2828
|
+
}
|
|
2829
|
+
};
|
|
2830
|
+
const defaults = domainRules[knowledge.domain] || domainRules.general;
|
|
2831
|
+
const contentPatterns = [];
|
|
2832
|
+
for (const rule of detectionRules) {
|
|
2833
|
+
if (rule.patterns?.keywords) {
|
|
2834
|
+
contentPatterns.push(...rule.patterns.keywords.slice(0, 3));
|
|
2835
|
+
}
|
|
2836
|
+
}
|
|
2837
|
+
for (const concept of knowledge.coreConcepts.slice(0, 5)) {
|
|
2838
|
+
if (concept.keywords) {
|
|
2839
|
+
contentPatterns.push(...concept.keywords.slice(0, 2));
|
|
2840
|
+
}
|
|
2841
|
+
}
|
|
2842
|
+
return {
|
|
2843
|
+
filePatterns: defaults.filePatterns || ["*"],
|
|
2844
|
+
contentPatterns: [...new Set(contentPatterns)].slice(0, 20),
|
|
2845
|
+
contextSignals: defaults.contextSignals || [],
|
|
2846
|
+
minConfidence: 0.3,
|
|
2847
|
+
priority: defaults.priority || 2
|
|
2848
|
+
};
|
|
2849
|
+
}
|
|
2850
|
+
async saveAgentConfig(config) {
|
|
2851
|
+
const trieDir = join4(process.cwd(), ".trie", "agents");
|
|
2852
|
+
await mkdir(trieDir, { recursive: true });
|
|
2853
|
+
const configPath = join4(trieDir, `${config.name}.json`);
|
|
2854
|
+
await writeFile(configPath, JSON.stringify(config, null, 2));
|
|
2855
|
+
return configPath;
|
|
2856
|
+
}
|
|
2857
|
+
formatSuccessResponse(config, configPath) {
|
|
2858
|
+
return `
|
|
2859
|
+
\u2705 **Agent Created Successfully!**
|
|
2860
|
+
|
|
2861
|
+
${"\u2501".repeat(50)}
|
|
2862
|
+
|
|
2863
|
+
**Agent Name:** \`${config.name}\`
|
|
2864
|
+
**Display Name:** ${config.displayName}
|
|
2865
|
+
**Category:** ${config.category}
|
|
2866
|
+
**Config Saved To:** \`${configPath}\`
|
|
2867
|
+
|
|
2868
|
+
\u{1F4CA} **Statistics:**
|
|
2869
|
+
\u2022 Core concepts: ${config.knowledge.coreConcepts.length}
|
|
2870
|
+
\u2022 Best practices: ${config.knowledge.bestPractices.length}
|
|
2871
|
+
\u2022 Anti-patterns: ${config.knowledge.antiPatterns.length}
|
|
2872
|
+
\u2022 Detection rules: ${config.patterns.length}
|
|
2873
|
+
|
|
2874
|
+
\u{1F680} **Next Steps:**
|
|
2875
|
+
1. The agent is now registered and will activate during scans
|
|
2876
|
+
2. Run \`trie_scan\` to test the new agent
|
|
2877
|
+
3. Edit \`${configPath}\` to customize detection rules
|
|
2878
|
+
|
|
2879
|
+
\u{1F4A1} **Usage:**
|
|
2880
|
+
The agent will automatically activate when scanning code that matches its patterns.
|
|
2881
|
+
Use \`trie_list_agents\` to see all registered agents.
|
|
2882
|
+
`.trim();
|
|
2883
|
+
}
|
|
2884
|
+
sanitizeAgentName(name) {
|
|
2885
|
+
return name.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
|
|
2886
|
+
}
|
|
2887
|
+
formatDisplayName(name) {
|
|
2888
|
+
return name.split(/[-_]/).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
2889
|
+
}
|
|
2890
|
+
errorResponse(message) {
|
|
2891
|
+
return {
|
|
2892
|
+
content: [
|
|
2893
|
+
{
|
|
2894
|
+
type: "text",
|
|
2895
|
+
text: `\u274C **Error:** ${message}`
|
|
2896
|
+
}
|
|
2897
|
+
],
|
|
2898
|
+
isError: true
|
|
2899
|
+
};
|
|
2900
|
+
}
|
|
2901
|
+
};
|
|
2902
|
+
var TrieListAgentsTool = class {
|
|
2903
|
+
async execute(args) {
|
|
2904
|
+
const { includeBuiltin = true } = args;
|
|
2905
|
+
try {
|
|
2906
|
+
const customAgentNames = await listCustomAgents();
|
|
2907
|
+
const customAgents = await Promise.all(
|
|
2908
|
+
customAgentNames.map(async (name) => {
|
|
2909
|
+
const config = await loadAgentConfig(name);
|
|
2910
|
+
return config ? {
|
|
2911
|
+
name: config.name,
|
|
2912
|
+
displayName: config.displayName,
|
|
2913
|
+
category: config.category,
|
|
2914
|
+
source: config.source.originalFile,
|
|
2915
|
+
patterns: config.patterns.length,
|
|
2916
|
+
isCustom: true
|
|
2917
|
+
} : null;
|
|
2918
|
+
})
|
|
2919
|
+
);
|
|
2920
|
+
const validCustomAgents = customAgents.filter(Boolean);
|
|
2921
|
+
const builtinAgents = includeBuiltin ? [
|
|
2922
|
+
{ name: "security", displayName: "Security Agent", category: "security", isCustom: false },
|
|
2923
|
+
{ name: "privacy", displayName: "Privacy Agent", category: "privacy", isCustom: false },
|
|
2924
|
+
{ name: "legal", displayName: "Legal Agent", category: "compliance", isCustom: false },
|
|
2925
|
+
{ name: "typecheck", displayName: "TypeCheck Agent", category: "quality", isCustom: false },
|
|
2926
|
+
{ name: "comprehension", displayName: "Comprehension Agent", category: "communication", isCustom: false },
|
|
2927
|
+
{ name: "design-engineer", displayName: "Design Engineer Agent", category: "accessibility", isCustom: false },
|
|
2928
|
+
{ name: "test", displayName: "Test Agent", category: "testing", isCustom: false },
|
|
2929
|
+
{ name: "software-architect", displayName: "Software Architect Agent", category: "architecture", isCustom: false },
|
|
2930
|
+
{ name: "devops", displayName: "DevOps Agent", category: "devops", isCustom: false },
|
|
2931
|
+
{ name: "bug-finding", displayName: "Bug Finding Agent", category: "quality", isCustom: false },
|
|
2932
|
+
{ name: "user-testing", displayName: "User Testing Agent", category: "ux", isCustom: false },
|
|
2933
|
+
{ name: "trie_clean", displayName: "Trie Clean Agent", category: "ai-code", isCustom: false }
|
|
2934
|
+
] : [];
|
|
2935
|
+
let response = `# \u{1F916} Registered Agents
|
|
2936
|
+
|
|
2937
|
+
`;
|
|
2938
|
+
if (builtinAgents.length > 0) {
|
|
2939
|
+
response += `## Built-in Agents (${builtinAgents.length})
|
|
2940
|
+
|
|
2941
|
+
`;
|
|
2942
|
+
for (const agent of builtinAgents) {
|
|
2943
|
+
response += `- **${agent.displayName}** (\`${agent.name}\`) - ${agent.category}
|
|
2944
|
+
`;
|
|
2945
|
+
}
|
|
2946
|
+
response += "\n";
|
|
2947
|
+
}
|
|
2948
|
+
response += `## Custom Agents (${validCustomAgents.length})
|
|
2949
|
+
|
|
2950
|
+
`;
|
|
2951
|
+
if (validCustomAgents.length === 0) {
|
|
2952
|
+
response += `_No custom agents created yet._
|
|
2953
|
+
|
|
2954
|
+
`;
|
|
2955
|
+
response += `\u{1F4A1} Create one with: \`trie_create_agent\`
|
|
2956
|
+
`;
|
|
2957
|
+
response += ` - Provide a PDF, TXT, or MD file path
|
|
2958
|
+
`;
|
|
2959
|
+
response += ` - Or paste/drag document content directly
|
|
2960
|
+
`;
|
|
2961
|
+
} else {
|
|
2962
|
+
for (const agent of validCustomAgents) {
|
|
2963
|
+
if (agent) {
|
|
2964
|
+
response += `- **${agent.displayName}** (\`${agent.name}\`)
|
|
2965
|
+
`;
|
|
2966
|
+
response += ` - Category: ${agent.category}
|
|
2967
|
+
`;
|
|
2968
|
+
response += ` - Patterns: ${agent.patterns}
|
|
2969
|
+
`;
|
|
2970
|
+
response += ` - Source: ${agent.source}
|
|
2971
|
+
|
|
2972
|
+
`;
|
|
2973
|
+
}
|
|
2974
|
+
}
|
|
2975
|
+
}
|
|
2976
|
+
return {
|
|
2977
|
+
content: [
|
|
2978
|
+
{
|
|
2979
|
+
type: "text",
|
|
2980
|
+
text: response
|
|
2981
|
+
}
|
|
2982
|
+
]
|
|
2983
|
+
};
|
|
2984
|
+
} catch (error) {
|
|
2985
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
2986
|
+
return {
|
|
2987
|
+
content: [
|
|
2988
|
+
{
|
|
2989
|
+
type: "text",
|
|
2990
|
+
text: `\u274C Error listing agents: ${errorMessage}`
|
|
2991
|
+
}
|
|
2992
|
+
],
|
|
2993
|
+
isError: true
|
|
2994
|
+
};
|
|
2995
|
+
}
|
|
2996
|
+
}
|
|
2997
|
+
};
|
|
2998
|
+
|
|
2999
|
+
// src/utils/ai-tool-detector.ts
|
|
3000
|
+
function detectAITool() {
|
|
3001
|
+
if (process.env.CLAUDE_CODE_VERSION) {
|
|
3002
|
+
return {
|
|
3003
|
+
name: "claude-code",
|
|
3004
|
+
preferredOutputFormat: "markdown",
|
|
3005
|
+
supportsDiffs: true,
|
|
3006
|
+
supportsInlineActions: true
|
|
3007
|
+
};
|
|
3008
|
+
}
|
|
3009
|
+
if (process.env.CURSOR_IDE) {
|
|
3010
|
+
return {
|
|
3011
|
+
name: "cursor",
|
|
3012
|
+
preferredOutputFormat: "rich-text",
|
|
3013
|
+
supportsDiffs: true,
|
|
3014
|
+
supportsInlineActions: false
|
|
3015
|
+
};
|
|
3016
|
+
}
|
|
3017
|
+
if (process.env.OPENCODE_INSTANCE) {
|
|
3018
|
+
return {
|
|
3019
|
+
name: "opencode",
|
|
3020
|
+
preferredOutputFormat: "plain-text",
|
|
3021
|
+
supportsDiffs: false,
|
|
3022
|
+
supportsInlineActions: false
|
|
3023
|
+
};
|
|
3024
|
+
}
|
|
3025
|
+
const parentProcess = process.env.PARENT_PROCESS_NAME;
|
|
3026
|
+
if (parentProcess?.toLowerCase().includes("claude")) {
|
|
3027
|
+
return {
|
|
3028
|
+
name: "claude-code",
|
|
3029
|
+
preferredOutputFormat: "markdown",
|
|
3030
|
+
supportsDiffs: true,
|
|
3031
|
+
supportsInlineActions: true
|
|
3032
|
+
};
|
|
3033
|
+
}
|
|
3034
|
+
if (parentProcess?.toLowerCase().includes("cursor")) {
|
|
3035
|
+
return {
|
|
3036
|
+
name: "cursor",
|
|
3037
|
+
preferredOutputFormat: "rich-text",
|
|
3038
|
+
supportsDiffs: true,
|
|
3039
|
+
supportsInlineActions: false
|
|
3040
|
+
};
|
|
3041
|
+
}
|
|
3042
|
+
if (parentProcess?.toLowerCase().includes("opencode")) {
|
|
3043
|
+
return {
|
|
3044
|
+
name: "opencode",
|
|
3045
|
+
preferredOutputFormat: "plain-text",
|
|
3046
|
+
supportsDiffs: false,
|
|
3047
|
+
supportsInlineActions: false
|
|
3048
|
+
};
|
|
3049
|
+
}
|
|
3050
|
+
return {
|
|
3051
|
+
name: "unknown",
|
|
3052
|
+
preferredOutputFormat: "markdown",
|
|
3053
|
+
supportsDiffs: true,
|
|
3054
|
+
supportsInlineActions: false
|
|
3055
|
+
};
|
|
3056
|
+
}
|
|
3057
|
+
|
|
3058
|
+
// src/config/loader.ts
|
|
3059
|
+
import { readFile as readFile5 } from "fs/promises";
|
|
3060
|
+
import { join as join5 } from "path";
|
|
3061
|
+
|
|
3062
|
+
// src/config/defaults.ts
|
|
3063
|
+
var DEFAULT_CONFIG = {
|
|
3064
|
+
version: "1.0",
|
|
3065
|
+
triaging: {
|
|
3066
|
+
enabled: true,
|
|
3067
|
+
riskThresholds: {
|
|
3068
|
+
critical: 70,
|
|
3069
|
+
high: 40,
|
|
3070
|
+
medium: 20
|
|
3071
|
+
},
|
|
3072
|
+
autoFixConfidence: 0.95
|
|
3073
|
+
},
|
|
3074
|
+
agents: {
|
|
3075
|
+
builtin: {
|
|
3076
|
+
security: { enabled: true },
|
|
3077
|
+
privacy: { enabled: true },
|
|
3078
|
+
legal: { enabled: true },
|
|
3079
|
+
"design-engineer": { enabled: true },
|
|
3080
|
+
"software-architect": { enabled: true },
|
|
3081
|
+
comprehension: { enabled: true },
|
|
3082
|
+
devops: { enabled: true },
|
|
3083
|
+
git: { enabled: true },
|
|
3084
|
+
typecheck: { enabled: true },
|
|
3085
|
+
test: { enabled: true },
|
|
3086
|
+
"user-testing": { enabled: true },
|
|
3087
|
+
"bug-finding": { enabled: true }
|
|
3088
|
+
},
|
|
3089
|
+
custom: []
|
|
3090
|
+
},
|
|
3091
|
+
ai: {
|
|
3092
|
+
provider: "hybrid",
|
|
3093
|
+
cloudModel: "claude-sonnet-4-20250514",
|
|
3094
|
+
localModel: "codellama:13b",
|
|
3095
|
+
useLocalFor: ["privacy-sensitive", "quick-checks"]
|
|
3096
|
+
},
|
|
3097
|
+
output: {
|
|
3098
|
+
format: "markdown",
|
|
3099
|
+
verbosity: "detailed",
|
|
3100
|
+
showConfidenceScores: true,
|
|
3101
|
+
includeAgentReasonings: false
|
|
3102
|
+
},
|
|
3103
|
+
compliance: {
|
|
3104
|
+
standards: ["GDPR", "HIPAA", "SOC2"],
|
|
3105
|
+
generateDocumentation: true,
|
|
3106
|
+
auditTrail: true
|
|
3107
|
+
}
|
|
3108
|
+
};
|
|
3109
|
+
|
|
3110
|
+
// src/config/loader.ts
|
|
3111
|
+
async function loadConfig() {
|
|
3112
|
+
try {
|
|
3113
|
+
const configPath = join5(process.cwd(), ".trie", "config.json");
|
|
3114
|
+
const configFile = await readFile5(configPath, "utf-8");
|
|
3115
|
+
const userConfig = JSON.parse(configFile);
|
|
3116
|
+
return mergeConfig(DEFAULT_CONFIG, userConfig);
|
|
3117
|
+
} catch (error) {
|
|
3118
|
+
console.error("No config found, using defaults");
|
|
3119
|
+
return DEFAULT_CONFIG;
|
|
3120
|
+
}
|
|
3121
|
+
}
|
|
3122
|
+
function mergeConfig(defaults, user) {
|
|
3123
|
+
if (typeof user !== "object" || user === null) {
|
|
3124
|
+
return defaults;
|
|
3125
|
+
}
|
|
3126
|
+
const result = { ...defaults };
|
|
3127
|
+
for (const [key, value] of Object.entries(user)) {
|
|
3128
|
+
if (typeof value === "object" && value !== null && !Array.isArray(value)) {
|
|
3129
|
+
result[key] = mergeConfig(defaults[key] || {}, value);
|
|
3130
|
+
} else {
|
|
3131
|
+
result[key] = value;
|
|
3132
|
+
}
|
|
3133
|
+
}
|
|
3134
|
+
return result;
|
|
3135
|
+
}
|
|
3136
|
+
|
|
3137
|
+
// src/index.ts
|
|
3138
|
+
var server = new Server(
|
|
3139
|
+
{
|
|
3140
|
+
name: "trie",
|
|
3141
|
+
version: "1.0.0",
|
|
3142
|
+
description: "Intelligent Agent Orchestration for AI Coding Tools"
|
|
3143
|
+
},
|
|
3144
|
+
{
|
|
3145
|
+
capabilities: {
|
|
3146
|
+
tools: {},
|
|
3147
|
+
resources: {}
|
|
3148
|
+
}
|
|
3149
|
+
}
|
|
3150
|
+
);
|
|
3151
|
+
var scanTool = new TrieScanTool();
|
|
3152
|
+
var fixTool = new TrieFixTool();
|
|
3153
|
+
var explainTool = new TrieExplainTool();
|
|
3154
|
+
var testTool = new TrieTestTool();
|
|
3155
|
+
var registerAgentTool = new TrieRegisterAgentTool();
|
|
3156
|
+
var watchTool = new TrieWatchTool();
|
|
3157
|
+
var agentTool = new TrieAgentTool();
|
|
3158
|
+
var createAgentTool = new TrieCreateAgentTool();
|
|
3159
|
+
var saveAgentTool = new TrieSaveAgentTool();
|
|
3160
|
+
var listAgentsTool = new TrieListAgentsTool();
|
|
3161
|
+
var tools = [
|
|
3162
|
+
{
|
|
3163
|
+
name: "scan",
|
|
3164
|
+
description: "Scan code with intelligent agent selection. Scans entire codebase if no files specified.",
|
|
3165
|
+
inputSchema: {
|
|
3166
|
+
type: "object",
|
|
3167
|
+
properties: {
|
|
3168
|
+
files: {
|
|
3169
|
+
type: "array",
|
|
3170
|
+
items: { type: "string" },
|
|
3171
|
+
description: "Files to scan (absolute paths). If empty, scans entire codebase."
|
|
3172
|
+
},
|
|
3173
|
+
directory: {
|
|
3174
|
+
type: "string",
|
|
3175
|
+
description: "Directory to scan (if files not specified). Defaults to current working directory."
|
|
3176
|
+
},
|
|
3177
|
+
context: {
|
|
3178
|
+
type: "object",
|
|
3179
|
+
properties: {
|
|
3180
|
+
changeType: {
|
|
3181
|
+
type: "string",
|
|
3182
|
+
enum: ["ui", "api", "database", "auth", "payment", "general"]
|
|
3183
|
+
},
|
|
3184
|
+
isNewFeature: { type: "boolean" },
|
|
3185
|
+
touchesUserData: { type: "boolean" }
|
|
3186
|
+
}
|
|
3187
|
+
},
|
|
3188
|
+
forceAgents: {
|
|
3189
|
+
type: "array",
|
|
3190
|
+
items: { type: "string" },
|
|
3191
|
+
description: "Manually specify agents to run (overrides triaging)"
|
|
3192
|
+
}
|
|
3193
|
+
}
|
|
3194
|
+
}
|
|
3195
|
+
},
|
|
3196
|
+
{
|
|
3197
|
+
name: "trie",
|
|
3198
|
+
description: "Quick menu of available Trie commands and aliases.",
|
|
3199
|
+
inputSchema: {
|
|
3200
|
+
type: "object",
|
|
3201
|
+
properties: {}
|
|
3202
|
+
}
|
|
3203
|
+
},
|
|
3204
|
+
// Alias so users can call `trie_scan` directly (no server prefix needed)
|
|
3205
|
+
{
|
|
3206
|
+
name: "trie_scan",
|
|
3207
|
+
description: "Alias for scan (same behavior as scan).",
|
|
3208
|
+
inputSchema: {
|
|
3209
|
+
type: "object",
|
|
3210
|
+
properties: {
|
|
3211
|
+
files: {
|
|
3212
|
+
type: "array",
|
|
3213
|
+
items: { type: "string" },
|
|
3214
|
+
description: "Files to scan (absolute paths). If empty, scans entire codebase."
|
|
3215
|
+
},
|
|
3216
|
+
directory: {
|
|
3217
|
+
type: "string",
|
|
3218
|
+
description: "Directory to scan (if files not specified). Defaults to current working directory."
|
|
3219
|
+
},
|
|
3220
|
+
context: {
|
|
3221
|
+
type: "object",
|
|
3222
|
+
properties: {
|
|
3223
|
+
changeType: {
|
|
3224
|
+
type: "string",
|
|
3225
|
+
enum: ["ui", "api", "database", "auth", "payment", "general"]
|
|
3226
|
+
},
|
|
3227
|
+
isNewFeature: { type: "boolean" },
|
|
3228
|
+
touchesUserData: { type: "boolean" }
|
|
3229
|
+
}
|
|
3230
|
+
},
|
|
3231
|
+
forceAgents: {
|
|
3232
|
+
type: "array",
|
|
3233
|
+
items: { type: "string" },
|
|
3234
|
+
description: "Manually specify agents to run (overrides triaging)"
|
|
3235
|
+
}
|
|
3236
|
+
}
|
|
3237
|
+
}
|
|
3238
|
+
},
|
|
3239
|
+
{
|
|
3240
|
+
name: "fix",
|
|
3241
|
+
description: "Apply high-confidence fixes to code",
|
|
3242
|
+
inputSchema: {
|
|
3243
|
+
type: "object",
|
|
3244
|
+
properties: {
|
|
3245
|
+
issueIds: {
|
|
3246
|
+
type: "array",
|
|
3247
|
+
items: { type: "string" },
|
|
3248
|
+
description: "Specific issues to fix (empty = all high-confidence)"
|
|
3249
|
+
},
|
|
3250
|
+
autoApprove: {
|
|
3251
|
+
type: "boolean",
|
|
3252
|
+
description: "Skip human review for high-confidence fixes"
|
|
3253
|
+
}
|
|
3254
|
+
}
|
|
3255
|
+
}
|
|
3256
|
+
},
|
|
3257
|
+
{
|
|
3258
|
+
name: "explain",
|
|
3259
|
+
description: "Explain code, issues, or changes in plain language",
|
|
3260
|
+
inputSchema: {
|
|
3261
|
+
type: "object",
|
|
3262
|
+
properties: {
|
|
3263
|
+
type: {
|
|
3264
|
+
type: "string",
|
|
3265
|
+
enum: ["code", "issue", "change", "risk"]
|
|
3266
|
+
},
|
|
3267
|
+
target: {
|
|
3268
|
+
type: "string",
|
|
3269
|
+
description: "What to explain (file path, issue ID, etc.)"
|
|
3270
|
+
}
|
|
3271
|
+
},
|
|
3272
|
+
required: ["type", "target"]
|
|
3273
|
+
}
|
|
3274
|
+
},
|
|
3275
|
+
{
|
|
3276
|
+
name: "test",
|
|
3277
|
+
description: "Generate tests or check test coverage",
|
|
3278
|
+
inputSchema: {
|
|
3279
|
+
type: "object",
|
|
3280
|
+
properties: {
|
|
3281
|
+
action: {
|
|
3282
|
+
type: "string",
|
|
3283
|
+
enum: ["generate", "coverage", "run"]
|
|
3284
|
+
},
|
|
3285
|
+
files: {
|
|
3286
|
+
type: "array",
|
|
3287
|
+
items: { type: "string" }
|
|
3288
|
+
}
|
|
3289
|
+
},
|
|
3290
|
+
required: ["action", "files"]
|
|
3291
|
+
}
|
|
3292
|
+
},
|
|
3293
|
+
{
|
|
3294
|
+
name: "register_agent",
|
|
3295
|
+
description: "Register a custom agent for your project",
|
|
3296
|
+
inputSchema: {
|
|
3297
|
+
type: "object",
|
|
3298
|
+
properties: {
|
|
3299
|
+
name: { type: "string" },
|
|
3300
|
+
path: {
|
|
3301
|
+
type: "string",
|
|
3302
|
+
description: "Path to agent implementation"
|
|
3303
|
+
},
|
|
3304
|
+
activationRules: {
|
|
3305
|
+
type: "object",
|
|
3306
|
+
description: "When to activate this agent"
|
|
3307
|
+
}
|
|
3308
|
+
},
|
|
3309
|
+
required: ["name", "path"]
|
|
3310
|
+
}
|
|
3311
|
+
},
|
|
3312
|
+
{
|
|
3313
|
+
name: "watch",
|
|
3314
|
+
description: "Autonomous mode: watch for file changes and scan/fix automatically. Use yolo:true for auto-fix!",
|
|
3315
|
+
inputSchema: {
|
|
3316
|
+
type: "object",
|
|
3317
|
+
properties: {
|
|
3318
|
+
action: {
|
|
3319
|
+
type: "string",
|
|
3320
|
+
enum: ["start", "stop", "status", "issues", "yolo"],
|
|
3321
|
+
description: "start = begin watching, stop = end, status = check, issues = list, yolo = toggle auto-fix"
|
|
3322
|
+
},
|
|
3323
|
+
directory: {
|
|
3324
|
+
type: "string",
|
|
3325
|
+
description: "Directory to watch (defaults to current working directory)"
|
|
3326
|
+
},
|
|
3327
|
+
yolo: {
|
|
3328
|
+
type: "boolean",
|
|
3329
|
+
description: "\u{1F525} YOLO MODE: Auto-fix issues as they occur! (default: false)"
|
|
3330
|
+
},
|
|
3331
|
+
debounceMs: {
|
|
3332
|
+
type: "number",
|
|
3333
|
+
description: "Milliseconds to wait after file change before scanning (default: 1000)"
|
|
3334
|
+
}
|
|
3335
|
+
},
|
|
3336
|
+
required: ["action"]
|
|
3337
|
+
}
|
|
3338
|
+
},
|
|
3339
|
+
// Individual agent commands
|
|
3340
|
+
{
|
|
3341
|
+
name: "security",
|
|
3342
|
+
description: "Run security agent: detect vulnerabilities, injection risks, auth issues, hardcoded secrets",
|
|
3343
|
+
inputSchema: {
|
|
3344
|
+
type: "object",
|
|
3345
|
+
properties: {
|
|
3346
|
+
files: { type: "array", items: { type: "string" }, description: "Files to scan (or scans codebase)" },
|
|
3347
|
+
directory: { type: "string", description: "Directory to scan" },
|
|
3348
|
+
output: {
|
|
3349
|
+
type: "string",
|
|
3350
|
+
enum: ["summary", "full"],
|
|
3351
|
+
description: "summary = concise (default), full = includes AI prompt/code (large output)"
|
|
3352
|
+
}
|
|
3353
|
+
}
|
|
3354
|
+
}
|
|
3355
|
+
},
|
|
3356
|
+
{
|
|
3357
|
+
name: "privacy",
|
|
3358
|
+
description: "Run privacy agent: PII handling, GDPR/HIPAA compliance, data encryption",
|
|
3359
|
+
inputSchema: {
|
|
3360
|
+
type: "object",
|
|
3361
|
+
properties: {
|
|
3362
|
+
files: { type: "array", items: { type: "string" }, description: "Files to scan" },
|
|
3363
|
+
directory: { type: "string", description: "Directory to scan" },
|
|
3364
|
+
output: {
|
|
3365
|
+
type: "string",
|
|
3366
|
+
enum: ["summary", "full"],
|
|
3367
|
+
description: "summary = concise (default), full = includes AI prompt/code (large output)"
|
|
3368
|
+
}
|
|
3369
|
+
}
|
|
3370
|
+
}
|
|
3371
|
+
},
|
|
3372
|
+
{
|
|
3373
|
+
name: "legal",
|
|
3374
|
+
description: "Run legal agent: GDPR, CCPA, consent patterns, data retention compliance",
|
|
3375
|
+
inputSchema: {
|
|
3376
|
+
type: "object",
|
|
3377
|
+
properties: {
|
|
3378
|
+
files: { type: "array", items: { type: "string" }, description: "Files to scan" },
|
|
3379
|
+
directory: { type: "string", description: "Directory to scan" },
|
|
3380
|
+
output: {
|
|
3381
|
+
type: "string",
|
|
3382
|
+
enum: ["summary", "full"],
|
|
3383
|
+
description: "summary = concise (default), full = includes AI prompt/code (large output)"
|
|
3384
|
+
}
|
|
3385
|
+
}
|
|
3386
|
+
}
|
|
3387
|
+
},
|
|
3388
|
+
{
|
|
3389
|
+
name: "accessibility",
|
|
3390
|
+
description: "Run accessibility agent: WCAG 2.1 compliance, keyboard nav, screen readers, color contrast",
|
|
3391
|
+
inputSchema: {
|
|
3392
|
+
type: "object",
|
|
3393
|
+
properties: {
|
|
3394
|
+
files: { type: "array", items: { type: "string" }, description: "Files to scan" },
|
|
3395
|
+
directory: { type: "string", description: "Directory to scan" },
|
|
3396
|
+
output: {
|
|
3397
|
+
type: "string",
|
|
3398
|
+
enum: ["summary", "full"],
|
|
3399
|
+
description: "summary = concise (default), full = includes AI prompt/code (large output)"
|
|
3400
|
+
}
|
|
3401
|
+
}
|
|
3402
|
+
}
|
|
3403
|
+
},
|
|
3404
|
+
{
|
|
3405
|
+
name: "architecture",
|
|
3406
|
+
description: "Run architecture agent: code organization, SOLID principles, N+1 queries, scalability",
|
|
3407
|
+
inputSchema: {
|
|
3408
|
+
type: "object",
|
|
3409
|
+
properties: {
|
|
3410
|
+
files: { type: "array", items: { type: "string" }, description: "Files to scan" },
|
|
3411
|
+
directory: { type: "string", description: "Directory to scan" },
|
|
3412
|
+
output: {
|
|
3413
|
+
type: "string",
|
|
3414
|
+
enum: ["summary", "full"],
|
|
3415
|
+
description: "summary = concise (default), full = includes AI prompt/code (large output)"
|
|
3416
|
+
}
|
|
3417
|
+
}
|
|
3418
|
+
}
|
|
3419
|
+
},
|
|
3420
|
+
{
|
|
3421
|
+
name: "bugs",
|
|
3422
|
+
description: "Run bug-finding agent: null safety, edge cases, common bugs, async issues",
|
|
3423
|
+
inputSchema: {
|
|
3424
|
+
type: "object",
|
|
3425
|
+
properties: {
|
|
3426
|
+
files: { type: "array", items: { type: "string" }, description: "Files to scan" },
|
|
3427
|
+
directory: { type: "string", description: "Directory to scan" },
|
|
3428
|
+
output: {
|
|
3429
|
+
type: "string",
|
|
3430
|
+
enum: ["summary", "full"],
|
|
3431
|
+
description: "summary = concise (default), full = includes AI prompt/code (large output)"
|
|
3432
|
+
}
|
|
3433
|
+
}
|
|
3434
|
+
}
|
|
3435
|
+
},
|
|
3436
|
+
{
|
|
3437
|
+
name: "ux",
|
|
3438
|
+
description: "Run UX testing agent: simulate happy path, security tester, confused user, impatient user",
|
|
3439
|
+
inputSchema: {
|
|
3440
|
+
type: "object",
|
|
3441
|
+
properties: {
|
|
3442
|
+
files: { type: "array", items: { type: "string" }, description: "Files to scan" },
|
|
3443
|
+
directory: { type: "string", description: "Directory to scan" },
|
|
3444
|
+
output: {
|
|
3445
|
+
type: "string",
|
|
3446
|
+
enum: ["summary", "full"],
|
|
3447
|
+
description: "summary = concise (default), full = includes AI prompt/code (large output)"
|
|
3448
|
+
}
|
|
3449
|
+
}
|
|
3450
|
+
}
|
|
3451
|
+
},
|
|
3452
|
+
{
|
|
3453
|
+
name: "types",
|
|
3454
|
+
description: "Run type safety agent: type errors, missing annotations, null checks",
|
|
3455
|
+
inputSchema: {
|
|
3456
|
+
type: "object",
|
|
3457
|
+
properties: {
|
|
3458
|
+
files: { type: "array", items: { type: "string" }, description: "Files to scan" },
|
|
3459
|
+
directory: { type: "string", description: "Directory to scan" },
|
|
3460
|
+
output: {
|
|
3461
|
+
type: "string",
|
|
3462
|
+
enum: ["summary", "full"],
|
|
3463
|
+
description: "summary = concise (default), full = includes AI prompt/code (large output)"
|
|
3464
|
+
}
|
|
3465
|
+
}
|
|
3466
|
+
}
|
|
3467
|
+
},
|
|
3468
|
+
{
|
|
3469
|
+
name: "devops",
|
|
3470
|
+
description: "Run devops agent: config issues, logging, environment variables, deployment patterns",
|
|
3471
|
+
inputSchema: {
|
|
3472
|
+
type: "object",
|
|
3473
|
+
properties: {
|
|
3474
|
+
files: { type: "array", items: { type: "string" }, description: "Files to scan" },
|
|
3475
|
+
directory: { type: "string", description: "Directory to scan" },
|
|
3476
|
+
output: {
|
|
3477
|
+
type: "string",
|
|
3478
|
+
enum: ["summary", "full"],
|
|
3479
|
+
description: "summary = concise (default), full = includes AI prompt/code (large output)"
|
|
3480
|
+
}
|
|
3481
|
+
}
|
|
3482
|
+
}
|
|
3483
|
+
},
|
|
3484
|
+
{
|
|
3485
|
+
name: "clean",
|
|
3486
|
+
description: "\u{1F9F9} Clean up AI-generated code: find common mistakes, bad patterns, and quick fixes",
|
|
3487
|
+
inputSchema: {
|
|
3488
|
+
type: "object",
|
|
3489
|
+
properties: {
|
|
3490
|
+
files: { type: "array", items: { type: "string" }, description: "Files to scan" },
|
|
3491
|
+
directory: { type: "string", description: "Directory to scan" },
|
|
3492
|
+
output: {
|
|
3493
|
+
type: "string",
|
|
3494
|
+
enum: ["summary", "full"],
|
|
3495
|
+
description: "summary = concise (default), full = includes AI prompt/code (large output)"
|
|
3496
|
+
}
|
|
3497
|
+
}
|
|
3498
|
+
}
|
|
3499
|
+
},
|
|
3500
|
+
// Custom agent creation tools (two-step process - no API key needed!)
|
|
3501
|
+
{
|
|
3502
|
+
name: "create_agent",
|
|
3503
|
+
description: "\u{1F4DA} Step 1: Parse a document to create a custom agent. Supports file path OR drag-and-drop content. Returns extraction prompt for Claude to process.",
|
|
3504
|
+
inputSchema: {
|
|
3505
|
+
type: "object",
|
|
3506
|
+
properties: {
|
|
3507
|
+
filePath: {
|
|
3508
|
+
type: "string",
|
|
3509
|
+
description: "Path to the document file (PDF, TXT, MD, or RTF)"
|
|
3510
|
+
},
|
|
3511
|
+
documentContent: {
|
|
3512
|
+
type: "string",
|
|
3513
|
+
description: "Raw document text (for drag-and-drop or pasted content). Use this OR filePath."
|
|
3514
|
+
},
|
|
3515
|
+
agentName: {
|
|
3516
|
+
type: "string",
|
|
3517
|
+
description: 'Name for the new agent (e.g., "react-fundamentals", "gdpr-compliance")'
|
|
3518
|
+
},
|
|
3519
|
+
displayName: {
|
|
3520
|
+
type: "string",
|
|
3521
|
+
description: 'Optional display name (e.g., "React Fundamentals Expert")'
|
|
3522
|
+
},
|
|
3523
|
+
description: {
|
|
3524
|
+
type: "string",
|
|
3525
|
+
description: "Optional description of what the agent does"
|
|
3526
|
+
},
|
|
3527
|
+
category: {
|
|
3528
|
+
type: "string",
|
|
3529
|
+
description: 'Optional category (e.g., "react", "security", "compliance", "architecture")'
|
|
3530
|
+
}
|
|
3531
|
+
},
|
|
3532
|
+
required: ["agentName"]
|
|
3533
|
+
}
|
|
3534
|
+
},
|
|
3535
|
+
{
|
|
3536
|
+
name: "save_agent",
|
|
3537
|
+
description: "\u{1F4E6} Step 2: Save the agent config after Claude extracts knowledge from the document. Pass the JSON output from trie_create_agent.",
|
|
3538
|
+
inputSchema: {
|
|
3539
|
+
type: "object",
|
|
3540
|
+
properties: {
|
|
3541
|
+
agentConfig: {
|
|
3542
|
+
type: "object",
|
|
3543
|
+
description: "Agent configuration (name, displayName, description, category)",
|
|
3544
|
+
properties: {
|
|
3545
|
+
name: { type: "string" },
|
|
3546
|
+
displayName: { type: "string" },
|
|
3547
|
+
description: { type: "string" },
|
|
3548
|
+
version: { type: "string" },
|
|
3549
|
+
category: { type: "string" }
|
|
3550
|
+
},
|
|
3551
|
+
required: ["name", "category"]
|
|
3552
|
+
},
|
|
3553
|
+
knowledge: {
|
|
3554
|
+
type: "object",
|
|
3555
|
+
description: "Extracted knowledge (domain, summary, coreConcepts, bestPractices, antiPatterns, glossary)"
|
|
3556
|
+
},
|
|
3557
|
+
detectionRules: {
|
|
3558
|
+
type: "array",
|
|
3559
|
+
description: "Array of detection rules with patterns and fixes"
|
|
3560
|
+
},
|
|
3561
|
+
prompts: {
|
|
3562
|
+
type: "object",
|
|
3563
|
+
description: "Agent prompts (systemPrompt, analysisPrompt, fixPrompt)",
|
|
3564
|
+
properties: {
|
|
3565
|
+
systemPrompt: { type: "string" },
|
|
3566
|
+
analysisPrompt: { type: "string" },
|
|
3567
|
+
fixPrompt: { type: "string" }
|
|
3568
|
+
},
|
|
3569
|
+
required: ["systemPrompt", "analysisPrompt", "fixPrompt"]
|
|
3570
|
+
},
|
|
3571
|
+
sourceFile: {
|
|
3572
|
+
type: "string",
|
|
3573
|
+
description: "Original source file path (optional)"
|
|
3574
|
+
}
|
|
3575
|
+
},
|
|
3576
|
+
required: ["agentConfig", "knowledge", "detectionRules", "prompts"]
|
|
3577
|
+
}
|
|
3578
|
+
},
|
|
3579
|
+
{
|
|
3580
|
+
name: "list_agents",
|
|
3581
|
+
description: "\u{1F4CB} List all registered agents including custom agents created from documents",
|
|
3582
|
+
inputSchema: {
|
|
3583
|
+
type: "object",
|
|
3584
|
+
properties: {
|
|
3585
|
+
includeBuiltin: {
|
|
3586
|
+
type: "boolean",
|
|
3587
|
+
description: "Include built-in agents in the list (default: true)"
|
|
3588
|
+
}
|
|
3589
|
+
}
|
|
3590
|
+
}
|
|
3591
|
+
}
|
|
3592
|
+
];
|
|
3593
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
3594
|
+
return {
|
|
3595
|
+
tools
|
|
3596
|
+
};
|
|
3597
|
+
});
|
|
3598
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
3599
|
+
const { name, arguments: args } = request.params;
|
|
3600
|
+
const stripNamespace = (n) => {
|
|
3601
|
+
const parts = n.split(":");
|
|
3602
|
+
const base = parts[0] || n;
|
|
3603
|
+
const slashParts = base.split("/");
|
|
3604
|
+
return slashParts[slashParts.length - 1] || base;
|
|
3605
|
+
};
|
|
3606
|
+
const rawName = stripNamespace(name);
|
|
3607
|
+
const normalizedName = rawName.startsWith("trie_") ? rawName.slice("trie_".length) : rawName;
|
|
3608
|
+
try {
|
|
3609
|
+
switch (normalizedName) {
|
|
3610
|
+
case "trie":
|
|
3611
|
+
return {
|
|
3612
|
+
content: [
|
|
3613
|
+
{
|
|
3614
|
+
type: "text",
|
|
3615
|
+
text: [
|
|
3616
|
+
"## Trie Menu",
|
|
3617
|
+
"",
|
|
3618
|
+
"- `trie_scan` or `scan`: Full intelligent scan (auto triage agents)",
|
|
3619
|
+
"- `fix`: Apply high-confidence fixes",
|
|
3620
|
+
"- `explain`: Explain code/issues/risks",
|
|
3621
|
+
"- `watch`: Watch mode (scan on change)",
|
|
3622
|
+
"- `security`, `privacy`, `legal`, `bugs`, `types`, `devops`, `architecture`, `ux`, `clean`: Run a specific agent",
|
|
3623
|
+
"- `test`: Generate/check tests",
|
|
3624
|
+
"- `register_agent`, `create_agent`, `save_agent`, `list_agents`: Custom agent lifecycle",
|
|
3625
|
+
"",
|
|
3626
|
+
"You can call these without any namespace; `trie_` aliases also work."
|
|
3627
|
+
].join("\n")
|
|
3628
|
+
}
|
|
3629
|
+
]
|
|
3630
|
+
};
|
|
3631
|
+
case "scan":
|
|
3632
|
+
return await scanTool.execute(args);
|
|
3633
|
+
case "fix":
|
|
3634
|
+
return await fixTool.execute(args);
|
|
3635
|
+
case "explain":
|
|
3636
|
+
return await explainTool.execute(args);
|
|
3637
|
+
case "test":
|
|
3638
|
+
return await testTool.execute(args);
|
|
3639
|
+
case "register_agent":
|
|
3640
|
+
return await registerAgentTool.execute(args);
|
|
3641
|
+
case "watch":
|
|
3642
|
+
return await watchTool.execute(args);
|
|
3643
|
+
// Individual agent commands
|
|
3644
|
+
case "security":
|
|
3645
|
+
return await agentTool.execute({ ...args, agent: "security" });
|
|
3646
|
+
case "privacy":
|
|
3647
|
+
return await agentTool.execute({ ...args, agent: "privacy" });
|
|
3648
|
+
case "legal":
|
|
3649
|
+
return await agentTool.execute({ ...args, agent: "legal" });
|
|
3650
|
+
case "accessibility":
|
|
3651
|
+
return await agentTool.execute({ ...args, agent: "design-engineer" });
|
|
3652
|
+
case "architecture":
|
|
3653
|
+
return await agentTool.execute({ ...args, agent: "software-architect" });
|
|
3654
|
+
case "bugs":
|
|
3655
|
+
return await agentTool.execute({ ...args, agent: "bug-finding" });
|
|
3656
|
+
case "ux":
|
|
3657
|
+
return await agentTool.execute({ ...args, agent: "user-testing" });
|
|
3658
|
+
case "types":
|
|
3659
|
+
return await agentTool.execute({ ...args, agent: "typecheck" });
|
|
3660
|
+
case "devops":
|
|
3661
|
+
return await agentTool.execute({ ...args, agent: "devops" });
|
|
3662
|
+
case "clean":
|
|
3663
|
+
return await agentTool.execute({ ...args, agent: "trie_clean" });
|
|
3664
|
+
// Custom agent creation tools (two-step process)
|
|
3665
|
+
case "create_agent":
|
|
3666
|
+
return await createAgentTool.execute(args);
|
|
3667
|
+
case "save_agent":
|
|
3668
|
+
return await saveAgentTool.execute(args);
|
|
3669
|
+
case "list_agents":
|
|
3670
|
+
return await listAgentsTool.execute(args);
|
|
3671
|
+
default:
|
|
3672
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
3673
|
+
}
|
|
3674
|
+
} catch (error) {
|
|
3675
|
+
return {
|
|
3676
|
+
content: [
|
|
3677
|
+
{
|
|
3678
|
+
type: "text",
|
|
3679
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`
|
|
3680
|
+
}
|
|
3681
|
+
]
|
|
3682
|
+
};
|
|
3683
|
+
}
|
|
3684
|
+
});
|
|
3685
|
+
var agentRegistry = new AgentRegistry();
|
|
3686
|
+
async function getAvailableResources() {
|
|
3687
|
+
const resources = [];
|
|
3688
|
+
resources.push({
|
|
3689
|
+
uri: "trie://agents",
|
|
3690
|
+
name: "Available Agents",
|
|
3691
|
+
description: "List of all available Trie agents (built-in and custom)",
|
|
3692
|
+
mimeType: "application/json"
|
|
3693
|
+
});
|
|
3694
|
+
resources.push({
|
|
3695
|
+
uri: "trie://config",
|
|
3696
|
+
name: "Trie Configuration",
|
|
3697
|
+
description: "Current Trie configuration settings",
|
|
3698
|
+
mimeType: "application/json"
|
|
3699
|
+
});
|
|
3700
|
+
resources.push({
|
|
3701
|
+
uri: "trie://cache/stats",
|
|
3702
|
+
name: "Cache Statistics",
|
|
3703
|
+
description: "Trie scan cache statistics and performance metrics",
|
|
3704
|
+
mimeType: "application/json"
|
|
3705
|
+
});
|
|
3706
|
+
resources.push({
|
|
3707
|
+
uri: "trie://signatures",
|
|
3708
|
+
name: "Vulnerability Signatures",
|
|
3709
|
+
description: "Summary of loaded vulnerability detection signatures",
|
|
3710
|
+
mimeType: "application/json"
|
|
3711
|
+
});
|
|
3712
|
+
try {
|
|
3713
|
+
const reportsDir = join6(process.cwd(), "trie-reports");
|
|
3714
|
+
const files = await readdir2(reportsDir);
|
|
3715
|
+
const reportFiles = files.filter((f) => f.endsWith(".txt") || f.endsWith(".json"));
|
|
3716
|
+
for (const file of reportFiles.slice(0, 10)) {
|
|
3717
|
+
resources.push({
|
|
3718
|
+
uri: `trie://reports/${file}`,
|
|
3719
|
+
name: `Scan Report: ${file}`,
|
|
3720
|
+
description: `Trie scan report from ${file}`,
|
|
3721
|
+
mimeType: file.endsWith(".json") ? "application/json" : "text/plain"
|
|
3722
|
+
});
|
|
3723
|
+
}
|
|
3724
|
+
} catch {
|
|
3725
|
+
}
|
|
3726
|
+
try {
|
|
3727
|
+
await agentRegistry.loadCustomAgents();
|
|
3728
|
+
const customAgents = agentRegistry.getCustomAgents();
|
|
3729
|
+
for (const agent of customAgents) {
|
|
3730
|
+
resources.push({
|
|
3731
|
+
uri: `trie://agents/custom/${agent.name}`,
|
|
3732
|
+
name: `Custom Agent: ${agent.name}`,
|
|
3733
|
+
description: agent.description,
|
|
3734
|
+
mimeType: "application/json"
|
|
3735
|
+
});
|
|
3736
|
+
}
|
|
3737
|
+
} catch {
|
|
3738
|
+
}
|
|
3739
|
+
return resources;
|
|
3740
|
+
}
|
|
3741
|
+
async function readResourceContent(uri) {
|
|
3742
|
+
const parsedUri = uri.replace("trie://", "");
|
|
3743
|
+
if (parsedUri === "agents") {
|
|
3744
|
+
await agentRegistry.loadCustomAgents();
|
|
3745
|
+
const agents = agentRegistry.getAgentDescriptions();
|
|
3746
|
+
return {
|
|
3747
|
+
contents: [{
|
|
3748
|
+
uri,
|
|
3749
|
+
mimeType: "application/json",
|
|
3750
|
+
text: JSON.stringify({
|
|
3751
|
+
totalAgents: agents.length,
|
|
3752
|
+
builtinCount: agents.filter((a) => !a.isCustom).length,
|
|
3753
|
+
customCount: agents.filter((a) => a.isCustom).length,
|
|
3754
|
+
agents: agents.map((a) => ({
|
|
3755
|
+
name: a.name,
|
|
3756
|
+
description: a.description,
|
|
3757
|
+
type: a.isCustom ? "custom" : "builtin"
|
|
3758
|
+
}))
|
|
3759
|
+
}, null, 2)
|
|
3760
|
+
}]
|
|
3761
|
+
};
|
|
3762
|
+
}
|
|
3763
|
+
if (parsedUri.startsWith("agents/custom/")) {
|
|
3764
|
+
const agentName = parsedUri.replace("agents/custom/", "");
|
|
3765
|
+
await agentRegistry.loadCustomAgents();
|
|
3766
|
+
const metadata = agentRegistry.getCustomAgentMetadata(agentName);
|
|
3767
|
+
if (!metadata) {
|
|
3768
|
+
throw new Error(`Custom agent not found: ${agentName}`);
|
|
3769
|
+
}
|
|
3770
|
+
return {
|
|
3771
|
+
contents: [{
|
|
3772
|
+
uri,
|
|
3773
|
+
mimeType: "application/json",
|
|
3774
|
+
text: JSON.stringify(metadata, null, 2)
|
|
3775
|
+
}]
|
|
3776
|
+
};
|
|
3777
|
+
}
|
|
3778
|
+
if (parsedUri === "config") {
|
|
3779
|
+
const config = await loadConfig();
|
|
3780
|
+
return {
|
|
3781
|
+
contents: [{
|
|
3782
|
+
uri,
|
|
3783
|
+
mimeType: "application/json",
|
|
3784
|
+
text: JSON.stringify(config, null, 2)
|
|
3785
|
+
}]
|
|
3786
|
+
};
|
|
3787
|
+
}
|
|
3788
|
+
if (parsedUri === "cache/stats") {
|
|
3789
|
+
try {
|
|
3790
|
+
const cachePath = join6(process.cwd(), ".trie", ".trie-cache.json");
|
|
3791
|
+
const cacheContent = await readFile6(cachePath, "utf-8");
|
|
3792
|
+
const cache = JSON.parse(cacheContent);
|
|
3793
|
+
const fileCount = Object.keys(cache.files || {}).length;
|
|
3794
|
+
const totalVulns = Object.values(cache.files || {}).reduce((acc, file) => {
|
|
3795
|
+
return acc + (file.vulnerabilities?.length || 0);
|
|
3796
|
+
}, 0);
|
|
3797
|
+
return {
|
|
3798
|
+
contents: [{
|
|
3799
|
+
uri,
|
|
3800
|
+
mimeType: "application/json",
|
|
3801
|
+
text: JSON.stringify({
|
|
3802
|
+
version: cache.version,
|
|
3803
|
+
created: new Date(cache.created).toISOString(),
|
|
3804
|
+
lastUpdated: new Date(cache.lastUpdated).toISOString(),
|
|
3805
|
+
cachedFiles: fileCount,
|
|
3806
|
+
totalVulnerabilitiesFound: totalVulns,
|
|
3807
|
+
cacheAgeMinutes: Math.round((Date.now() - cache.lastUpdated) / 6e4)
|
|
3808
|
+
}, null, 2)
|
|
3809
|
+
}]
|
|
3810
|
+
};
|
|
3811
|
+
} catch {
|
|
3812
|
+
return {
|
|
3813
|
+
contents: [{
|
|
3814
|
+
uri,
|
|
3815
|
+
mimeType: "application/json",
|
|
3816
|
+
text: JSON.stringify({
|
|
3817
|
+
error: "No cache found. Run a scan first to build the cache.",
|
|
3818
|
+
hint: "Use trie_scan to scan your codebase"
|
|
3819
|
+
}, null, 2)
|
|
3820
|
+
}]
|
|
3821
|
+
};
|
|
3822
|
+
}
|
|
3823
|
+
}
|
|
3824
|
+
if (parsedUri === "signatures") {
|
|
3825
|
+
const { getVulnerabilityStats } = await import("./vulnerability-signatures-J3CUQ7VR.js");
|
|
3826
|
+
const { getVibeCodeStats } = await import("./vibe-code-signatures-4CBHUSI7.js");
|
|
3827
|
+
const vulnStats = getVulnerabilityStats();
|
|
3828
|
+
const vibeStats = getVibeCodeStats();
|
|
3829
|
+
return {
|
|
3830
|
+
contents: [{
|
|
3831
|
+
uri,
|
|
3832
|
+
mimeType: "application/json",
|
|
3833
|
+
text: JSON.stringify({
|
|
3834
|
+
vulnerabilitySignatures: vulnStats,
|
|
3835
|
+
vibeCodeSignatures: vibeStats,
|
|
3836
|
+
totalSignatures: vulnStats.total + vibeStats.total
|
|
3837
|
+
}, null, 2)
|
|
3838
|
+
}]
|
|
3839
|
+
};
|
|
3840
|
+
}
|
|
3841
|
+
if (parsedUri.startsWith("reports/")) {
|
|
3842
|
+
const fileName = parsedUri.replace("reports/", "");
|
|
3843
|
+
const reportPath = join6(process.cwd(), "trie-reports", fileName);
|
|
3844
|
+
try {
|
|
3845
|
+
const content = await readFile6(reportPath, "utf-8");
|
|
3846
|
+
return {
|
|
3847
|
+
contents: [{
|
|
3848
|
+
uri,
|
|
3849
|
+
mimeType: fileName.endsWith(".json") ? "application/json" : "text/plain",
|
|
3850
|
+
text: content
|
|
3851
|
+
}]
|
|
3852
|
+
};
|
|
3853
|
+
} catch {
|
|
3854
|
+
throw new Error(`Report not found: ${fileName}`);
|
|
3855
|
+
}
|
|
3856
|
+
}
|
|
3857
|
+
throw new Error(`Unknown resource: ${uri}`);
|
|
3858
|
+
}
|
|
3859
|
+
server.setRequestHandler(ListResourcesRequestSchema, async () => {
|
|
3860
|
+
const resources = await getAvailableResources();
|
|
3861
|
+
return { resources };
|
|
3862
|
+
});
|
|
3863
|
+
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
3864
|
+
const { uri } = request.params;
|
|
3865
|
+
return await readResourceContent(uri);
|
|
3866
|
+
});
|
|
3867
|
+
async function main() {
|
|
3868
|
+
const aiTool = detectAITool();
|
|
3869
|
+
console.error(`Detected AI Tool: ${aiTool.name}`);
|
|
3870
|
+
const config = await loadConfig();
|
|
3871
|
+
console.error(`Loaded config for ${config.agents.builtin ? Object.keys(config.agents.builtin).length : 0} built-in agents`);
|
|
3872
|
+
const transport = new StdioServerTransport();
|
|
3873
|
+
await server.connect(transport);
|
|
3874
|
+
console.error("Trie Agent MCP Server running...");
|
|
3875
|
+
}
|
|
3876
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
3877
|
+
main().catch((error) => {
|
|
3878
|
+
console.error("Fatal error:", error);
|
|
3879
|
+
process.exit(1);
|
|
3880
|
+
});
|
|
3881
|
+
}
|
|
3882
|
+
//# sourceMappingURL=index.js.map
|